MODULE Dates;
IMPORT
Clock;
TYPE
DateTime* = RECORD
year*, month*, day*,
hour*, minute*, second*: LONGINT
END;
VAR
Months-: ARRAY 12 OF ARRAY 10 OF CHAR;
Days-: ARRAY 7 OF ARRAY 10 OF CHAR;
NoDays: ARRAY 12 OF INTEGER;
PROCEDURE LeapYear*(year: LONGINT): BOOLEAN;
BEGIN
RETURN (year > 0) & (year MOD 4 = 0) & (~(year MOD 100 = 0) OR (year MOD 400 = 0))
END LeapYear;
PROCEDURE NofDays*(year, month: LONGINT): LONGINT;
BEGIN
DEC(month);
ASSERT((month >= 0) & (month < 12));
IF (month = 1) & LeapYear(year) THEN RETURN NoDays[1]+1
ELSE RETURN NoDays[month]
END
END NofDays;
PROCEDURE ValidDateTime*(dt: DateTime): BOOLEAN;
BEGIN
RETURN (dt.year > 0) & (dt.month > 0) & (dt.month <= 12) & (dt.day > 0) & (dt.day <= NofDays(dt.year, dt.month)) &
(dt.hour >= 0) & (dt.hour < 24) & (dt.minute >= 0) & (dt.minute < 60) & (dt.second >= 0) & (dt.second < 60)
END ValidDateTime;
PROCEDURE OberonToDateTime*(Date, Time: LONGINT): DateTime;
VAR dt: DateTime;
BEGIN
dt.second := Time MOD 64; Time := Time DIV 64;
dt.minute := Time MOD 64; Time := Time DIV 64;
dt.hour := Time MOD 24;
dt.day := Date MOD 32; Date := Date DIV 32;
dt.month := Date MOD 16; Date := Date DIV 16;
dt.year := 1900 + Date;
RETURN dt
END OberonToDateTime;
PROCEDURE DateTimeToOberon*(dt: DateTime; VAR date, time: LONGINT);
BEGIN
ASSERT(ValidDateTime(dt));
date := (dt.year-1900)*512 + dt.month*32 + dt.day;
time := dt.hour*4096 + dt.minute*64 + dt.second
END DateTimeToOberon;
PROCEDURE Now*(): DateTime;
VAR d, t: LONGINT;
BEGIN
Clock.Get(t, d);
RETURN OberonToDateTime(d, t)
END Now;
PROCEDURE WeekDate*(Date: DateTime; VAR year, week, weekday: LONGINT);
VAR doy, i, yy, c, g, jan1: LONGINT; leap: BOOLEAN;
BEGIN
IF ValidDateTime(Date) THEN
leap := LeapYear(Date.year);
doy := Date.day; i := 0;
WHILE (i < Date.month-1) DO doy := doy + NoDays[i]; INC(i) END;
IF leap & (Date.month > 2) THEN INC(doy) END;
yy := (Date.year-1) MOD 100; c := (Date.year-1) - yy; g := yy + yy DIV 4;
jan1 := 1 + (((((c DIV 100) MOD 4) * 5) + g) MOD 7);
weekday := 1 + (((doy + (jan1-1))-1) MOD 7);
IF (doy <= (8-jan1)) & (jan1 > 4) THEN
year := Date.year-1;
IF (jan1 = 5) OR ((jan1 = 6) & LeapYear(year)) THEN week := 53
ELSE week := 52
END
ELSE
IF leap THEN i := 366 ELSE i := 365 END;
IF ((i - doy) < (4 - weekday)) THEN
year := Date.year + 1;
week := 1
ELSE
year := Date.year;
i := doy + (7-weekday) + (jan1-1);
week := i DIV 7;
IF (jan1 > 4) THEN DEC(week) END
END
END
ELSE
year := -1; week := -1; weekday := -1
END
END WeekDate;
PROCEDURE Equal*(t1, t2 : DateTime) : BOOLEAN;
BEGIN
RETURN
(t1.second = t2.second) & (t1.minute = t2.minute) & (t1.hour = t2.hour) &
(t1.day = t2.day) & (t1.month = t2.month) & (t1.year = t2.year);
END Equal;
PROCEDURE CompareDateTime*(t1, t2 : DateTime) : LONGINT;
VAR result : LONGINT;
PROCEDURE Compare(t1, t2 : LONGINT) : LONGINT;
VAR result : LONGINT;
BEGIN
IF (t1 < t2) THEN result := -1;
ELSIF (t1 > t2) THEN result := 1;
ELSE result := 0;
END;
RETURN result;
END Compare;
BEGIN
ASSERT(ValidDateTime(t1) & (ValidDateTime(t2)));
result := Compare(t1.year, t2.year);
IF (result = 0) THEN
result := Compare(t1.month, t2.month);
IF (result = 0) THEN
result := Compare(t1.day, t2.day);
IF (result = 0) THEN
result := Compare(t1.hour, t2.hour);
IF (result = 0) THEN
result := Compare(t1.minute, t2.minute);
IF (result = 0) THEN
result := Compare(t1.second, t2.second);
END;
END;
END;
END;
END;
RETURN result;
END CompareDateTime;
PROCEDURE TimeDifference*(t1, t2 : DateTime; VAR days, hours, minutes, seconds : LONGINT);
CONST SecondsPerMinute = 60; SecondsPerHour = 3600; SecondsPerDay = 86400;
VAR start, end : DateTime; year, month, second : LONGINT;
BEGIN
IF (CompareDateTime(t1, t2) = -1) THEN start := t1; end := t2; ELSE start := t2; end := t1; END;
IF (start.year = end.year) & (start.month = end.month) & (start.day = end.day) THEN
second := end.second - start.second + (end.minute - start.minute) * SecondsPerMinute + (end.hour - start.hour) * SecondsPerHour;
days := 0;
ELSE
second := SecondsPerDay - start.second - start.minute * SecondsPerMinute - start.hour * SecondsPerHour;
IF (start.year = end.year) & (start.month = end.month) THEN
days := (end.day - start.day) - 1;
ELSE
days := NofDays(start.year, start.month) - start.day;
IF (start.year = end.year) THEN
FOR month := start.month + 1 TO end.month - 1 DO
days := days + NofDays(start.year, month);
END;
ELSE
FOR month := start.month + 1 TO 12 DO
days := days + NofDays(start.year, month);
END;
FOR year := start.year + 1 TO end.year - 1 DO
IF LeapYear(year) THEN days := days + 366; ELSE days := days + 365; END;
END;
FOR month := 1 TO end.month - 1 DO
days := days + NofDays(end.year, month);
END;
END;
days := days + end.day - 1;
END;
second := second + end.second + end.minute * SecondsPerMinute + end.hour * SecondsPerHour;
END;
days := days + (second DIV SecondsPerDay); second := second MOD SecondsPerDay;
hours := second DIV SecondsPerHour; second := second MOD SecondsPerHour;
minutes := second DIV SecondsPerMinute; second := second MOD SecondsPerMinute;
seconds := second;
END TimeDifference;
PROCEDURE AddYears*(VAR dt : DateTime; years : LONGINT);
BEGIN
ASSERT(ValidDateTime(dt));
dt.year := dt.year + years;
ASSERT(ValidDateTime(dt));
END AddYears;
PROCEDURE AddMonths*(VAR dt : DateTime; months : LONGINT);
VAR years : LONGINT;
BEGIN
ASSERT(ValidDateTime(dt));
years := months DIV 12;
dt.month := dt.month + (months MOD 12);
IF (dt.month > 12) THEN
dt.month := dt.month - 12;
INC(years);
ELSIF (dt.month < 1) THEN
dt.month := dt.month + 12;
DEC(years);
END;
IF (years # 0) THEN AddYears(dt, years); END;
ASSERT(ValidDateTime(dt));
END AddMonths;
PROCEDURE AddDays*(VAR dt : DateTime; days : LONGINT);
VAR nofDaysLeft : LONGINT;
BEGIN
ASSERT(ValidDateTime(dt));
IF (days > 0) THEN
WHILE (days > 0) DO
nofDaysLeft := NofDays(dt.year, dt.month) - dt.day;
IF (days > nofDaysLeft) THEN
dt.day := 1;
AddMonths(dt, 1);
days := days - nofDaysLeft - 1;
ELSE
dt.day := dt.day + days;
days := 0;
END;
END;
ELSIF (days < 0) THEN
days := -days;
WHILE (days > 0) DO
nofDaysLeft := dt.day - 1;
IF (days > nofDaysLeft) THEN
dt.day := 1;
AddMonths(dt, -1);
dt.day := NofDays(dt.year, dt.month);
days := days - nofDaysLeft - 1;
ELSE
dt.day := dt.day - days;
days := 0;
END;
END;
END;
ASSERT(ValidDateTime(dt));
END AddDays;
PROCEDURE AddHours*(VAR dt : DateTime; hours : LONGINT);
VAR days : LONGINT;
BEGIN
ASSERT(ValidDateTime(dt));
dt.hour := dt.hour + hours;
days := dt.hour DIV 24;
dt.hour := dt.hour MOD 24;
IF (dt.hour < 0) THEN
dt.hour := dt.hour + 24;
DEC(days);
END;
IF (days # 0) THEN AddDays(dt, days); END;
ASSERT(ValidDateTime(dt));
END AddHours;
PROCEDURE AddMinutes*(VAR dt : DateTime; minutes : LONGINT);
VAR hours : LONGINT;
BEGIN
ASSERT(ValidDateTime(dt));
dt.minute := dt.minute + minutes;
hours := dt.minute DIV 60;
dt.minute := dt.minute MOD 60;
IF (dt.minute < 0) THEN
dt.minute := dt.minute + 60;
DEC(hours);
END;
IF (hours # 0) THEN AddHours(dt, hours); END;
ASSERT(ValidDateTime(dt));
END AddMinutes;
PROCEDURE AddSeconds*(VAR dt : DateTime; seconds : LONGINT);
VAR minutes : LONGINT;
BEGIN
ASSERT(ValidDateTime(dt));
dt.second := dt.second + seconds;
minutes := dt.second DIV 60;
dt.second := dt.second MOD 60;
IF (dt.second < 0) THEN
dt.second := dt.second + 60;
DEC(minutes);
END;
IF (minutes # 0) THEN AddMinutes(dt, minutes); END;
ASSERT(ValidDateTime(dt));
END AddSeconds;
BEGIN
Months[0] := "January"; Months[1] := "February"; Months[2] := "March"; Months[3] := "April"; Months[4] := "May";
Months[5] := "June"; Months[6] := "July"; Months[7] := "August"; Months[8] := "September";
Months[9] := "October"; Months[10] := "November"; Months[11] := "December";
Days[0] := "Monday"; Days[1] := "Tuesday"; Days[2] := "Wednesday"; Days[3] := "Thursday";
Days[4] := "Friday"; Days[5] := "Saturday"; Days[6] := "Sunday";
NoDays[0] := 31; NoDays[1] := 28; NoDays[2] := 31; NoDays[3] := 30; NoDays[4] := 31; NoDays[5] := 30;
NoDays[6] := 31; NoDays[7] := 31; NoDays[8] := 30; NoDays[9] := 31; NoDays[10] := 30; NoDays[11] := 31;
END Dates.