DIRECTORY Rope USING [ROPE], System USING [gmtEpoch, SecondsSinceEpoch, GreenwichMeanTime], Time USING [Current] ; Tempus: CEDAR DEFINITIONS IMPORTS Time, System = BEGIN Packed: TYPE = System.GreenwichMeanTime; -- RECORD[LONG CARDINAL] PackedSeconds: TYPE = RECORD[LONG CARDINAL]; Seconds: TYPE = LONG CARDINAL; -- for convenience and readability defaultTime: Packed = System.gmtEpoch; 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 dst: BOOLEAN, weekday: DayOfWeek, secondsThisYear: INT, -- [0..secondsPerYear]. Values > secondsPerYear are outofbounds and give errors when passed to Pack daysThisYear: [0..daysPerYear] ]; 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}; 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]; MakeRope: PROCEDURE [ time: Packed _ defaultTime, precision: Precision _ minutes, includeDayOfWeek: BOOL _ FALSE, useAMPM: BOOL _ TRUE ] RETURNS[value: Rope.ROPE]; 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: INT _ LAST[INT], secondsThisYear: INT _ LAST[INT], daysThisYear: [0..daysPerYear] _ daysPerYear, weekday: DayOfWeek _ unspecified, baseTime: Packed _ defaultTime ] RETURNS [time: Packed, precision: Precision]; -- precision is determined by which arguments were specified. Adjust: PROCEDURE [ years: INT _ LAST[INT], months: INT _ LAST[INT], days: INT _ LAST[INT], hours: INT _ LAST[INT], minutes: INT _ LAST[INT], seconds: INT _ LAST[INT], baseTime: Packed _ defaultTime, precisionOfResult: Precision _ unspecified -- unspecified means result should be precision of smallest specified argument. ] RETURNS [time: Packed, precision: Precision]; Parse: PROCEDURE [rope: Rope.ROPE, baseTime: Packed _ defaultTime, search: BOOL _ TRUE] RETURNS [time: Packed, precision: Precision, start, length: NAT]; Error: ERROR [ec: ErrorCode]; ErrorCode: TYPE = { invalid, overConstrained, tooVague, nothingSpecified, notImplemented }; Unintelligible: ERROR [rope: Rope.ROPE, vicinity: INT, ec: ErrorCode]; END. ÈTempus.mesa (last edited by: Teitelman on: July 2, 1983 8:41 pm) Types 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. 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. -- from here on down is redundant information. It is ignored by Pack. 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. 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 inverse of Unpack, ignores redundant fields. Raises Invalid if given invalid data, e.g. February 30. 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 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 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 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 raised by Pack, SmartPack raised by SmartPack, e.g. daysThisYear = 300 and month = January, etc. 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 raised by SmartPack for things we haven't gotten around to implementing yet. 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. ÊE˜Jšœ@™@J˜šÏk ˜ Jšœœœ˜Jšœœ2˜>Jšœœ ˜Jšœ˜J˜—J˜JšÐblœœ œœ˜0Jš˜head™JšÏnœœÏcÑckr˜Bš Ÿ œœœœœ˜.Ibodyš Ïoœ—¢œ#Ïiœ$¢œ$¢ œ.£œ’™êLš œ^¢œ¢ œ¢œ¢œ™½LšœN™N—J˜Jš Ÿœœœœ "˜BJ˜šŸ œŸœ˜&JšœU£œJ™¢—J˜šŸœœœ˜Jšœ ˜%Jšœ ˜Jšœ ˜2Jšœ˜Jšœ˜Jšœ˜Jšœœ ˆ˜“J™J™EJšœ ˜Jšœ˜Jšœœ c˜yJšœ˜Jšœ˜J™J™ÌJ˜—JšŸœœ˜JšŸœœ˜JšŸ œœ˜JšŸ œœ˜JšŸ œœ˜JšŸ œœ˜JšŸœœ;˜NJšŸ œœ˜JšŸ œœQ˜`JšŸ œœs˜„J˜šŸ œœ?˜NJ™Ž——™ šŸœ œœ˜L™.—šœQ™QLšœ>™>—LšœŸ™Ÿ——J˜šŸœ œ œ*œœœ5œ˜™Lšœú™úLšœ£œâ™Lšœ„œß™çLšœû™û——šœ™JšŸœœ˜šŸ œœ˜J˜šœ˜J™—J˜šœ˜JšœF™F—J˜Jšœ ˜ J˜˜J™~—J˜šœ˜J™M—J˜Jšœ˜—J˜šŸœœ œ œ˜FJšœœá™÷˜J˜———Jšœ˜J˜J˜—…— ¨/µ