!!!Date Routines Library of routines supporting the input, storage and manipulation of dates. General Information Author: Paul B. Loux \\ Language: ACTION! \\ Compiler/Interpreter: ACTION! \\ Published: 1986 \\ Requirements: EntryD() utilizes "EntryS()" (universal string entry routine), "PrintM()" (output formatter), and the "ValD()" function provided herein. EntryS() is available under the name ENTRYS.ACT PrintM() is available under the name PRINTM.ACT Four routines are provided to facilitate the storage and manipulation of dates. The CARD FUNC ValD(<string>) will convert a date in string format to a unique CARD value. The CARD returned by this function can be used to compute the number of calender days between two dates. The string can have non-numeric characters; for instance "12/31/85" is legal. Used together with its converse, PROC StrD(CARD number,<string>), it is also possible to find the calender date which falls a given number of days before or after a reference date. The string returned by StrD() contains only numbers; formatting must be performed separately. PROC Day(CARD number,<string>) provides the day of the week corresponding to a given calender date, as represented by a CARD value generated by ValD(). CARD FUNC EntryD() obtains a date from the keyboard. It uses EntryS(), the universal string entry utility; therefore it has the associated features of error checking, timeout, etc. EntryD() will assure the validity of the entered date, check it against optional minimum and maximum dates, and echo succesful entry in mm-dd-yy format, by use of PrintM(). The calling program provides the entry buffer, so EntryD() can be used to return a CARD value (as with ValD()) or to obtain an unformatted string (as with StrD()). PROC PrintM(<String>,<mask>) and its variants *ME,*MD,and *MDE can be used to print a date in any format desired, such as "mm-dd-yy". To facilitate usage into the next century, the date computations include a 40-year offset. Thus, the date "043020" is presumed to mean April 30, 2020. Therefore, date computations are only valid for dates within the range from 1-1-1940 through 12-31-2039. ValD() and StrD() are consistent in this regard. Note that more efficient storage results from use of CARD values (2 bytes) rather than strings (5 or 6 bytes plus length byte). This technique also facilitates ease in sorting data by date. Technical note: in general, any string variable should be pre- extended to its maxmium length prior to making a call which will use it to pass data. {{{ ;************************************ ;* * ;*(C)Copyright 1986 by Paul B. Loux * ;* * ;* These routines are in the public * ;* domain, and are not to be sold * ;* for a profit. They may be freely * ;* distributed, provided that this * ;* header remains in place. Use and * ;* enjoy! PBL, CIS 72337,2073. * ;* * ;************************************ ; ; File: DATES.LIB ; ; Desciption: Library of routines ; supporting the input, storage ; and manipulation of dates. ; ; Requirements: EntryD() utilizes ; "EntryS()" (universal string ; entry routine), "PrintM()" ; (output formatter), and the ; "ValD()" function provided ; herein. ; ; EntryS() is available under the ; name ENTRYS.ACT ; ; PrintM() is available under the ; name PRINTM.ACT ; ;************************************ ; ; CARD FUNC ValD() ; PROC StrD() ; PROC Day() ; CARD FUNC EntryD() ; ;************************************ ; ; Four routines are provided to ; facilitate the storage and ; manipulation of dates. The ; CARD FUNC ValD(<string>) will ; convert a date in string format ; to a unique CARD value. The ; CARD returned by this function ; can be used to compute the ; number of calender days between ; two dates. The string can have ; non-numeric characters; for ; instance "12/31/85" is legal. ; Used together with its converse, ; PROC StrD(CARD number,<string>), ; it is also possible to find ; the calender date which falls ; a given number of days before ; or after a reference date. ; The string returned by StrD() ; contains only numbers; formatting ; must be performed separately. ; ; PROC Day(CARD number,<string>) ; provides the day of the week ; corresponding to a given calender ; date, as represented by a CARD ; value generated by ValD(). ; ; CARD FUNC EntryD() obtains a ; date from the keyboard. It uses ; EntryS(), the universal string ; entry utility; therefore it has ; the associated features of error ; checking, timeout, etc. EntryD() ; will assure the validity of the ; entered date, check it against ; optional minimum and maximum ; dates, and echo succesful entry ; in mm-dd-yy format, by use of ; PrintM(). The calling program ; provides the entry buffer, so ; EntryD() can be used to return ; a CARD value (as with ValD()) ; or to obtain an unformatted ; string (as with StrD()). ; ; PROC PrintM(<String>,<mask>) and ; its variants *ME,*MD,and *MDE ; can be used to print a date in ; any format desired, such as ; "mm-dd-yy". ; ; To facilitate usage into the next ; century, the date computations ; include a 40-year offset. Thus, ; the date "043020" is presumed to ; mean April 30, 2020. Therefore, ; date computations are only valid ; for dates within the range from ; 1-1-1940 through 12-31-2039. ; ValD() and StrD() are consistent ; in this regard. ; ; Note that more efficient storage ; results from use of CARD values ; (2 bytes) rather than strings ; (5 or 6 bytes plus length byte). ; This technique also facilitates ; ease in sorting data by date. ; ; Technical note: in general, any ; string variable should be pre- ; extended to its maxmium length ; prior to making a call which ; will use it to pass data. ; ; ;************************************ ; ; "ValD()" ; ; Convert a date string into ; a unique CARD value. Input ; expected: ; ; "010185" ; "1-01-85" ; "Date: 01/01/85" ; etc. ; ; NOT: "1/1/85" ; CARD FUNC ValD(BYTE ARRAY dateS) BYTE ARRAY digits(0)="......" BYTE ARRAY month(0)="..", day(0)="..", year(0)=".." BYTE mm,dd,yy BYTE dmax,bad_date BYTE len1 BYTE len2 BYTE ctr,tmp BYTE xtmp,ztmp CARD value INT offset len1=dateS(0) len2=6 DO ; assure only digits tmp=dateS(len1) IF (tmp>47 AND tmp <58) THEN digits(len2)=tmp len2==-1 FI len1==-1 UNTIL len1=0 OR len2=0 OD IF len2>1 THEN ; 4 or less #'s RETURN(0) FI IF len2=1 THEN ; 5 #'s digits(1)=48 ; '0 FI digits(0)=6 SCopyS(month,digits,1,2) SCopyS(day,digits,3,4) SCopyS(year,digits,5,6) mm=ValB(month) dd=ValB(day) yy=ValB(year) bad_date=0 IF mm>12 OR ; legal date mm<1 OR ; checks dd<1 THEN bad_date=1 FI IF mm=2 THEN IF yy MOD 4 THEN dmax=28 ELSE dmax=29 FI ELSEIF mm=4 OR mm=6 OR mm=9 OR mm=11 THEN dmax=30 ELSE dmax=31 FI IF dd>dmax THEN bad_date=1 FI IF bad_date THEN RETURN(0) FI IF yy<40 THEN ; 40 year offset yy==+100 FI IF mm<3 THEN xtmp=0 ztmp=(yy-1)/4 ELSE xtmp=(4*mm + 23)/10 ztmp=yy/4 FI mm==-1 value=365*yy+31*mm+dd+ztmp-xtmp RETURN(value) ;************************************ ; ; "StrD()" ; ; Restores a date compressed ; to a CARD value by ValD(), ; into a fixed length string ; of six digital characters; ; no formating is performed. ; Example output: ; ; "010185" ; ; Note: calling program must ; pre-extend string "dateS" ; to six places. ; PROC StrD(CARD dateC BYTE ARRAY dateS) BYTE ARRAY mm(0)="..", dd(0)="..", yy(0)=".." BYTE POINTER ptr1,ptr2 INT m,d,y,r,s,t,y1,ly BYTE dmax y=0 y1=0 IF dateC>36524 THEN ; yy=1** dateC==-36525 FI IF dateC>29220 THEN ; # too big dateC==-7305 y1=20 FI IF dateC<61 THEN ; handle yr=0 dateC==+1461 y1=-4 FI y=dateC/365 r=dateC-(y*365)-y/4 IF r<31 THEN y==-1 r=dateC-(y*365)-y/4 FI IF r>59 then s=7 ELSE s=0 FI m=(r+s)/31 ly=(y/4)-((y-1)/4) IF m<3 THEN t=ly ELSE t=(4*m+23)/10 FI IF m=2 THEN IF y MOD 4 =0 THEN dmax=29 ELSE dmax=28 FI ELSEIF m=4 OR m=6 OR m=9 OR m=11 THEN dmax=30 ELSE dmax=31 FI d=r-31*(m-1)+t IF d>dmax THEN m==+1 IF m<3 THEN t=ly ELSE t=(4*m+23)/10 FI d=r-31*(m-1)+t FI IF m=13 THEN y==+1 m==-12 FI y==+y1 StrI(m,mm) StrI(d,dd) StrI(y,yy) SCopy(dateS,"000000") ptr1=mm+1 ptr2=dateS+1 IF mm(0)=1 THEN ptr2==+1 ptr2^=ptr1^ ELSE ptr2^=ptr1^ ptr1==+1 ptr2==+1 ptr2^=ptr1^ FI ptr1=dd+1 ptr2=dateS+3 IF dd(0)=1 THEN ptr2==+1 ptr2^=ptr1^ ELSE ptr2^=ptr1^ ptr1==+1 ptr2==+1 ptr2^=ptr1^ FI ptr1=yy+1 ptr2=dateS+5 IF yy(0)=1 THEN ptr2==+1 ptr2^=ptr1^ ELSE ptr2^=ptr1^ ptr1==+1 ptr2==+1 ptr2^=ptr1^ FI RETURN ;************************************ ; ; "Day()" ; ; Day of the week computation ; ; Returns variable-length string ; containing corresponding day ; of the week for the CARD value ; supplied. String can be easily ; massaged to obtain upper case ; only, first three letters,etc. ; ; Note: string "day" must be ; pre-xtended to 9 places by the ; the calling program, to allow ; room for "Wednesday" response. ; PROC Day(CARD dateC BYTE ARRAY day) CARD ref=[31412] ; Wednesday 1/1/86 INT dif BYTE num,dir BYTE ARRAY ptr CARD ARRAY dow(7) dow(0)="Wednesday" dow(1)="Thursday" dow(2)="Friday" dow(3)="Saturday" dow(4)="Sunday" dow(5)="Monday" dow(6)="Tuesday" dow(7)="Wednesday" dir=0 dif=dateC-ref IF dif<0 THEN dif=-dif dir=1 FI num=dif MOD 7 IF dir THEN num=7-num FI ptr=dow(num) SCopy(day,ptr) RETURN ;************************************ ; ; ; CARD FUNC EntryD() ; ; Data entry utility used to ; gather a calender date from ; the keyboard in the "mmddyy" ; format. The routine performs ; checks for illegal dates and ; echoes a valid response in ; "mm-dd-yy" format. Returns ; date as a CARD value as per ; ValD(), or as an unformatted ; string as per StrD(). ; ; This function uses both the ; EntryS() data entry utility ; and the PrintM() formatter. ; ; Calling options include the ; screen coordinates; high and ; low checks; null-entry flag; ; and exit flag, per EntryS(). ; ; ;************************************ INCLUDE "ENTRYS.ACT" INCLUDE "PRINTM.ACT" ;************************************ MODULE CARD FUNC EntryD(BYTE ARRAY field BYTE col,row,nullok,xit CARD min_date,max_date BYTE POINTER err_ptr) BYTE bad_date,accept,ctr, min,max,typec CARD value BYTE POINTER ptr1,ptr2 INT chk min=5 IF nullok THEN min=0 FI IF max_date=0 THEN max_date=51134 ; 12-31-39 FI max=6 typec=5 ; pos int accept=0 chk=0 DO POSITION(row,col) PRINT(" ") ENTRYS(field,min,max,typec,xit, col,row,err_ptr) IF err_ptr^#0 THEN RETURN(0) FI bad_date=0 IF field(0)=0 THEN IF nullok=1 THEN RETURN(0) ELSE bad_date=1 FI FI value=ValD(field) IF value=0 THEN bad_date=1 ELSEIF value<min_date OR value>max_date THEN bad_date=2 FI IF bad_date=1 THEN MSG(8) ELSEIF bad_date=2 THEN MSG(7) ELSE accept=1 FI UNTIL accept OD POSITION(col,row) PRINTM(field,"<Z/<Z/ZZ") RETURN(value) ;************************************ ; ; Example of usage of Date functions. PROC Test5() BYTE ARRAY date_field="......" BYTE ARRAY dow="........." BYTE x,y CARD date_val,min_date,max_date BYTE errcde BYTE POINTER err_ptr errcde=0 err_ptr=@errcde min_date=0 max_date=0 PUT(125) POSITION(1,5) x=22 y=5 PRINT("Enter date (mmddyy): ") date_val=EntryD(date_field,x,y,0,0, min_date,max_date, err_ptr) PUTE() PRINTE("The CARD value representing") PRINT("this date is ") PRINTC(date_val) PRINTE(".") PUTE() PRINTE("StrD() gives us back the string") StrD(date_val,date_field) PRINT("representation, ") PRINT(date_field) PRINTE(".") PUTE() PRINTE("Adding 31 days to this date") PRINT("gives us a CARD value of ") date_val==+31 PRINTC(date_val) PRINTE(".") PUTE() PRINTE("The date corresponding to this") PRINT("CARD value is ") StrD(date_val,date_field) PRINT(date_field) PRINTE(".") PUTE() PRINTE("The ValD() of this date gives") PRINT("back the CARD value, ") date_val=ValD(date_field) PRINTC(date_val) PRINTE(".") PUTE() PRINTE("The day of the week for these") PRINTE("two days is as follows:") PUTE() PRINT(date_field) PRINT(" ") Day(date_val,dow) PRINTE(dow) date_val==-31 StrD(date_val,date_field) PRINT(date_field) PRINT(" ") Day(date_val,dow) PRINTE(dow) PUTE() PRINTE("Done...") RETURN }}}