Contributor: ALAN GRAFF (* * * * * * * * * * * * * * * * * * * * * * *) (* UNIT: DTIME - By Alan Graff, Nov. 92*) (* Compiled from routines found in: *) (* DATEPAK4: W.G.Madison, Nov. 87 *) (* UNIXDATE: Brian Stark, Jan. 92 *) (* Plus various things of my own creation *) (* and extracted from Fidonet PASCAL echo *) (* messages and other sources. *) (* Contributed to the Public Domain *) (* Version 1.1 - Nov. 1992 *) (* * * * * * * * * * * * * * * * * * * * * * *) UNIT DTime; {**************************************************************} INTERFACE uses crt,dos; TYPE DATETYPE = record day:WORD; MONTH:WORD; YEAR:WORD; dow:word; end; (* Sundry determinations of current date/time variables *) Function DayOfYear:word; (* Returns 1 to 365 *) Function DayOfMonth:word; (* Returns 1 to 31 *) Function DayOfWeek:word; (* Returns 1 to 7 *) Function MonthOfYear:word; (* Returns 1 to 12 *) Function ThisYear:word;(* Returns current year *) Function ThisHour:word;(* Returns 1 to 24 *) Function ThisMinute:word; (* Returns 0 to 59 *) (* Calculate what day of the week a particular date falls on *) Procedure WkDay(Year,Month,Day:Integer; var WeekDay:Integer); (* Full Julian conversions *) Procedure GregorianToJulianDN(Year,Month,Day:Integer;var JulianDN:LongInt); Procedure JulianDNToGregorian(JulianDN:LongInt;var Year,Month,Day:Integer); (* 365 day Julian conversions *) Procedure GregorianToJulianDate(Year,Month,Day:Integer;var JulianDate:Integer); Procedure JulianToGregorianDate(JulianDate,Year:Integer;var Month,Day:Integer); (* Sundry string things *) Function DateString:String; (* Returns system date as "mm-dd-yy" string *) Function TimeString:String; (* Returns system time as "00:00:00" string *) (* Create current YYMMDD string to use as a file name *) Function DateAFile(dy,dm,dd:word):string; (* Return YY-MM-DD string from filename created by DateAFile func *) Function Parsefile(s:string):string; (* Return values of 1 day ago *) Procedure Yesterday(Var y,m,d:integer); (* Return values of 1 day ahead *) Procedure Tomorrow(Var y,m,d:integer); (* Adjust time based on "TZ" environment *) Function GetTimeZone : ShortInt; Function IsLeapYear(Source : Word) : Boolean; (* What it says :-) *) (* Unix date conversions *) Function Norm2Unix(Y,M,D,H,Min,S:Word):LongInt; Procedure Unix2Norm(Date:LongInt;Var Y,M,D,H,Min,S:Word); (* Determines what day of year Easter falls on *) Procedure Easter(Year:Word;Var Date:DateType); (* Determines what day of year Thanksgiving falls on *) Procedure Thanksgiving(Year:Word;Var Date:DateType); (* Determine what percentage of moon is lit on a particular night *) Function MoonPhase(Date:Datetype):Real; IMPLEMENTATION const D0 =1461; D1 = 146097; D2 = 1721119; DaysPerMonth : Array[1..12] of ShortInt = (031,028,031,030,031,030,031,031,030,031,030,031); DaysPerYear : Array[1..12] of Integer = (031,059,090,120,151,181,212,243,273,304,334,365); DaysPerLeapYear :Array[1..12] of Integer = (031,060,091,121,152,182,213,244,274,305,335,366); SecsPerYear : LongInt = 31536000; SecsPerLeapYear : LongInt = 31622400; SecsPerDay : LongInt = 86400; SecsPerHour : Integer = 3600; SecsPerMinute: ShortInt = 60; Procedure GregorianToJulianDN; var Century, XYear: LongInt; begin {GregorianToJulianDN} If Month <= 2 then begin Year := pred(Year); Month := Month + 12; end; Month := Month - 3; Century := Year div 100; XYear := Year mod 100; Century := (Century * D1) shr 2; XYear := (XYear * D0) shr 2; JulianDN := ((((Month * 153) + 2) div 5) + Day) + D2 + XYear + Century; end; {GregorianToJulianDN} {**************************************************************} Procedure JulianDNToGregorian; var Temp, XYear : LongInt; YYear, YMonth, YDay: Integer; begin {JulianDNToGregorian} Temp := (((JulianDN - D2) shl 2) - 1); XYear := (Temp mod D1) or 3; JulianDN := Temp div D1; YYear := (XYear div D0); Temp := ((((XYear mod D0) + 4) shr 2) * 5) - 3; YMonth := Temp div 153; If YMonth >= 10 then begin YYear := YYear + 1; YMonth := YMonth - 12; end; YMonth := YMonth + 3; YDay := Temp mod 153; YDay := (YDay + 5) div 5; Year := YYear + (JulianDN * 100); Month := YMonth; Day := YDay; end; {JulianDNToGregorian} {**************************************************************} Procedure GregorianToJulianDate; var Jan1, Today : LongInt; begin {GregorianToJulianDate} GregorianToJulianDN(Year, 1, 1, Jan1); GregorianToJulianDN(Year, Month, Day, Today); JulianDate := (Today - Jan1 + 1); end; {GregorianToJulianDate} {**************************************************************} Procedure JulianToGregorianDate; var Jan1 : LongInt; begin GregorianToJulianDN(Year, 1, 1, Jan1); JulianDNToGregorian((Jan1 + JulianDate - 1), Year, Month, Day); end; {JulianToGregorianDate} {**************************************************************} Procedure WkDay; var DayNum : LongInt; begin GregorianToJulianDN(Year, Month, Day, DayNum); DayNum := ((DayNum + 1) mod 7); WeekDay := (DayNum) + 1; end; {DayOfWeek} {**************************************************************} Procedure Yesterday(Var Y,M,D:integer); var jdn:longint; begin GregorianToJulianDN(Y,M,D,JDN); JDN:=JDN-1; JulianDNToGregorian(JDN,Y,M,D); end; {**************************************************************} Procedure Tomorrow(Var Y,M,D:integer); var JDN:longint; begin GregorianToJulianDN(Y,M,D,JDN); JDN:=JDN+1; JulianDNToGregorian(JDN,Y,M,D); end; {**************************************************************} Function TimeString:string; var hr,mn,sec,hun:word; s,q:string; begin q:=''; gettime(hr,mn,sec,hun); if hr<10 then q:=q+'0'; str(hr:1,s); q:=q+s+':'; if mn<10 then q:=q+'0'; str(mn:1,s); q:=q+s; TimeString:=q; end; {**************************************************************} Function ThisHour:Word; var hr,mn,sec,hun:word; begin gettime(hr,mn,sec,hun); ThisHour:=hr; end; {**************************************************************} Function ThisMinute:Word; var hr,mn,sec,hun:word; begin gettime(hr,mn,sec,hun); ThisMinute:=mn; end; {**************************************************************} Function DateString:string; var yr,mo,dy,dow:word; s,q:string; begin q:=''; getdate(yr,mo,dy,dow); if mo<10 then q:=q+'0'; str(mo:1,s); q:=q+s+'-'; if dy<10 then q:=q+'0'; str(dy:1,s); q:=q+s+'-'; while yr>100 do yr:=yr-100; if yr<10 then q:=q+'0'; str(yr:1,s); q:=q+s; Datestring:=q; end; {**************************************************************} Function parsefile(s:string):string; { Return date string from a file name } var mo,errcode:word; { in either YYMMDD.EXT or MMDDYY.EXT } st:string;{ format. } begin st:=copy(s,1,2)+'-'+copy(s,3,2)+'-'+copy(s,5,2); parsefile:=st; end; {**************************************************************} function dateafile(dy,dm,dd:word):string; var s1,s2:string; begin while dy>100 do dy:=dy-100; str(dy,s1); while length(s1)<2 do s1:='0'+s1; s2:=s1; str(dm,s1); while length(s1)<2 do s1:='0'+s1; s2:=s2+s1; str(dd,s1); while length(s1)<2 do s1:='0'+s1; s2:=s2+s1; dateafile:=s2; end; {**************************************************************} Function DayOfMonth:Word; var yr,mo,dy,dow:word; begin getdate(yr,mo,dy,dow); DayOfMonth:=dy; end; {**************************************************************} Function ThisYear:Word; var yr,mo,dy,dow:word; begin getdate(yr,mo,dy,dow); ThisYear:=yr; end; {**************************************************************} Function DayOfWeek:word; var yr,mo,dy,dow:word; begin getdate(yr,mo,dy,dow);(* Turbo Pascal authors never saw a *) dow:=dow+1; (* calendar. Their first day of*) if dow=8 then dow:=1; (* week is Monday.... *) DayOfWeek:=dow; end; {**************************************************************} Function MonthOfYear:Word; var yr,mo,dy,dow:word; begin getdate(yr,mo,dy,dow); monthofyear:=mo; end; {**************************************************************} Function GetTimeZone : ShortInt; Var Environment : String; Index : Integer; Begin GetTimeZone := 0;{Assume UTC} Environment := GetEnv('TZ'); {Grab TZ string} For Index := 1 To Length(Environment) Do Environment[Index] := Upcase(Environment[Index]); If Environment = 'EST05'Then GetTimeZone := -05; {USA EASTERN} If Environment = 'EST05EDT' Then GetTimeZone := -06; If Environment = 'CST06'Then GetTimeZone := -06; {USA CENTRAL} If Environment = 'CST06CDT' Then GetTimeZone := -07; If Environment = 'MST07'Then GetTimeZone := -07; {USA MOUNTAIN} If Environment = 'MST07MDT' Then GetTimeZone := -08; If Environment = 'PST08'Then GetTimeZone := -08; If Environment = 'PST08PDT' Then GetTimeZone := -09; If Environment = 'YST09'Then GetTimeZone := -09; If Environment = 'AST10'Then GetTimeZone := -10; If Environment = 'BST11'Then GetTimeZone := -11; If Environment = 'CET-1'Then GetTimeZone := 01; If Environment = 'CET-01' Then GetTimeZone := 01; If Environment = 'EST-10' Then GetTimeZone := 10; If Environment = 'WST-8'Then GetTimeZone := 08; {Perth,W.Austrailia} If Environment = 'WST-08' Then GetTimeZone := 08; End; {**************************************************************} Function IsLeapYear(Source : Word) : Boolean; Begin If (Source Mod 4 = 0) Then IsLeapYear := True Else IsLeapYear := False; End; {**************************************************************} Function Norm2Unix(Y,M,D,H,Min,S : Word) : LongInt; Var UnixDate : LongInt; Index: Word; Begin UnixDate := 0; {initialize} Inc(UnixDate,S); {add seconds} Inc(UnixDate,(SecsPerMinute * Min)); {add minutes} Inc(UnixDate,(SecsPerHour * H)); {add hours} UnixDate := UnixDate - (GetTimeZone * SecsPerHour); {UTC offset} If D > 1 Then {has one day already passed?} Inc(UnixDate,(SecsPerDay * (D-1))); If IsLeapYear(Y) Then DaysPerMonth[02] := 29 Else DaysPerMonth[02] := 28; {Check for Feb. 29th} Index := 1; If M > 1 Then For Index := 1 To (M-1) Do {has one month already passed?} Inc(UnixDate,(DaysPerMonth[Index] * SecsPerDay)); While Y > 1970 Do Begin If IsLeapYear((Y-1)) Then Inc(UnixDate,SecsPerLeapYear) Else Inc(UnixDate,SecsPerYear); Dec(Y,1); End; Norm2Unix := UnixDate; End; Procedure Unix2Norm(Date : LongInt; Var Y, M, D, H, Min, S : Word); {} Var LocalDate : LongInt; Done : Boolean; X : ShortInt; TotDays : Integer; Begin Y := 1970; M := 1; D := 1; H := 0; Min := 0; S := 0; LocalDate := Date + (GetTimeZone * SecsPerHour); {Local time date} Done := False; While Not Done Do Begin If LocalDate >= SecsPerYear Then Begin Inc(Y,1); Dec(LocalDate,SecsPerYear); End Else Done := True; If (IsLeapYear(Y+1)) And (LocalDate >= SecsPerLeapYear) And (Not Done) Then Begin Inc(Y,1); Dec(LocalDate,SecsPerLeapYear); End; End; M := 1; D := 1; Done := False; TotDays := LocalDate Div SecsPerDay; If IsLeapYear(Y) Then Begin DaysPerMonth[02] := 29; X := 1; Repeat If (TotDays <= DaysPerLeapYear[x]) Then Begin M := X; Done := True; Dec(LocalDate,(TotDays * SecsPerDay)); D := DaysPerMonth[M]-(DaysPerLeapYear[M]-TotDays) + 1; End Else Done := False; Inc(X); Until (Done) or (X > 12); End Else Begin DaysPerMonth[02] := 28; X := 1; Repeat If (TotDays <= DaysPerYear[x]) Then Begin M := X; Done := True; Dec(LocalDate,(TotDays * SecsPerDay)); D := DaysPerMonth[M]-(DaysPerYear[M]-TotDays) + 1; End Else Done := False; Inc(X); Until Done = True or (X > 12); End; H := LocalDate Div SecsPerHour; Dec(LocalDate,(H * SecsPerHour)); Min := LocalDate Div SecsPerMinute; Dec(LocalDate,(Min * SecsPerMinute)); S := LocalDate; End; {**************************************************************} Function DayOfYear; var HCentury,Century,Xyear, Ripoff,HXYear: LongInt; Holdyear,Holdmonth,Holdday:Integer; year,month,day,dofwk:word; begin {DayofYear} getdate(year,month,day,dofwk); Holdyear:=year-1; Holdmonth:=9; Holdday:=31; HCentury := HoldYear div 100; HXYear := HoldYear mod 100; HCentury := (HCentury * D1) shr 2; HXYear := (HXYear * D0) shr 2; Ripoff := ((((HoldMonth * 153) + 2) div 5) + HoldDay) + D2 + HXYear + HCentury; If Month <= 2 then begin Year := pred(Year); Month := Month + 12; end; Month := Month - 3; Century := Year div 100; XYear := Year mod 100; Century := (Century * D1) shr 2; XYear := (XYear * D0) shr 2; DayofYear := (((((Month * 153) + 2) div 5) + Day) + D2 + XYear + Century)- ripoff; end; {DayOfYear} Procedure Easter(Year : Word; Var Date : DateType); (* Calculates what day Easter falls on in a given year *) (* Set desired Year and result is returned in Date variable*) Var GoldenNo, Sun, Century, LeapCent, LunarCorr, Epact, FullMoon : Integer; Begin Date.Year := Year; GoldenNo := (Year Mod 19) + 1; Century := (Year Div 100) + 1; LeapCent := (3 * Century Div 4) - 12; LunarCorr := ((8 * Century + 5) Div 25) - 5; Sun := (5 * Year Div 4) - LeapCent - 10; Epact := Abs(11 * GoldenNo + 20 + LunarCorr - LeapCent) Mod 30; If ((Epact = 25) And (GoldenNo > 11)) Or (Epact = 24) then Inc(Epact); FullMoon := 44 - Epact; If FullMoon < 21 then Inc(FullMoon, 30); Date.Day := FullMoon + 7 - ((Sun + FullMoon) Mod 7); If Date.Day > 31 then Begin Dec(Date.Day, 31); Date.Month := 4; End Else Date.Month := 3; Date.DOW := 0; End; {**************************************************************} Procedure Thanksgiving(Year : Word; Var Date : DateType); (* Calculates what day Thanksgiving falls on in a given year *) (* Set desired Year and result is returned in Date variable*) Var Counter,WeekDay:Word; Daynum:longint; Begin Date.Year := Year; Date.Month := 11; counter:=29; repeat dec(counter); GregorianToJulianDN(Date.Year, Date.Month, Counter, DayNum); DayNum := ((DayNum + 1) mod 7); WeekDay := (DayNum) + 1; Until Weekday = 5; Date.Day:=Counter; End; {*************************************************************} Function MoonPhase(Date:Datetype):Real; (* Determines APPROXIMATE phase of the moon (percentage lit) *) (* 0.00 = New moon, 1.00 = Full moon *) (* Due to rounding, full values may possibly never be reached *) (* Valid from Oct. 15, 1582 to Feb. 28, 4000 *) (* Calculations adapted to Turbo Pascal from routines found in *) (* "119 Practical Programs For The TRS-80 Pocket Computer" *) (* John Clark Craig, TAB Books, 1982 (Ag) *) VAR j:longint; m:real; Begin GregorianToJulianDN(Date.Year,Date.Month,Date.Day,J); M:=(J+4.867)/ 29.53058; M:=2*(M-Int(m))-1; MoonPhase:=Abs(M); end; END.