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