############################################################################ # # File: daystil.icn # # Subject: Program to calculate the number of days until a given date # # Author: Nevin Liber # # Date: June 29, 1994 # ########################################################################### # # This file is in the public domain. # ############################################################################ # # Usage: daystil sMonth iDay # # Returns: # 0 number of days written on &output # 1 Usage message on &errout (bad parameters) # # Revision History: # <1> njl 6/29/94 9:50 PM First written # # This program calculates the number of days between the current date # and the date specified on the command line, and writes this number to # &output. This is useful if you want to know how many days it is # until a birthday, wedding day, etc. # # The date on the command line can be specified in a variety of ways. # For instance, if you wanted to know how many days it is until # August 12 (my birthday), you could specify it as "August 12", "Aug 12", # "12 August", or "12 aUGuS", among others. The match is case # insensitive, and the arguments will be accepted as long as exactly # one of them is an integer, and if there are exactly two arguments. # ########################################################################### # # NumberOfDays(sMonth, iDay, iYear) : iNumberOfDays # # NumberOfDays() returns the number of days into iYear that sMonth/iDay # occurs. For instance, NumberOfDays("February", 28) returns 59, since # it is the 59th day into any year. Leap years from 1901 until 2099 # are taken into account. It fails if any parameters are bad. # # Defaults: # sMonth current month # iDay current day of the current month # iYear current year # ############################################################################ procedure NumberOfDays(sMonth, iDay, iYear) static LMonths static LDays static sThisMonth static iThisDay static iThisYear local iDays local i initial { LMonths := [ "january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december" ] LDays := [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30 ] &dateline ? { &pos := find(" ") + 1 sThisMonth := tab(find(" ")) &pos +:= 1 iThisDay := integer(tab(find(","))) &pos +:= 2 iThisYear := integer(move(4)) } } /sMonth := sThisMonth /iDay := iThisDay /iYear := iThisYear sMonth := string(sMonth) | fail iDay := integer(iDay) | fail iYear := integer(iYear) | fail if 0 ~= iYear % 4 then { LDays[2] := 28 } else { LDays[2] := 29 } iDays := iDay every i := 1 to *LMonths do { if CaselessMatch(sMonth, LMonths[i]) then { return iDays } iDays +:= LDays[i] } end ############################################################################ # # CaselessMatch(s1, s2, i1, i2) : i3 caseless match of initial string # # CaselessMatch(s1, s2, i1, i2) produces i1 + *s1 if # map(s1) == map(s2[i1+:*s1]) but fails otherwise. # # This is the same as the built-in function match(), except the # comparisons are done without regard to case. # # Defaults: # s2 &subject # i1 &pos if s2 is defaulted, otherwise 1 # i2 0 # # Errors: # 101 i1 or i2 not integer # 103 s1 or s2 not string # ############################################################################ procedure CaselessMatch(s1, s2, i1, i2) s1 := map(string(s1)) /i1 := (/s2 & &pos) s2 := map(string(s2) | (/s2 & &subject)) return match(s1, s2, i1, i2) end ############################################################################ # # Usage(fErrout, iStatus) write usage message to fErrout and exit # # Usage(fErrout, iStatus) writes the usage message to file fErrout # and exits with exit status code iStatus # # Defaults: # fErrout &errout # iStatus 1 # ############################################################################ procedure Usage(fErrout, iStatus) /fErrout := &errout iStatus := (integer(iStatus) | 1) write(fErrout, "Usage: DaysTil sMonth iDay") exit(iStatus) end ############################################################################ # # main(LArguments) # # main(LArguments) checks to make sure there are two arguments, exactly # one of which is an integer. If so, it tries to calculate the number # of days between the current date and the date specified, taking into # account if the specified date occurs after today's date only in the # following year. On a parameter error, it writes the usage message # to &errout and exits with status 1. Otherwise, it writes the number # of days to &output and exits with status 0. # ############################################################################ procedure main(LArguments) local sArgument local sMonth local iDay local iToday local iNumberOfDays if 2 ~= *LArguments then { Usage() } every sArgument := !LArguments do { (iDay := integer(sArgument)) | (sMonth := sArgument) } if /iDay | /sMonth then { Usage() } iToday := NumberOfDays() iNumberOfDays := NumberOfDays(sMonth, iDay) | Usage() if iNumberOfDays < iToday then { iNumberOfDays := NumberOfDays("december", 31) + NumberOfDays(sMonth, iDay, integer(&date[1+:4]) + 1) } write(iNumberOfDays - iToday) end