-- TimeConvert.Mesa Edited by Johnsson on 29-Jul-80 9:25:55
-- Copyright Xerox Corporation 1979, 1980
DIRECTORY
InlineDefs USING [BcplLongNumber, DIVMOD, LDIVMOD, LongNumber],
StringDefs USING [AppendChar, AppendString],
Time USING [],
TimeDefs USING [
BaseYear, currentParameters, currentTime, DaysInFourYears, DefaultTime,
PackedTime, StartWeekDay, UnpackedTime, WestEast];
TimeConvert: PROGRAM
IMPORTS InlineDefs, StringDefs EXPORTS TimeDefs, Time SHARES TimeDefs =
BEGIN OPEN TimeDefs;
UP: TYPE = POINTER TO UnpackedTime;
Number: TYPE = InlineDefs.LongNumber;
DivideTime: PROCEDURE [num: Number, den: CARDINAL]
RETURNS [quotient: PackedTime, remainder: CARDINAL] =
BEGIN OPEN InlineDefs;
q: Number;
t: CARDINAL;
[q.highbits, t] ← LDIVMOD[num.highbits, 0, den];
[q.lowbits, remainder] ← LDIVMOD[num.lowbits, t, den];
RETURN[quotient: q.lc, remainder: remainder]
END;
CurrentDayTime, Current: PUBLIC PROCEDURE RETURNS [PackedTime] =
BEGIN
t: InlineDefs.BcplLongNumber ← currentTime↑;
RETURN[LOOPHOLE[Number[num[highbits: t.highbits, lowbits: t.lowbits]]]]
END;
TP: TYPE = RECORD [beginDST, endDST: CARDINAL, zone, zoneminutes: INTEGER];
TimeParameters: PROCEDURE RETURNS [p: TP] =
BEGIN OPEN p;
direction: WestEast;
[beginDST: beginDST, endDST: endDST, zone: zone, zoneminutes: zoneminutes,
direction: direction] ← currentParameters↑;
IF direction # west THEN BEGIN zone ← -zone; zoneminutes ← -zoneminutes END;
RETURN
END;
MonthTable: ARRAY [0..12] OF CARDINAL =
[0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366];
Unpack: PUBLIC PROCEDURE [p: PackedTime ← 0] RETURNS [unpacked: UnpackedTime] =
UnpackDT;
UnpackDT: PUBLIC PROCEDURE [p: PackedTime ← DefaultTime]
RETURNS [unp: UnpackedTime] =
BEGIN
u: UP = @unp;
day4, day, yr4: CARDINAL;
month: CARDINAL ← 1;
t: Number;
parms: TP;
parms ← TimeParameters[];
IF p = DefaultTime THEN p ← CurrentDayTime[];
[p, u.second] ← DivideTime[[lc[p]], 60];
p ← p - parms.zoneminutes; -- ignore underflow
[p, u.minute] ← DivideTime[[lc[p]], 60];
u.zone ← parms.zone;
u.dst ← FALSE;
p ← p - parms.zone; -- ignore underflow
DO
-- have to repeat if DST
[t.lc, u.hour] ← DivideTime[[lc[p]], 24];
u.weekday ← (t.lowbits + StartWeekDay) MOD 7;
[yr4, day4] ← InlineDefs.DIVMOD[t.lowbits, DaysInFourYears];
day4 ←
(IF day4 >= 2*365 + 31 + 28 THEN 3 ELSE (day4 + (365 - 31 - 28))/365) +
day4;
[day4, day] ← InlineDefs.DIVMOD[day4, 366];
u.year ← BaseYear + yr4*4 + day4;
WHILE day >= MonthTable[month] DO month ← month + 1 ENDLOOP;
u.month ← month ← month - 1;
day ← day + 1;
u.day ← day - MonthTable[month];
IF u.dst OR ~CheckDateGE[u, day, parms.beginDST, 2] OR CheckDateGE[
u, day, parms.endDST, 1] THEN EXIT;
p ← p + 1;
u.dst ← TRUE;
ENDLOOP;
RETURN
END;
InvalidTime, Invalid: PUBLIC ERROR = CODE;
Pack: PUBLIC PROCEDURE [UnpackedTime, BOOLEAN] RETURNS [time: PackedTime] =
PackDT;
PackDT: PUBLIC PROCEDURE [unp: UnpackedTime, computeDST: BOOLEAN ← TRUE]
RETURNS [time: PackedTime] =
BEGIN
u: UP = @unp;
year, month, day, day1, hour, minute, second: CARDINAL;
zone: INTEGER;
dst: BOOLEAN;
yr3: [0..3];
t: Number;
tp: TP ← TimeParameters[];
[year: year, month: month, day: day, hour: hour, minute: minute,
second: second, zone: zone, dst: dst] ← u↑;
IF (year ← year - BaseYear) >= 136 OR month >= 12 OR day ~IN [1..31] OR hour
>= 24 OR minute >= 60 OR second >= 60 THEN ERROR InvalidTime;
yr3 ← year MOD 4;
IF day > LOOPHOLE[MonthTable[month + 1] - MonthTable[month], CARDINAL] OR
(month = 1 AND day = 29 AND yr3 # 3) THEN ERROR InvalidTime;
-- compute days this year in day1
day1 ← MonthTable[month] + day;
IF yr3 # 3 THEN
BEGIN
tp.beginDST ← tp.beginDST - 1;
tp.endDST ← tp.endDST - 1;
IF month >= 2 THEN day1 ← day1 - 1;
END;
t ← Number[
num[highbits: 0, lowbits: (year/4)*DaysInFourYears + yr3*365 + day1 - 1]];
u.weekday ← (t.lowbits + TimeDefs.StartWeekDay) MOD 7;
IF computeDST THEN
BEGIN OPEN tp;
IF CheckDateGE[u, day1, beginDST, 2] AND ~CheckDateGE[u, day1, endDST, 2]
THEN zone ← zone - 1
END
ELSE
BEGIN
tp.zone ← zone;
tp.zoneminutes ← 0;
IF dst THEN tp.zone ← tp.zone - 1;
END;
RETURN[((t.lc*24 + hour + tp.zone)*60 + minute + tp.zoneminutes)*60 + second]
END;
Append: PUBLIC PROCEDURE [STRING, UnpackedTime, BOOLEAN] = AppendDayTime;
AppendDayTime: PUBLIC PROCEDURE [
s: STRING, unp: UnpackedTime, zone: BOOLEAN ← FALSE] =
BEGIN
z: INTEGER;
KnownZones: TYPE = [4..10];
zones: PACKED ARRAY KnownZones OF CHARACTER ← ['A, 'E, 'C, 'M, 'P, 'Y, 'H];
u: UP = @unp;
p: CARDINAL ← s.length;
m: CARDINAL;
w2d: PROCEDURE [v: CARDINAL] =
BEGIN
d1, d2: CARDINAL;
[d1, d2] ← InlineDefs.DIVMOD[v, 10];
IF d1 # 0 THEN s[p] ← '0 + d1;
s[p + 1] ← '0 + d2;
p ← p + 3;
END;
StringDefs.AppendString[s, " 0-xxx-00 0:00:00"L];
w2d[u.day];
m ← u.month*3;
THROUGH [0..2] DO
s[p] ← ("JanFebMarAprMayJunJulAugSepOctNovDec"L)[m];
p ← p + 1;
m ← m + 1;
ENDLOOP;
p ← p + 1;
w2d[u.year MOD 100];
w2d[u.hour];
w2d[u.minute];
w2d[u.second];
IF zone AND (z ← unp.zone) IN KnownZones THEN
BEGIN OPEN StringDefs;
c: CHARACTER ← IF unp.dst THEN 'D ELSE 'S;
AppendChar[s, ' ];
AppendChar[s, zones[z]];
AppendChar[s, c];
AppendChar[s, 'T];
END;
RETURN
END;
CheckDateGE: PROCEDURE [u: UP, days, dstDay, dstHour: INTEGER]
RETURNS [BOOLEAN] =
BEGIN
weekday: INTEGER ← u.weekday;
RETURN[
IF days < dstDay - 6 THEN FALSE
ELSE
IF days > dstDay THEN TRUE
ELSE
IF weekday = 6 THEN u.hour >= dstHour ELSE days - weekday > dstDay - 6]
END;
END..