DIRECTORY BasicTime USING [DayOfWeek, ExtendedGMT, minutesPerHour, MonthOfYear, nullGMT, Pulses, secondsPerMinute, Unpacked, UnpackedPeriod, unspecifiedTP, unspecifiedZone, Zone, ZoneAndDST], BasicTimeBackdoor, HostTime USING [ExtendedGMTFromHostTime, GetTime, GetZoneAndDST, TimeToMicroseconds]; BasicTimeImpl: CEDAR MONITOR IMPORTS HostTime EXPORTS BasicTime, BasicTimeBackdoor = { OPEN BasicTime; knownTP: ZoneAndDST; knownTPValid: BOOL ¬ FALSE; OutOfRange: PUBLIC ERROR = CODE; TimeNotKnown: PUBLIC ERROR = CODE; TimeParametersNotKnown: PUBLIC ERROR = CODE; GetClockPulses: PUBLIC PROC RETURNS [CARD] = TRUSTED { RETURN[HostTime.TimeToMicroseconds[HostTime.GetTime[]]] }; PulsesToMicroseconds: PUBLIC PROC [p: Pulses] RETURNS [CARD] = { RETURN [p]; }; PulsesToSeconds: PUBLIC PROC [p: Pulses] RETURNS [s: REAL] = { RETURN [p*1.0e-6]; }; MicrosecondsToPulses: PUBLIC PROC [m: CARD] RETURNS [Pulses] = { RETURN [m]; }; GMT: PUBLIC TYPE = INT; gmtBaseYear: CARDINAL = 1968; gmtOrigin: GMT = 0; -- representation for midnight, January 1st 1968 GMT alto1968: CARD = -- our time origin in Alto/Pup format -- LONG[ (gmtBaseYear-1901) * 365 + (gmtBaseYear-1901)/4 ] * 24 * 60 * 60; altoExpiry: CARD = LAST[CARD] - alto1968; gmtExpiry: INT = -- intersection of Alto expiry and 31 bits, minus "nullGMT" representation -- CARD[gmtOrigin] + (IF altoExpiry > CARD[LAST[INT]] THEN LAST[INT] - gmtOrigin - 1 ELSE MIN[altoExpiry, CARD[LAST[INT]] - CARD[gmtOrigin] - 1]) - 1; earliestGMT: PUBLIC GMT ¬ gmtOrigin + LONG[LAST[Zone]] * 60; latestGMT: PUBLIC GMT ¬ gmtExpiry; nullGMT: GMT ~ BasicTime.nullGMT; Now: PUBLIC PROC RETURNS [gmt: GMT] = TRUSTED { gmt ¬ HostTime.ExtendedGMTFromHostTime[HostTime.GetTime[]].gmt; }; ExtendedNow: PUBLIC PROC RETURNS [egmt: ExtendedGMT] = TRUSTED { egmt ¬ HostTime.ExtendedGMTFromHostTime[HostTime.GetTime[]]; }; Period: PUBLIC PROC [from, to: GMT] RETURNS [INT] = { RETURN [to-from]; }; Update: PUBLIC PROC [base: GMT, period: INT] RETURNS [GMT] = { IF period IN [gmtOrigin-base .. gmtExpiry-base] THEN RETURN [base+period] ELSE ERROR OutOfRange; }; ToPupTime: PUBLIC PROC [g: GMT] RETURNS [ CARD ] = { RETURN [alto1968+CARD[g-gmtOrigin]]; }; ToNSTime: PUBLIC PROC [g: GMT] RETURNS [ CARD ] = { RETURN [alto1968+CARD[g-gmtOrigin]]; }; FromPupTime: PUBLIC PROC [p: CARD] RETURNS [ GMT ] = { IF p >= alto1968 THEN RETURN [(p-alto1968)+gmtOrigin] ELSE ERROR OutOfRange; }; FromNSTime: PUBLIC PROC [p: CARD] RETURNS [ GMT ] = { IF p >= alto1968 THEN RETURN [(p-alto1968)+gmtOrigin] ELSE ERROR OutOfRange; }; monthTable: ARRAY MonthOfYear OF CARDINAL = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]; startWeekday: DayOfWeek = Monday; -- January 1st 1968 was a Monday UnpackZ: PUBLIC PROC [time: GMT, tp: ZoneAndDST] RETURNS [ unpacked: Unpacked ] = { secs, mins, hrs: CARD; IF time < earliestGMT OR time > latestGMT THEN ERROR OutOfRange[]; secs ¬ time - gmtOrigin; mins ¬ secs / 60 - (IF tp.zone = unspecifiedZone THEN 0 ELSE tp.zone); hrs ¬ mins / 60; unpacked.second ¬ secs MOD 60; unpacked.minute ¬ mins MOD 60; unpacked.zone ¬ (IF tp.zone = unspecifiedZone THEN 0 ELSE tp.zone); unpacked.dst ¬ no; DO -- once if not dst, twice if dst daysSinceBase: CARDINAL = hrs / 24; daysInFourYears: CARDINAL = ( 3*365 + 366 ); fourYears: CARDINAL = daysSinceBase / daysInFourYears; daysBeyondFourYears: CARDINAL = daysSinceBase MOD daysInFourYears; pseudoDaysBeyond: CARDINAL = -- corrected as if every year had 366 days daysBeyondFourYears + (MAX[31+29,daysBeyondFourYears]-(31+29))/365; oddYears: [0..4) = pseudoDaysBeyond / 366; dayOfYear: CARDINAL = (pseudoDaysBeyond MOD 366)+1; -- one's origin, 366-day year unpacked.year ¬ gmtBaseYear + 4*fourYears + oddYears; unpacked.weekday ¬ startWeekday; THROUGH [0..daysSinceBase MOD 7) DO unpacked.weekday ¬ SUCC[unpacked.weekday]; IF unpacked.weekday = unspecified THEN unpacked.weekday ¬ FIRST[DayOfWeek]; ENDLOOP; FOR month: MonthOfYear IN MonthOfYear[January..December] DO IF dayOfYear <= monthTable[SUCC[month]] THEN {unpacked.month ¬ month; EXIT}; REPEAT FINISHED => ERROR ENDLOOP; unpacked.day ¬ dayOfYear-monthTable[unpacked.month]; unpacked.hour ¬ hrs MOD 24; { yearStart: CARDINAL = fourYears * daysInFourYears + (IF oddYears = 0 THEN 0 ELSE 366 + (oddYears-1)*365); unpacked.daysThisYear ¬ daysSinceBase - yearStart; unpacked.secondsThisYear ¬ secs - ((LONG[yearStart] * 24) * 60) * 60; }; IF unpacked.dst = yes OR tp.zone = unspecifiedZone OR NOT CheckDateGE[unpacked, tp.beginDST, 2] OR CheckDateGE[unpacked, tp.endDST, 1] THEN EXIT; hrs ¬ hrs+1; unpacked.dst ¬ yes; ENDLOOP; }; Unpack: PUBLIC PROC [time: GMT] RETURNS [Unpacked] = { RETURN [UnpackZ[time, GetZoneAndDST[]]]; }; Pack: PUBLIC PROC [unpacked: Unpacked] RETURNS [ time: GMT ] = { tp: ZoneAndDST = GetZoneAndDST[]; daysInFourYears: CARDINAL = ( 3*365 + 366 ); yearsSinceBase: CARDINAL = IF unpacked.year < gmtBaseYear THEN ERROR OutOfRange ELSE unpacked.year - gmtBaseYear; oddYears: [0..4) = yearsSinceBase MOD 4; day: CARDINAL = -- day number in year -- monthTable[unpacked.month] + unpacked.day - 1--adjust to zero origin-- - -- monthTable assumes all February's have 29 days, so correct for that -- (IF oddYears#0 AND unpacked.month NOT IN [January..February] THEN 1 ELSE 0); daysSinceBase: CARDINAL = (yearsSinceBase/4) * daysInFourYears + oddYears * 365 + day + (IF oddYears # 0 THEN 1 ELSE 0) --leap day--; zone: [-720-60..+721] ¬ IF unpacked.zone # unspecifiedZone THEN unpacked.zone ELSE IF tp.zone # unspecifiedZone THEN tp.zone ELSE ERROR TimeParametersNotKnown[]; SELECT unpacked.dst FROM unspecified => { unpacked.weekday ¬ startWeekday; THROUGH [0..daysSinceBase MOD 7) DO unpacked.weekday ¬ SUCC[unpacked.weekday]; IF unpacked.weekday = unspecified THEN unpacked.weekday ¬ FIRST[DayOfWeek]; ENDLOOP; IF tp.zone = unspecifiedZone THEN ERROR TimeParametersNotKnown[]; IF CheckDateGE[unpacked,tp.beginDST,2] AND ~CheckDateGE[unpacked,tp.endDST,2] THEN zone ¬ zone - 60; }; yes => zone ¬ zone - 60; no => NULL; ENDCASE => ERROR; RETURN [ gmtOrigin + ((LONG[daysSinceBase] * 24 + unpacked.hour) * 60 + unpacked.minute + zone) * 60 + unpacked.second ] }; CheckDateGE: PROC [unpacked: Unpacked, dstDay, dstHour: CARDINAL] RETURNS [BOOL] = { day: CARDINAL ¬ monthTable[unpacked.month] + unpacked.day; SELECT TRUE FROM day < dstDay-6 => RETURN [FALSE]; -- before the interesting week day > dstDay => RETURN [TRUE]; -- after the interesting week unpacked.weekday = Sunday => RETURN [unpacked.hour >= dstHour]; -- critical Sunday ENDCASE => { THROUGH DayOfWeek[FIRST[DayOfWeek]..unpacked.weekday) DO day ¬ day-1; ENDLOOP; -- calculate day number of preceding Sunday RETURN [day > dstDay-6] -- before/after the critical Sunday } }; secondsPerHour: NAT ~ secondsPerMinute*minutesPerHour; UnpackPeriod: PUBLIC PROC [period: INT] RETURNS [UnpackedPeriod] ~ { seconds: CARD ~ ABS[period]; minutes: CARD ~ seconds/secondsPerMinute; hours: CARD ~ minutes/minutesPerHour; RETURN [[ hours: hours, minutes: minutes-hours*minutesPerHour, seconds: seconds-minutes*secondsPerMinute, negative: period<0 ]]; }; PackPeriod: PUBLIC PROC [period: UnpackedPeriod] RETURNS [seconds: INT] ~ { part: CARD ~ period.seconds+period.minutes*secondsPerMinute; IF period.hours<0 THEN ERROR OutOfRange; IF CARD[period.hours]>(CARD[INT.LAST]-part)/secondsPerHour THEN ERROR OutOfRange; seconds ¬ part+period.hours*secondsPerHour; IF period.negative THEN seconds ¬ -seconds; }; GetTPFromHost: INTERNAL PROC RETURNS [ok: BOOL ¬ TRUE] = { knownTP ¬ HostTime.GetZoneAndDST[]; knownTPValid ¬ TRUE; }; SetZoneAndDST: PUBLIC ENTRY PROC [tp: ZoneAndDST] = { ENABLE UNWIND => NULL; knownTP ¬ tp; knownTPValid ¬ TRUE; }; GetZoneAndDST: PUBLIC ENTRY PROC RETURNS [tp: ZoneAndDST] = { ENABLE UNWIND => NULL; IF knownTPValid OR GetTPFromHost[].ok THEN RETURN [knownTP]; RETURN [ BasicTime.unspecifiedTP ]; }; }. ΔBasicTimeImpl.mesa: low-level time facilities Copyright Σ 1985, 1986, 1987, 1991 by Xerox Corporation. All rights reserved. Russ Atkinson (RRA) July 11, 1988 2:43:53 pm PDT Hal Murray, June 3, 1986 3:31:31 pm PDT Doug Wyatt, February 3, 1987 6:15:21 pm PST Carl Hauser, October 27, 1988 1:56:38 pm PDT JKF August 30, 1988 5:15:27 pm PDT Global variables Sampling this need not be locked because it can only change in ONE direction, and that will only happen after knownTP is set correctly. Errors Part I: Fine-grain timer Part II: Packed times Returns the time "period" seconds later than "base" (earlier iff "period" is negative). Raises "OutOfRange" if the result would be before 1968 or after 2036. Returns time according to the Alto/Pup time standard. No errors. Returns time according to the Xerox NS protocol time standard. No errors. Accepts time according to the Alto/Pup time standard. Raises "OutOfRange" for times earlier than 1968. Accepts time according to the Xerox NS protocol time standard. Raises "OutOfRange" for times beyond about 2036. Part III: Packing and unpacking Raises "OutOfRange" for times before 1968 or after 2036. Part IV: Finding and setting time and zone/dst information Κ Ά–(cedarcode) style•NewlineDelimiter ˜headšœ.™.Icodešœ ΟeœC™NJ™0L™'L™+L™,L™"—˜šΟk ˜ Lšœ žœ¦˜΅Lšœ˜Lšœ žœG˜U——L˜šΟn œžœž˜Lšžœ ˜Lšžœ˜$Lšœžœ ˜—™Lšœ˜šœžœžœ˜Lšœ‡™‡L™——šœ™šŸ œžœžœžœ˜ L˜—šŸ œžœžœžœ˜"L˜—šŸœžœžœžœ˜,L˜——™š Ÿœžœžœžœžœžœ˜6Lšžœ1˜7Lšœ˜L˜—š Ÿœžœžœ žœžœ˜@Lšžœ˜ Lšœ˜—L˜š Ÿœžœžœ žœžœ˜>Lšžœ ˜Lšœ˜—L˜š Ÿœžœžœžœžœ ˜@Lšžœ˜ Lšœ˜—L˜—šœ™Lšžœžœžœžœ˜L˜šœ žœ˜L˜—šœ žœΟc4˜HL˜—šœ žœ (˜9LšžœC˜GL˜—šœ žœžœžœ ˜)L˜—šœ žœ M˜^Lšžœžœžœžœžœžœžœžœžœžœ žœžœžœžœ˜–L˜—š œ žœžœžœžœ ˜Lšœž™žšžœžœ#˜/Lšžœžœ˜Lšžœžœ ˜—Lšœ˜L˜—š Ÿ œžœžœžœžœžœ˜4LšœA™ALšžœ žœ˜$Lšœ˜L˜—š Ÿœžœžœžœžœžœ˜3LšœJ™JLšžœ žœ˜$Lšœ˜L˜—š Ÿ œžœžœžœžœžœ˜6Lšœg™gLš žœžœžœžœžœ ˜LLšœ˜L˜—š Ÿ œžœžœžœžœžœ˜5Lšœp™pLš žœžœžœžœžœ ˜LLšœ˜L˜——šœ™L˜šœ žœ žœžœ˜+Lšœ=˜=L˜—šœ"  ˜BL˜—š Ÿœžœžœžœžœ˜SLšœžœ˜Lšžœžœžœžœ˜BLšœ˜šœ˜Lšœžœžœžœ ˜3—Lšœ˜Lšœžœ˜Lšœžœ˜Lšœžœžœžœ ˜CLšœ˜šžœ  ˜#Lšœžœ ˜#Lšœžœ˜,Lšœ žœ#˜6Lšœžœžœ˜Bšœžœ *˜GLšœžœ)˜C—Lšœ*˜*Lšœ žœžœ  ˜QLšœ5˜5Lšœ ˜ Lšžœžœ˜ šžœžœ˜-Lšžœ˜!Lšžœžœ ˜)Lšžœ˜—šžœžœ ž˜;Lšžœžœ žœžœ˜LLšžœžœž˜Lšžœ˜—Lšœ4˜4Lšœžœ˜šœ˜šœ žœ ˜3Lšœžœžœžœ˜5—Lšœ2˜2Lšœ$žœ˜ELšœ˜—Lšžœ˜Lšžœ˜Lšžœžœ&˜,Lšžœ$˜&Lšžœžœ˜ L˜ Lšœ˜Lšžœ˜—Lšœ˜L˜—š Ÿœžœžœžœžœ˜6Lšžœ"˜(L˜L˜—š Ÿœžœžœžœ žœ˜@Lšœ8™8Lšœ!˜!Lšœžœ˜,šœžœžœ˜9Lšžœžœ ˜Lšžœ˜!—Lšœ"žœ˜(šœžœ ˜(Lšœ- ˜FLšœ I˜KLš œžœ žœžœžœžœžœ˜L—šœžœ˜Lšœ$˜$Lšœ˜Lš œžœžœžœ  œ˜/—šœ˜Lšžœ ˜"Lšžœ˜šžœžœ˜!Lšžœ˜ Lšžœžœ˜$——šžœž˜šœ˜Lšœ ˜ šžœžœž˜#Lšœžœ˜*šžœž˜&Lšœžœ ˜$—Lšžœ˜—Lšžœžœžœ˜Ašžœ%žœ#˜MLšžœ˜—Lšœ˜—Lšœ˜Lšœžœ˜ —Lšžœžœ˜Lšžœžœ]˜xLšœ˜L˜—š Ÿ œžœ&žœžœžœ˜TLšœžœ-˜:šžœžœž˜Lšœžœžœ ˜@Lšœžœžœ ˜