-- 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..