Tempus.mesa (last edited by: Teitelman on: July 2, 1983 8:41 pm)
DIRECTORY
Rope USING [ROPE],
System USING [gmtEpoch, SecondsSinceEpoch, GreenwichMeanTime],
Time USING [Current]
;
Tempus: CEDAR DEFINITIONS IMPORTS Time, System =
BEGIN
Types
Packed: TYPE = System.GreenwichMeanTime; -- RECORD[LONG CARDINAL]
PackedSeconds: TYPE = RECORD[LONG CARDINAL];
Packed represents the time which is t-gmtEpoch seconds after midnight, 1 January 1968, the time chosen as the epoch or beginning of the Pilot time standard. Packed times should be compared directly only for equality; to find which of two Packed times comes first, convert each to PackedSeconds compare the result. However, if t2 is a gmt known to occur after t1, then t2-t1 is the seconds between t1 and t2. If t is a Packed time, then Packed[t+60] is the Packed time one minute after t.
PackedSeconds represents the number of seconds elapsed since the gmtEpoch. To convert between Packed and PackedSeconds, use the procedures PackedToSeconds and SecondsToPacked defined below.
Both Packed and PackedSeconds have associated printprocs for easier debugging.
Seconds: TYPE = LONG CARDINAL; -- for convenience and readability
defaultTime: Packed = System.gmtEpoch;
All procedures that accept a Packed time as an argument treat defaultTime as meaning now, i.e. the current time. Note however that Packed.Current[] # defaultTime.
Unpacked: TYPE = RECORD [
year: [0..2050], -- base year is 1968
month: MonthOfYear, 
day: [0..daysPerMonth], -- first day of month = 1.
hour: [0..hoursPerDay],
minute: [0..minutesPerHour],
second: [0..secondsPerMinute],
zone: INT, -- delta in minutes, e.g. 60 corresponds to one zone east of here. Values > ABS[720] are outofbounds and give errors when passed to Pack
-- from here on down is redundant information. It is ignored by Pack.
dst: BOOLEAN,
weekday: DayOfWeek,
secondsThisYear: INT, -- [0..secondsPerYear]. Values > secondsPerYear are outofbounds and give errors when passed to Pack
daysThisYear: [0..daysPerYear]
];
Note that each of the subrange types used in Unpacked have an extra element, in order to provide for an "unspecified" value for the corresponding argument for use with the procedures SmartPack and Adjust.
secondsPerMinute: INT = 60;
minutesPerHour: INT = 60;
hoursPerDay: INT = 24;
daysPerMonth: INT = 31;
monthsPerYear: INT = 12;
daysPerYear: INT = 366;
secondsPerYear: INT = secondsPerMinute*minutesPerHour*hoursPerDay*daysPerYear;
daysPerWeek: INT = 7;
DayOfWeek: TYPE = {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday, unspecified};
MonthOfYear: TYPE = {January, February, March, April, May, June, July, August, September, October, November, December, unspecified};
Precision: TYPE = {years, months, days, hours, minutes, seconds, unspecified};
Describes the precision of the result returned by SmartPack, and Parse, and is used to specify the precision of the result returned by Adjust.
Procedures
Current: PROCEDURE RETURNS [time: Packed] = TRUSTED INLINE {
RETURN[Time.Current[]];
};
PackedToSeconds: PROCEDURE [time: Packed ← defaultTime] RETURNS[PackedSeconds] = INLINE {
RETURN[[System.SecondsSinceEpoch[IF time = defaultTime THEN Current[] ELSE time]]];
};
SecondsToPacked: PROCEDURE [seconds: PackedSeconds] RETURNS[time: Packed] = INLINE {
RETURN[[seconds + System.gmtEpoch]];
};
Unpack: PROCEDURE [time: Packed ← defaultTime] RETURNS [unpacked: Unpacked];
Pack: PROCEDURE [unpacked: Unpacked] RETURNS [time: Packed];
inverse of Unpack, ignores redundant fields. Raises Invalid if given invalid data, e.g. February 30.
MakeRope: PROCEDURE [
time: Packed ← defaultTime,
precision: Precision ← minutes,
includeDayOfWeek: BOOLFALSE,
useAMPM: BOOLTRUE
] RETURNS[value: Rope.ROPE];
defaulting all arguments produces a rope identical to that produced by the Tioga Time command, e.g. April 29, 1983 4:16 pm.
If includeDayOfWeek then the same time would produce Friday, April 29, 1983 4:16 pm.
precision determines how accurately to represent the time, e.g. if precision = days, then the same time would produce April 29, 1983.
If useAMPM is FALSE, then 24 hour notation is used, i.e. the same time would produce April 29, 1983 16:16.
SmartPack: PROCEDURE [
year: [0..2050] ← 0, 
month: MonthOfYear ← unspecified, 
day: [0..daysPerMonth] ← 0,  
hour: [0..hoursPerDay] ← hoursPerDay,
minute: [0..minutesPerHour] ← minutesPerHour,
second: [0..secondsPerMinute] ← secondsPerMinute,
zone: INTLAST[INT],
secondsThisYear: INTLAST[INT],
daysThisYear: [0..daysPerYear] ← daysPerYear,
weekday: DayOfWeek ← unspecified,  
baseTime: Packed ← defaultTime
]
RETURNS [time: Packed, precision: Precision]; -- precision is determined by which arguments were specified.
SmartPack accepts a partial specification of a time and fills in the missing information assuming that the desired time refers to the earliest point after baseTime which satisfies the arguments specified. It is designed to be used to compute times corresponding to English phrases that contain the word "at" or "on". For example:
SmartPack[hour: 11, minute: 30]
means "at 11:30" today, i.e. if it is earlier than 11:30 now, otherwise 11:30 tomorrow. Precision is in minutes.
SmartPack[month: February, day: 16]
means "on February 16", i.e. 12:00AM of next February 16. Precision is in days.
SmartPack[weekDay: Thursday, hour: 16]
means "on Thursday, 4PM", i.e. two days from now, if today is Tuesday, six days from now if today is Friday, and seven days from now if today is Thursday and it is later than 4PM, otherwise, today if today is Thursday and it is earlier than 4pm. Precision is in hours.
Note: if both weekday and day are specified, then they must agree or else Error[overConstrained] is raised.
SmartPack[weekday: Tuesday, baseTime: SmartPack[month: November, day: 1].time]
means first Tuesday after first day in November, i.e. election day
SmartPack[weekday: Thursday, baseTime: SmartPack[month: November, day: 21].time]
means first Thursday after November 21, i.e. ThanksGiving.
Note: if arguments of two different precisions are specified, then values for all of the intervening precisions must also be specified or else an error is raised. For example, SmartPack[year: 1983, day: 14], or SmartPack[month: January, hour: 11] are illegal and raise Error[tooVague].
Adjust: PROCEDURE [
years: INTLAST[INT], 
months: INTLAST[INT], 
days: INTLAST[INT],  
hours: INTLAST[INT],
minutes: INTLAST[INT],
seconds: INTLAST[INT],
baseTime: Packed ← defaultTime,
precisionOfResult: Precision ← unspecified -- unspecified means result should be precision of smallest specified argument.
]
RETURNS [time: Packed, precision: Precision];
Adjust is used for specifying a desired time in terms of a base time and an interval (positive or negative) from that time. It is designed to be used to compute times corresponding to English phrases that contain the word "in". For example
Adjust[hours: 11, minutes: 30]
means in eleven hours and thirty minutes from now.
Adjust[months: 1, days: 3]
means in one month and three days from now.
Note that for many situations, the effect of Adjust can be obtained by simply adding the appropriate seconds to the baseTime. However, "in one day" cannot be computed by simply adding 24 hours because of possibility of changeover to/from daylight saving time. Similarly, "in one month and three days" requires knowing something about how many days in that month. This is the reason for the existence of Adjust.
The precision argument is used to specify the precision of the result. The resulting time is truncated (not rounded) to the corresponding precision. For example, if it is now April 28, 1983 11:20 am, Adjust[months: 1, days: 3] is May 31, 1983 12:00am, i.e. precision = days, whereas Adjust[months: 1, days: 3, precisionOfResult: minutes] is May 31, 1983 11:20am, precision = minutes.
SmartPack[hour: 16, baseTime: Adjust[months: 1, days: 3].time]
means One month and three days from now at 4PM
Adjust[days: -7, hours: 2, baseTime: SmartPack[month: May, weekday: Sunday].time]
last sunday in April, 2AM, i.e. daylight savings time begins.
Note: Adjust will never raise an error. In particular, Adjusting from a longer month to a shorter month is handled by truncating, i.e. one month from May 31 is June 30, one month from January 29, 30 or 31 is the last day in February. Similarly, one year from February 29 is February 28.
Parse: PROCEDURE [rope: Rope.ROPE, baseTime: Packed ← defaultTime, search: BOOLTRUE] RETURNS [time: Packed, precision: Precision, start, length: NAT];
Parse performs the function of DateAndTime.Parse, i.e. parses the input string and returns a GMT time according to the Pilot standard. Parse recognizes a variety of forms, e.g. "April 29, 1983 8:54 pm", "29-Apr-83 20:54:03 PDT", "4/29/83 8 54", etc.
In addition to parsing complete dates, Parse also recognizes a variety of phrases that specify times in conjunction with SmartPack and Adjust, such as "Thursday, 4PM", "in 15 minutes", "Tomorrow at noon", "a month from Tuesday", "a week before May 2", etc.
If the input can't be reasonably interpreted as a date, Parse raises the error Unintelligible with an appropraite error code. If the input contains additional material at the beginning, e.g. "last edited by: Teitelman on: April 26, 1983 2:23 pm", and search = TRUE, then Parse will search until it finds what looks like the beginning of a date. However, once it starts on a date, any errors raised by Pack, SmartPack, or Adjust will be caught and converted into the error Unintelligible.
If Parse is successful, the start and length return values indicates the index of the first character in the date, and the number of characters in the date, i.e. start + length is the index of the first character in r that the parser did not examine.
Errors
Error: ERROR [ec: ErrorCode];
ErrorCode: TYPE = {
invalid,
raised by Pack, SmartPack
overConstrained,
raised by SmartPack, e.g. daysThisYear = 300 and month = January, etc.
tooVague,
nothingSpecified,
in order to be able to distinguish case where rope did not contain a time, from one in which there was a problem with the time
notImplemented
raised by SmartPack for things we haven't gotten around to implementing yet.
};
Unintelligible: ERROR [rope: Rope.ROPE, vicinity: INT, ec: ErrorCode];
If vicinity # -1, THEN vicinity gives the approximate index in the input string where Parse gave up. vicinity = -1 when the parse appeared successful, and then smartpack or adjust raised an error, e.g. Tuesday, May 2 where in fact May 2 is Monday.
END.