marcel@duteca.tudelft.nl (Marcel Mol) (02/27/90)
Posting-number: Volume 10, Issue 84 Submitted-by: marcel@duteca.tudelft.nl (Marcel Mol) Archive-name: weekday [Shar, please!!! ++bsa] This version of weekday repairs some bugs and adds the ISO date standard as date input format. (The diffs were almost as large as the source so I send out the soure). -Marcel ------------------------- Cut here --------------------------------------- #! /bin/sh # This file was wrapped with "dummyshar". "sh" this file to extract. # Contents: weekday.c echo extracting 'weekday.c' if test -f 'weekday.c' -a -z "$1"; then echo Not overwriting 'weekday.c'; else sed 's/^X//' << \EOF > 'weekday.c' X/* @(#) weekday.c 3.40 02/06/90 */ X/************************************************************************* X ** ** X ** Name : weekday ** X ** Author : Marcel J.E. Mol ** X ** Date : 12/28/89 (first release) ** X ** Version : 3.40 ** X ** Files : weekday.c Main source file ** X ** ** X ** ------------------------- Revision List ------------------------- ** X ** Ver Date Name Remarks ** X ** 3.40 02/06/90 Marcel Mol Added ISO date standard YYYY-MM-DD ** X ** Suggested by mathew smm12@cl.cam.ac.uk ** X ** 3.33 02/04/90 Marcel Mol Fixed bug when entering years in ** X ** 0 <= year <= 99 with leading '0' ** X ** 3.32 02/01/90 Marcel Mol Fixed typos in January February ** X ** October ** X ** 3.31 01/09/90 Marcel Mol Removed unnecessary code. ** X ** Added comments. ** X ** 3.30 01/08/90 Marcel Mol Improved option handling. ** X ** verbose works on more options. ** X ** use printf field length option to ** X ** get short day and month names. ** X ** 3.26 01/07/90 Marcel Mol Filled in usage function. ** X ** 3.25 01/04/90 Marcel Mol Use 'today' when no date is given. ** X ** 3.20 01/03/90 Marcel Mol Allow bsd `date` as input. ** X ** 3.11 01/03/90 Marcel Mol Documentation, typos, checkdate(). ** X ** Removed redundant checks. ** X ** 3.10 01/02/90 Marcel Mol Added -s and -v options. ** X ** allow yy.ddd input and implement ** X ** julian date conversion. ** X ** 3.00 12/30/89 Marcel Mol Set up user interface. ** X ** Allow mm/dd/yy and dd-mm-yy input. ** X ** Options to print different parts. ** X ** 2.10 12/28/89 Marcel Mol Added Julianday. ** X ** 2.00 12/27/89 Marcel Mol Made weekday and weeknr everlasting.** X ** 1.00 12/26/89 Marcel Mol First release. ** X ** Set up algorithms weekday weeknr. ** X ** ================================================================= ** X ** ** X ** Compile as follows: cc weekday.c -Oso weekday ** X ** ** X ** Usage: weekday [-svdDmMywjnah] [<date>] ** X ** ** X ** Weekday outputs line on which it informs you about several ** X ** thing of the given <date>. ** X ** The <date> input may be in several formats: ** X ** ** X ** mm/dd/yy e.g. 10/30/89 or 1/2/510 ** X ** dd-mm-yy e.g. 30-10-89 or 2-1-510 ** X ** yyyy-mm-dd e.g. 1989-10-30 or 0510-1-2 ** X ** yy.ddd e.g. 89.303 or 510.2 ** X ** `date` output e.g. Mon Oct 30 99:99:99 1989 ** X ** ** X ** If no date argument is given, weekday uses the system date. ** X ** The yyyy-mm-dd date format must have 4 digits for the yyyy ** X ** part, 2 for the mm part and 2 for the dd part; it's just ** X ** the ISO date standard. (undocumented feature: it also works ** X ** if the mm and/or dd part is only one digit long.) ** X ** So the following trick does not apply to this date format: ** X ** When the year number is less than 100 and the string ** X ** forming the year does not start with a 0, the year is ** X ** converted to be in the current century. So 10/30/89 is ** X ** actually 10/30/1989. If the real year 89 is wanted specify ** X ** it as 10/30/089. (the current century is a constant in the ** X ** source code. If this programs survives 1999 this constant ** X ** must be changed, and the program recompiled.) ** X ** Note: make sure that `date` output is passed as one ** X ** argument, so enclose it with "s. ** X ** ** X ** The output, when no options are given, is something like ** X ** this: ** X ** ** X ** 30 October 1989 is on a Monday on julian day 303 in week 44 ** X ** ** X ** or: ** X ** ** X ** 2 January 510 is on a Friday on julian day 2 in week 1 ** X ** ** X ** Depending on the options the output changes: ** X ** ** X ** -s toggle short form output: Monday -> Mon, June -> Jun ** X ** and vice versa. ** X ** -v toggle verbose output; removes 'is on a' etc. ** X ** and vice versa. ** X ** -d prints day of week number: 1 is Monday, 7 is Sunday ** X ** -D gives day of week name, i.e Monday or Mon ** X ** -m prints the month number ** X ** -M outputs the month name, i.e. January or Jan ** X ** -y outputs the year number ** X ** -w gives the week number ** X ** -j outputs the julian day number ** X ** -n gives day of month ** X ** -a gives everything it knows. Without the -s and -v ** X ** options this outputs the same as the default case. ** X ** In fact -a is just short for -nmyDjw. ** X ** -h give a usage message ** X ** ** X ** With the verbose option on, each printing option speaks to ** X ** you in a more or less english way. ** X ** All options that print something also print a space ** X ** character (except -a). ** X ** The options may come in any order and as many times as ** X ** you like. The -s and -v options only have an effect on the ** X ** options that follow them. ** X ** When now option is given (except for the -v and -s option) ** X ** the -a option is default. ** X ** The output is ended with a newline ** X ** Examples: ** X ** ** X ** % weekday -yjMdD 12/31/89 ** X ** 89 365 December 7 Sunday ** X ** ** X ** % weekday -yjMsdD 12/31/89 ** X ** 89 365 December 7 Sun ** X ** ** X ** % weekday -yjsMsdD 12/31/89 ** X ** 89 365 Dec 7 Sunday ** X ** ** X ** % weekday -v 30/30/89 ** X ** 30 Oct 1989 is on a Mon on julian day 303 in week 44 ** X ** ** X ** BUGS: the program assumes there have always been leap years ** X ** so year 4 is a leap year as far as the program can tell... ** X ** ** X ** This software is copyrighted (c) 1989 1990 by M.J.E. Mol. ** X ** This code or part of this code may be used by freeware, ** X ** public domain or any other non-commercial product. I only ask ** X ** you to give me credit for the used code in the documentation ** X ** and in your source code. ** X ** ** X ** If you want to use this code or part of this code in your ** X ** shareware or any other commercial product, I ask you to contact ** X ** me so we can setup an arrangement for the use of the code or part ** X ** of the code. ** X ** ** X ** In any case, I can not be held responsible for any damage this ** X ** code or part of this code may cause you or anyone or anything ** X ** else. ** X ** ** X ** Mail bugs to: email: marcel@duteca.tudelft.nl ** X ** pmail: Marcel J.E. Mol ** X ** Karel Doormanlaan 58 ** X ** 2283AT Rijswijk ** X ** The Netherlands (Holland) ** X ** Phone: (+31) 070-3932390 ** X ** Bankaccount: 36.89.35.922 (In the Netherlands) ** X ** ** X ************************************************************************/ X Xchar * copyright = "@(#) weekday.c 3.40 02/06/90 (c) M.J.E. Mol"; X X#include <stdio.h> X#include <string.h> X#include <time.h> X X X#define CENTURY 1900 /* prefix for years */ X#define LEAP(year) ((year%4 == 0) && (year%100 != 0) || (year%400 == 0)) X X/* X * Global variables X */ X Xchar *weekdays[8] = {"", "Monday", "Tuesday", "Wednesday", "Thursday", X "Friday", "Saterday", "Sunday"}; Xchar days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; Xint juldays[13] = {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; Xchar *monthname[13] = {"", "January", "February", "March", "April", X "May", "June", "July", "August", X "September", "October", "November", "December"}; X Xint shortflag = 0; /* short output of day and month name */ Xint verbose = 1; /* output in a 'sentence' */ Xchar * progname; X X X/* X * function declarations X */ Xvoid main(); Xvoid scanmmddyy(); Xvoid scanyyyymmdd(); Xvoid scanddmmyy(); Xint scanjul(); Xint scandate(); Xint julianday(); Xvoid julian2day(); Xint weekday(); Xint weeknr(); Xvoid checkdate(); Xvoid usage(); X X Xvoid main(argc, argv) Xint argc; Xchar **argv; X{ X int year, month, day, week; X int wday = -1; X int jday = -1; X int opt, haveopt; X char *date; X extern int optind; /* For getopt */ X extern char * optarg; /* For getopt */ X X progname = *argv; X date = argv[argc-1]; X X /* X * pick up the date argument and scan it X */ X if (*date == '-' || argc == 1) /* no date, take current */ X wday = scandate(NULL, &year, &month, &day); X else if (strchr(date, '/') != NULL) /* mm/dd/yy format date */ X scanmmddyy(date, &year, &month, &day); X else if (strchr(date, '-') == date+4) /* yyyy-mm-dd format */ X scanyyyymmdd(date, &year, &month, &day); X else if (strchr(date, '-') != NULL) /* dd-mm-yy format date */ X scanddmmyy(date, &year, &month, &day); X else if (strchr(date, '.') != NULL) /* yyyy.ddd format date */ X jday = scanjulian(date, &year, &month, &day); X else /* unix date output format */ X wday = scandate(date, &year, &month, &day); X X /* X * get missing data X */ X if (jday == -1) X jday = julianday(year, month, day); X if (wday == -1) X wday = weekday(year, month, day); X week = weeknr(year, month, day); X X X /* X * print the wanted stuff ... X */ X while ((opt = getopt(argc, argv, "svdDmMywjnah")) != EOF) { X switch (opt) { X case 's' : shortflag ^= 1; X break; X case 'v' : verbose ^= 1; X break; X case 'D' : printf("%s%.*s ", X verbose ? "is on a " : "", X shortflag ? 3 : 9 , weekdays[wday]); X haveopt = 1; X break; X case 'd' : printf("%s%d ", verbose ? "is weekday " : "", wday); X haveopt = 1; X break; X case 'm' : printf("%d ", month); X haveopt = 1; X break; X case 'M' : printf("%.*s ", shortflag ? 3 : 9, monthname[month]); X haveopt = 1; X break; X case 'y' : printf("%d ", year); X haveopt = 1; X break; X case 'w' : printf("%s%d ", verbose ? "in week " : "", week); X haveopt = 1; X break; X case 'j' : printf("%s%d ", verbose ? "on julian day " : "", jday); X haveopt = 1; X break; X case 'n' : printf("%d ", day); X haveopt = 1; X break; X case 'a' : if (verbose) { X printf("%d %.*s %d is on a %.*s ", X day, shortflag ? 3 : 9, monthname[month], X year, shortflag ? 3 : 9, weekdays[wday]); X printf("on julian day %d in week %d", jday, week); X } X else X printf("%d %.*s %d %.*s %d %d", X day, shortflag ? 3 : 9, monthname[month], X year, shortflag ? 3 : 9, weekdays[wday], X jday, week); X haveopt = 1; X break; X case 'h' : usage(); X break; X default : fprintf(stderr, X "%s: use -h option for help\n", progname); X exit(1); X break; X } X } X X X if (!haveopt) { X if (verbose) { X printf("%d %.*s %d is on a %.*s ", X day, shortflag ? 3 : 9, monthname[month], X year, shortflag ? 3 : 9, weekdays[wday]); X printf("on julian day %d in week %d", jday, week); X } X else X printf("%d %.*s %d %.*s %d %d", X day, shortflag ? 3 : 9, monthname[month], X year, shortflag ? 3 : 9, weekdays[wday], X jday, week); X } X X /* X * finish with a newline X */ X putchar('\n'); X X exit(0); X X} /* main */ X/************************************************************************* X ** ** X ** DATE SCANNING ROUTINES ** X ** ** X *************************************************************************/ X X/* X * scanmmddyy --- scan the date variable in the form MM/DD/YY. X * MM, DD and YY may be variable sized numbers. X * output is in output variables year, month and day. X */ Xvoid scanmmddyy(date, year, month, day) Xchar *date; Xint *year, *month, *day; X{ X char *i; X X if (sscanf(date, "%d/%d/%d", month, day, year) != 3 || X *month <= 0 || *day <= 0 || *year < 0) { X fprintf(stderr, "%s: error in mm/dd/yy format date %s\n", X progname, date); X exit(4); X } X X if (*year < 100) { X i = strrchr(date, '/') + 1; X if (*i != '0') X *year += CENTURY; X } X X return; X X} /* scanmmddyy */ X X X X/* X * scanyyyymmdd --- scan the date variable in the form YYYY-MM-DD. X * YYYY, MM and DD must be 4, 2 and 2 digits wide. X * output is in output variables year, month and day. X */ Xvoid scanyyyymmdd(date, year, month, day) Xchar *date; Xint *year, *month, *day; X{ X X if (sscanf(date, "%4d-%2d-%2d", year, month, day) != 3 || X *month <= 0 || *day <= 0 || *year < 0) { X fprintf(stderr, "%s: error in yyyy-mm-dd format date %s\n", X progname, date); X exit(4); X } X X return; X X} /* scanyyyymmdd */ X X X/* X * scanddmmyy --- scan the date variable in the form DD-MM-YY. X * Also YYYY-MM-DD is recognized if YYYY > 31 and DD <= 31. X * DD, MM and YY may be variable sized numbers. X * output is in output variables year, month and day. X */ Xvoid scanddmmyy(date, year, month, day) Xchar *date; Xint *year, *month, *day; X{ X char *i; X X if (sscanf(date, "%d-%d-%d", day, month, year) != 3 || X *month <= 0 || *day <= 0 || *year < 0) { X fprintf(stderr, "%s: error in dd-mm-yy format date %s\n", X progname, date); X exit(4); X } X X if (*year < 100) { X i = strrchr(date, '-') + 1; X if (*i != '0') X *year += CENTURY; X } X X return; X X} /* scanddmmyy */ X X X X/* X * scanjulian --- scan the date variable in the form YY.DDD. X * YY and DDD may be variable sized numbers. X * output is in output variables year, month and day. X * the return value is tha actual julian day DDD. X */ Xint scanjulian(date, year, month, day) Xchar *date; Xint *year, *month, *day; X{ X int jday; X X if (sscanf(date, "%d.%d", year, &jday) != 2 || *year < 0 || jday <= 0) { X fprintf(stderr, "%s: error in julian date %s\n", progname, date); X exit(4); X } X X if ((*year < 100) && (*date != '0')) X *year += CENTURY; X julian2day(*year, jday, month, day); X X return jday; X X} /* scanjulian */ X X X X/* X * scandate --- scan the date variable in the Unix `date` form. X * This can look like: "Mon Jan 8 20:10:10 MET 1990" X * or: "Mon Jan 8 20:10:10 1990" X * the number of spaces between the fields is not fixed X * (although date output is always in the same with, see ctime(3). X * output is in output variables year, month and day. X * the return value is the day of the week number. X * If the date input is the NULL pointer, the xurrent system date X * is taken. X */ Xint scandate(date, year, month, day) Xchar *date; Xint *year, *month, *day; X{ X char *i; X char dayname[4]; X char monname[10]; X int j, k; X long tim; X X if (date == NULL) { X tim = time((long *) 0); X date= ctime(&tim); X } X X if (sscanf(date, "%3s %3s %d %*s %d", /* date without time zone */ X dayname, monname, day, year) != 4 && X sscanf(date, "%3s %3s %d %*s %*s %d", /* date with time zone */ X dayname, monname, day, year) != 4) { X fprintf(stderr, "%s: error in unix `date` format date %s\n", X progname, date); X exit(4); X } X X for(j=1; j<12; j++) { X if (strncmp(monname, monthname[j], 3) == NULL) X break; X } X if (strncmp(monname, monthname[j], 3) != NULL) { X fprintf(stderr, "%s: error in unix `date` format date %s\n", X progname, date); X exit(4); X } X *month = j; X for(j=1; j<7; j++) { X if (strncmp(dayname, weekdays[j], 3) == NULL) X break; X } X if (strncmp(dayname, weekdays[j], 3) != NULL) { X fprintf(stderr, "%s: error in unix `date` format date %s\n", X progname, date); X exit(4); X } X X return j; X X} /* scandate */ X/************************************************************************* X ** ** X ** DATE CONVERSION ROUTINES ** X ** ** X *************************************************************************/ X X X X/* X * julian2day -- converts year and julian day value to the corresponding X * month and day of the month values for that year. X * these values are returned in the ouput variables month and day. X */ Xvoid julian2day(year, jday, month, day) Xint year, jday; Xint *month, *day; X{ X int leap; X X leap = LEAP(year) ? 1 : 0; X if (jday > 365+leap || jday <= 0) { X fprintf(stderr, "%s: error in julian date %d.%d\n", X progname, year, jday); X exit(8); X } X X if (jday <= 31) { X *month = 1; X *day = jday; X return; X } X X jday -= leap; X if (jday <= 59) { X *month = 2; X *day = jday - 31 + leap; X } X else if (jday <= 90) { X *month = 3; X *day = jday - 59; X } X else if (jday <= 120) { X *month = 4; X *day = jday - 90; X } X else if (jday <= 151) { X *month = 5; X *day = jday - 120; X } X else if (jday <= 181) { X *month = 6; X *day = jday - 151; X } X else if (jday <= 212) { X *month = 7; X *day = jday - 181; X } X else if (jday <= 243) { X *month = 8; X *day = jday - 212; X } X else if (jday <= 273) { X *month = 9; X *day = jday - 243; X } X else if (jday <= 304) { X *month = 10; X *day = jday - 273; X } X else if (jday <= 334) { X *month = 11; X *day = jday - 304; X } X else { X *month = 12; X *day = jday - 334; X } X X return; X X} /* julian2day */ X X X X/* X * julianday -- converts a year, mont and day value the corresponding julian X * day number of that year. X * the julian day is passed as a return value. X */ Xjulianday(year, month, day) Xint year, month, day; X{ X register int doy; X X checkdate(year, month, day); /* exits if date in error */ X X doy = juldays[month]; X if ((month > 2) && LEAP(year)) X doy++; X doy += day; X X return doy; X X} /* julianday */ X X X X/* X * weekday -- returns the day of the week number based on the given X * year, month and day of the month value. X */ Xweekday(year, month, day) Xint year, month, day; X{ X register int dow; X X checkdate(year, month, day); /* exits if date in error */ X X dow = 6 + /* the magic number */ X year + /* shift a day each year */ X year/4 - year/100 + year/400 + /* shift a day each leap year */ X juldays[month] + day; /* day of year */ X X if ((month < 3) && LEAP(year)) /* first two months of leap don't */ X dow--; /* have the extra leap shift */ X X dow %= 7; X return (dow == 0) ? 7 : dow; /* convert 0 to 7 */ X X} /* weekday */ X X X/* X * weeknr -- returns the weeknumber of the given date (year, month and day of X * the month) for that year. X */ Xweeknr(year, month, day) Xint year, month, day; X{ X register int fday, jul, weeknum; X X checkdate(year, month, day); /* exits if date in error */ X X fday = weekday(year, 1, 1); X X jul = julianday(year, month, day); X weeknum = (jul + fday - 2)/7; X if (fday <= 4) X weeknum++; X X if (weeknum == 0) /* go into previous year */ X if ((fday == 5) || /* week 1 has 4 or 5 days and week */ X ((fday == 6) && LEAP(year-1)) ) /* 53 has 4 or 5 days in year-1 */ X return 53; X else X return 52; X X if (weeknum == 53) { /* only a week 53 if we have > 4 */ X fday = weekday(year, 12, 31); /* days in the week */ X if (fday < 4) X weeknum = 1; X } X X return weeknum; X X} /* weeknr */ X/************************************************************************* X ** ** X ** OTHER ROUTINES ** X ** ** X *************************************************************************/ X X X X/* X * checkdate -- checks if the given date is a valid one. X * if not, it just exits. X */ Xvoid checkdate(year, month, day) Xint year, month, day; X{ X int dim; X X if (year < 0) { X fprintf(stderr, "%s: %d is an invalid year\n", progname, year); X exit(2); X } X X if ((month < 1) || (month > 12)) { X fprintf(stderr, "%s: %d is an invalid month\n", progname, month); X exit(2); X } X X dim = days[month]; X if ((month == 2) && LEAP(year)) X dim++; X if ((day > dim) || (day < 1)) { X fprintf(stderr, "%s: %d is an invalid day in %s %d\n", progname, X day, monthname[month], year); X exit(2); X } X X return; X X} /* checkdate */ X X X X/* X * usage -- give usage message. X */ Xvoid usage() X{ X X fprintf(stderr,"Usage: weekday [-svdDmMywjnah] [<date>]\n"); X fprintf(stderr," <date>: mm/dd/yy | dd-mm-yy | yyyy-mm-dd\n |"); X fprintf(stderr," yy.ddd | Unix date\n"); X fprintf(stderr," default is today\n"); X fprintf(stderr," options: -s toggle short form output\n"); X fprintf(stderr," -v toggle verbose output\n"); X fprintf(stderr," -d prints day of week number\n"); X fprintf(stderr," -D gives day of week name\n"); X fprintf(stderr," -m prints the month number\n"); X fprintf(stderr," -M outputs the month name\n"); X fprintf(stderr," -y outputs the year number\n"); X fprintf(stderr," -w gives the week number\n"); X fprintf(stderr," -j outputs the julian day number\n"); X fprintf(stderr," -n gives day of month\n"); X fprintf(stderr," -a gives everything it knows (default)\n"); X fprintf(stderr," -h give a usage message\n"); X X return; X X} /* usage */ EOF chars=`wc -c < 'weekday.c'` if test $chars != 27998; then echo 'weekday.c' is $chars characters, should be 27998 characters!; fi fi exit 0 -- ######################################### # Marcel J.E. Mol ###################################### # Delft University of Technology Pink Elephant Management Services # # The Netherlands Voorburg # # UUCP: marcel@duteca.tudelft.nl Tel: 070-694231 # # ###################################### #########################################