<> <> <> <> <> <<>> DIRECTORY Ascii USING [Digit, Letter, Lower], BasicTime USING [daysPerMonth, DayOfWeek, earliestGMT, GMT, minutesPerHour, secondsPerMinute, MonthOfYear, Now, Pack, Unpack, Unpacked, Update], Convert USING [IntFromRope, Error], Rope USING [Fetch, Find, IsEmpty, Length, ROPE, SkipTo, Substr], TimeParse; TimeParseImpl: CEDAR PROGRAM IMPORTS Ascii, BasicTime, Convert, Rope EXPORTS TimeParse = BEGIN OPEN TimeParse; AmPm: TYPE = {am, pm}; shortMonths: ARRAY[0..11] OF Rope.ROPE; months: ARRAY[0..11] OF Rope.ROPE = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; shortWeekdays: ARRAY[0..6] OF Rope.ROPE; otherWeekdays: ARRAY[0..6] OF Rope.ROPE ¬ ALL[""]; weekdays: ARRAY[0..6] OF Rope.ROPE = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]; ParseError: PUBLIC SIGNAL[errorType: ParseErrorType] = CODE; MakeArrays: PROC[] = { FOR i: INT IN [0..11] DO shortMonths[i] ¬ Rope.Substr[months[i], 0, 3]; ENDLOOP; FOR i: INT IN [0..6] DO shortWeekdays[i] ¬ Rope.Substr[weekdays[i], 0, 3]; ENDLOOP; otherWeekdays[1] ¬ "Tues"; otherWeekdays[3] ¬ "Thurs"; }; <> <> <<>> <> << Algorithm used by Parse:>> <<>> <> <> <<>> <> <<>> <> <> <> <> <> <> <<>> Parse: PUBLIC PROC[ str: Rope.ROPE, now: BasicTime.GMT, direction: DirectionType ¬ heuristic, insistTime: BOOLEAN ¬ TRUE, insistDay: BOOLEAN ¬ TRUE] RETURNS [time: BasicTime.GMT, pieces: PiecesType ¬ NIL] = { offset: INT; hr: INT ¬ -1; min: INT ¬ 0; year, tmpYr: INT; month: BasicTime.MonthOfYear; weekday: BasicTime.DayOfWeek; day, wday: INT ¬ -1; past, maybe: BOOLEAN ¬ FALSE; noAmPm: BOOLEAN ¬ FALSE; unp: BasicTime.Unpacked; <> MatchAmPm: PROC [str: Rope.ROPE, k: INT ¬ 0] RETURNS [found: BOOL, which: AmPm, start, stop: INT] = { ln: INT; ch: CHAR; found ¬ FALSE; ln ¬ Rope.Length[str]; WHILE k < Rope.Length[str] DO start ¬ k ¬ Rope.SkipTo[str, k, "aApP"]; IF k < ln-1 AND (ch ¬ Rope.Fetch[str, k+1]) = '. THEN k ¬ k + 1; IF k < ln-1 AND (ch ¬ Rope.Fetch[str, k+1]) = 'm OR ch = 'M THEN { IF (ch ¬ Rope.Fetch[str, start]) = 'a OR ch = 'A THEN which ¬ am ELSE which ¬ pm; found ¬ TRUE; stop ¬ k+1; IF Rope.Fetch[str, start+1] = '. THEN stop ¬ stop + 1; RETURN; }; k ¬ k + 1; ENDLOOP; }; <> SlashFormat: PROC[] RETURNS [ month: BasicTime.MonthOfYear ¬ unspecified, day, yr: INT ¬ -1 ] = { i, j, k, ln: INT; tmpDay, tmpMonth: INT; ln ¬ Rope.Length[str]; FOR i ¬ 0, Rope.Find[str, "/", i] UNTIL i = -1 DO IF i > 0 AND Ascii.Digit[Rope.Fetch[str, i-1]] AND i < ln + 1 AND Ascii.Digit[Rope.Fetch[str, i+1]] THEN { j ¬ i-1; IF j > 0 AND Ascii.Digit[Rope.Fetch[str, j-1]] THEN j ¬ j - 1; IF j > 0 AND AlNum[Rope.Fetch[str, j-1]] THEN {i ¬ i + 1; LOOP;}; k ¬ i + 1; IF k < ln - 1 AND Ascii.Digit[Rope.Fetch[str, k+1]] THEN k ¬ k + 1; IF k < ln-1 AND AlNum[Rope.Fetch[str, k+1]] THEN {i ¬ i + 1; LOOP;}; tmpDay ¬ Convert.IntFromRope[Rope.Substr[str, i+1, k - i]]; tmpMonth ¬ Convert.IntFromRope[Rope.Substr[str, j, i - j]]; IF tmpDay < 1 OR tmpDay > 31 OR tmpMonth < 1 OR tmpMonth > 12 THEN {i ¬ i + 1; LOOP;}; day ¬ tmpDay; month ¬ VAL[CARDINAL[tmpMonth-1]]; -- mm=1 means month is January IF k < ln - 3 AND Rope.Fetch[str, k+1] = '/ AND Ascii.Digit[Rope.Fetch[str, k+2]] AND Ascii.Digit[Rope.Fetch[str, k+3]] THEN { yr ¬ Convert.IntFromRope[Rope.Substr[str, k+2, 2]]; IF yr < 80 OR yr > 99 THEN SIGNAL ParseError[badYearInSlash]; yr ¬ 1900 + yr; k ¬ k + 3; }; pieces ¬ CONS[NEW[PieceType ¬ [j, k-j+1]], pieces]; RETURN; } ELSE i ¬ i + 1; ENDLOOP; }; <<>> <> MatchColonTime: PROC[] RETURNS [hr: INT ¬ -1, min: INT ¬ 0] = { ln, i, j: INT; ok: BOOLEAN; start, stop: INT; which: AmPm; i ¬ 0; ln ¬ Rope.Length[str]; WHILE (i ¬ Rope.Find[str, ":", i]) # -1 DO IF i >= 1 AND Ascii.Digit[Rope.Fetch[str, i-1]] AND i < ln-2 AND Ascii.Digit[Rope.Fetch[str, i+1]] AND Ascii.Digit[Rope.Fetch[str, i+2]] THEN { IF i >= 2 AND Ascii.Digit[Rope.Fetch[str, i-2]] THEN { hr ¬ Convert.IntFromRope[Rope.Substr[str, i-2, 2]]; j ¬ i-2; ln ¬ 5; } ELSE { hr ¬ Convert.IntFromRope[Rope.Substr[str, i-1, 1]]; j ¬ i-1; ln ¬ 4; }; min ¬ Convert.IntFromRope[Rope.Substr[str, i+1, 2]]; pieces ¬ CONS[NEW[PieceType ¬ [j, ln]], pieces]; [ok, which, start, stop] ¬ MatchAmPm[str, i+3 - ln]; IF ok THEN { pieces ¬ CONS[NEW[PieceType ¬ [start, stop - start + 1]], pieces]; IF hr = 12 THEN hr ¬ 0; IF which = pm THEN hr ¬ hr + 12; } ELSE noAmPm ¬ TRUE; }; i ¬ i + 1; ENDLOOP; }; <<>> <> MatchAmPmTime: PROC[] RETURNS [hr: INT ¬ -1] = { ok: BOOLEAN; which: AmPm; i, start, stop: INT; [ok, which, start, stop] ¬ MatchAmPm[str]; IF ok THEN { i ¬ start-1; WHILE i >= 0 AND Rope.Fetch[str,i] = ' DO i ¬ i - 1; ENDLOOP; WHILE i >= 0 AND Ascii.Digit[Rope.Fetch[str,i]] DO i ¬ i - 1; ENDLOOP; hr ¬ Convert.IntFromRope[Rope.Substr[str, i+1, (start-1) - (i+1) + 1] ! Convert.Error => {GOTO done;}]; IF hr >= 24 THEN hr ¬ -1 ELSE IF which = pm THEN hr ¬ hr + 12; --XXX: check to see if hr <= 12? IF hr >= 0 THEN pieces ¬ CONS[NEW[PieceType ¬ [i+1, stop - i]], pieces]; EXITS done => {}; }; }; <> MatchWord: PROC[word: Rope.ROPE] RETURNS [BOOLEAN] = { ln, k: INT; ln ¬ Rope.Length[word]; k ¬ 0; WHILE TRUE DO k ¬ Rope.Find[str, word, k, FALSE]; IF k = -1 THEN RETURN[FALSE]; IF (k = 0 OR ~Ascii.Letter[Rope.Fetch[str, k-1]]) AND (k + ln = Rope.Length[str] OR ~Ascii.Letter[Rope.Fetch[str, k+ln]]) THEN { pieces ¬ CONS[NEW[PieceType ¬ [k, ln]], pieces]; RETURN[TRUE]; }; k ¬ k + 1; ENDLOOP; RETURN[FALSE]; -- this is here to keep compiler happy }; AlNum : PROC [ch: CHAR] RETURNS [BOOL] = INLINE { RETURN [ch IN ['A..'Z] OR ch IN ['a..'z] OR ch IN ['0..'9]]; }; <<>> <> MatchYear: PROC[] RETURNS [year: INT] = { k, ln: INT; year ¬ -1; k ¬ 0; ln ¬ Rope.Length[str]; WHILE TRUE DO k ¬ Rope.Find[str, "19", k, FALSE]; IF k = -1 THEN RETURN; IF (k = 0 OR ~AlNum[Rope.Fetch[str, k-1]]) AND k < ln - 3 AND Ascii.Digit[Rope.Fetch[str, k+2]] AND Ascii.Digit[Rope.Fetch[str, k+3]] AND (k + 4 = ln OR ~Ascii.Letter[Rope.Fetch[str, k+4]]) THEN { year ¬ Convert.IntFromRope[Rope.Substr[str, k, 4]]; pieces ¬ CONS[NEW[PieceType ¬ [k, 4]], pieces]; RETURN; }; k ¬ k + 2; ENDLOOP; RETURN; -- this is here to keep compiler happy }; MatchMonth: PROC[] RETURNS [mnth: BasicTime.MonthOfYear] = { i: CARDINAL; FOR i IN [0..11] DO IF MatchWord[shortMonths[i]] THEN RETURN[VAL[i]]; ENDLOOP; FOR i IN [0..11] DO IF MatchWord[months[i]] THEN RETURN[VAL[i]]; ENDLOOP; RETURN[unspecified]; }; MatchWeekday: PROC[] RETURNS [BasicTime.DayOfWeek] = { i: CARDINAL; FOR i IN [0..6] DO IF MatchWord[shortWeekdays[i]] THEN RETURN[VAL[i]]; ENDLOOP; FOR i IN [0..6] DO IF MatchWord[weekdays[i]] THEN RETURN[VAL[i]]; ENDLOOP; FOR i IN [0..6] DO IF ~Rope.IsEmpty[otherWeekdays[i]] AND MatchWord[otherWeekdays[i]] THEN RETURN[VAL[i]]; ENDLOOP; RETURN[unspecified]; }; <> MatchTime: PROC[] RETURNS [INT] = { k, k1, ln, cnt, val: INT; ch: CHAR; ln ¬ Rope.Length[str]; k ¬ 0; WHILE TRUE DO k1 ¬ k ¬ Rope.SkipTo[str, k, "0123456789"]; cnt ¬ 1; IF k = ln THEN RETURN[-1]; IF k > 0 AND (AlNum[ch ¬ Rope.Fetch[str, k-1]] OR ch = ':) THEN {k ¬ k + 1; LOOP}; IF k < ln-1 AND Ascii.Digit[Rope.Fetch[str, k+1]] THEN {k ¬ k + 1; cnt ¬ 2;}; IF k = ln-1 OR (~AlNum[ch ¬ Rope.Fetch[str, k+1]] AND ch # ':) THEN { val ¬ Convert.IntFromRope[Rope.Substr[str, k1, cnt]]; IF val >= 1 AND val <= 12 THEN { pieces ¬ CONS[NEW[PieceType ¬ [k1, cnt]], pieces]; noAmPm ¬ TRUE; RETURN[val]; }; }; k ¬ k + 1; ENDLOOP; RETURN[-1]; -- this is here to keep compiler happy }; InPieces: PROC[i: INT] RETURNS [BOOLEAN] = { plist: PiecesType; plist ¬ pieces; WHILE plist # NIL DO IF i >= plist.first.start AND i < plist.first.start + plist.first.len THEN RETURN[TRUE]; plist ¬ plist.rest; ENDLOOP; RETURN[FALSE]; }; <<>> <> <> MatchDay: PROC[] RETURNS [INT] = { k, k1, ln, cnt, val: INT; ch: CHAR; b: BOOLEAN ¬ FALSE; <> Th: PROC[k: INT] RETURNS [b: BOOLEAN] = { IF k >= ln-2 THEN RETURN[FALSE]; SELECT Rope.Fetch[str, k] FROM '1 => b ¬ Ascii.Lower[Rope.Fetch[str, k+1]] = 's AND Ascii.Lower[Rope.Fetch[str, k+2]] = 't; '2 => b ¬ Ascii.Lower[Rope.Fetch[str, k+1]] = 'n AND Ascii.Lower[Rope.Fetch[str, k+2]] = 'd; '3 => b ¬ Ascii.Lower[Rope.Fetch[str, k+1]] = 'r AND Ascii.Lower[Rope.Fetch[str, k+2]] = 'd; ENDCASE =>b ¬ Ascii.Lower[Rope.Fetch[str, k+1]] = 't AND Ascii.Lower[Rope.Fetch[str, k+2]] # 'h; RETURN[b AND (k+2 = ln-1 OR ~AlNum[Rope.Fetch[str,k+3]])]; }; ln ¬ Rope.Length[str]; k ¬ 0; WHILE TRUE DO k1 ¬ k ¬ Rope.SkipTo[str, k, "0123456789"]; cnt ¬ 1; IF k = ln THEN RETURN[-1]; IF k > 0 AND (AlNum[ch ¬ Rope.Fetch[str, k-1]] OR ch = ':) THEN {k ¬ k + 1; LOOP}; IF InPieces[k] THEN {k ¬ k + 1; LOOP}; IF k < ln-1 AND Ascii.Digit[Rope.Fetch[str, k+1]] THEN {k ¬ k + 1; cnt ¬ 2;}; IF k = ln-1 OR (~AlNum[ch ¬ Rope.Fetch[str, k+1]] AND ch # ':) OR (b ¬ Th[k]) THEN { val ¬ Convert.IntFromRope[Rope.Substr[str, k1, cnt]]; IF val >= 1 AND val <= 31 THEN { IF b THEN {cnt ¬ cnt + 2;}; pieces ¬ CONS[NEW[PieceType ¬ [k1, cnt]], pieces]; RETURN[val]; }; }; k ¬ k + 1; ENDLOOP; RETURN[-1]; -- this is here to keep compiler happy }; <> MyAdjust: PROC[time: BasicTime.GMT, min, hr: INT, days: INT] RETURNS [BasicTime.GMT] = { unp: BasicTime.Unpacked; unp ¬ BasicTime.Unpack[time]; unp.minute ¬ min; unp.hour ¬ hr; time ¬ BasicTime.Pack[unp]; RETURN[Adjust[days: days, baseTime: time, precisionOfResult: minutes].time]; }; MonthDistance: PROC[a, b: BasicTime.MonthOfYear] RETURNS [k: INT] = { k ¬ 0; WHILE a # b DO <> IF a = December THEN a ¬ FIRST[BasicTime.MonthOfYear] ELSE a ¬ SUCC[a]; k ¬ k + 1; ENDLOOP; }; unp ¬ BasicTime.Unpack[now]; unp.second ¬ 0; unp.dst ¬ unspecified; -- we want it to float to the correct value for the result time <> IF MatchWord["noon"] THEN hr ¬ 12 <> ELSE [hr, min] ¬ MatchColonTime[]; <> IF hr < 0 THEN hr ¬ MatchAmPmTime[]; <> IF hr < 0 AND (hr ¬ MatchTime[]) # -1 THEN maybe ¬ TRUE; IF hr = -1 THEN { IF insistTime THEN SIGNAL ParseError[noTime] ELSE hr ¬ 0; }; <> IF MatchWord["today"] THEN { IF noAmPm AND hr # 0 AND hr < 12 THEN { IF direction # backward AND hr < unp.hour THEN hr ¬ hr + 12 ELSE IF direction = backward AND hr + 12 > unp.hour THEN NULL ELSE IF hr < 8 THEN hr ¬ hr + 12; }; time ¬ MyAdjust[now, min, hr, 0]; RETURN; } ELSE IF MatchWord["yesterday"] THEN { IF noAmPm AND hr # 0 AND hr < 8 THEN hr ¬ hr + 12; time ¬ MyAdjust[now, min, hr, -1]; RETURN; } ELSE IF MatchWord["tomorrow"] THEN { IF noAmPm AND hr # 0 AND hr < 8 THEN hr ¬ hr + 12; time ¬ MyAdjust[now, min, hr, 1]; RETURN; }; <> <<>> year ¬ MatchYear[]; [month, day, tmpYr] ¬ SlashFormat[]; IF month = unspecified THEN month ¬ MatchMonth[]; IF tmpYr # -1 AND year # -1 THEN SIGNAL ParseError[twoYears]; IF year = -1 THEN year ¬ tmpYr; weekday ¬ MatchWeekday[]; IF day = -1 THEN day ¬ MatchDay[]; <> IF day = -1 AND maybe THEN { IF ~insistTime THEN { day ¬ hr; hr ¬ 0; } ELSE { <> IF (month # unspecified OR year # -1) AND insistDay THEN SIGNAL ParseError[noTime]; }; }; <<>> <> <> <> <> <<(unless direction=backward)>> <> <> <> < now ( or < now if direction = backward)>> <> <> <> <> <<(or thismonth -1 if direction=backward)>> <> <> <=11 months past now, assume last month>> <<>> <> BEGIN -- establish scope for EXITS clause IF weekday = unspecified AND day = -1 THEN { IF (month # unspecified OR year # -1) THEN { IF insistDay THEN SIGNAL ParseError[yearOrMonthButNoDay] ELSE day ¬ 1; } ELSE { IF noAmPm AND hr # 0 AND hr < 12 THEN { IF direction # backward AND hr < unp.hour THEN hr ¬ hr + 12 ELSE IF direction = backward AND hr + 12 > unp.hour THEN NULL ELSE IF hr < 8 THEN hr ¬ hr + 12; }; past ¬ 60*hr + min < 60*unp.hour + unp.minute; unp.minute ¬ min; unp.hour ¬ hr; time ¬ BasicTime.Pack[unp]; SELECT direction FROM forward, heuristic => IF past THEN time ¬ Adjust[baseTime: time, days: 1, precisionOfResult: minutes].time; backward => IF ~past THEN time ¬ Adjust[baseTime: time, days: -1, precisionOfResult: minutes].time; ENDCASE => {}; GOTO out; }; }; IF noAmPm THEN { <> IF hr < 8 AND hr # 0 THEN hr ¬ hr + 12; }; IF day = -1 AND weekday # unspecified THEN { IF month # unspecified OR year # -1 THEN SIGNAL ParseError[yearOrMonthButNoDay]; <> BEGIN -- establish scope for GOTO SELECT direction FROM forward, heuristic => IF unp.weekday # weekday OR 60*hr + min < 60*unp.hour + unp.minute THEN offset ¬ 1; backward => IF unp.weekday # weekday OR 60*hr + min > 60*unp.hour + unp.minute THEN offset ¬ -1; ENDCASE => {GOTO nochange;}; WHILE unp.weekday # weekday DO now ¬ Adjust[baseTime: now, days: offset, precisionOfResult: minutes].time; unp ¬ BasicTime.Unpack[now]; ENDLOOP; EXITS nochange => {}; END; unp.minute ¬ min; unp.hour ¬ hr; time ¬ BasicTime.Pack[unp]; GOTO out; }; IF month = unspecified THEN { IF year # -1 THEN SIGNAL ParseError[yearButNoMonth]; past ¬ day < unp.day OR (day = unp.day AND 60*hr + min < 60*unp.hour + unp.minute); offset ¬ 0; SELECT direction FROM forward, heuristic => IF past THEN offset ¬ 1; backward => IF ~past THEN offset ¬ -1; ENDCASE => {}; IF offset = 0 THEN { unp.minute ¬ min; unp.hour ¬ hr; unp.day ¬ day; } ELSE { time ¬ Adjust[baseTime: now, months: offset, precisionOfResult: minutes].time; unp ¬ BasicTime.Unpack[time]; unp.minute ¬ min; unp.hour ¬ hr; unp.day ¬ day; }; time ¬ BasicTime.Pack[unp]; GOTO out; }; IF year = -1 THEN { offset ¬ MonthDistance[month, unp.month]; SELECT direction FROM forward => time ¬ Adjust[baseTime: now, months: 12 - offset, precisionOfResult: minutes].time; heuristic => IF offset <= 1 THEN <> time ¬ Adjust[baseTime: now, months: -offset, precisionOfResult: minutes].time ELSE <> time ¬ Adjust[baseTime: now, months: 12 - offset, precisionOfResult: minutes].time; backward => time ¬ Adjust[baseTime: now, months: -offset, precisionOfResult: minutes].time ENDCASE => {}; unp ¬ BasicTime.Unpack[time]; unp.minute ¬ min; unp.hour ¬ hr; unp.day ¬ day; time ¬ BasicTime.Pack[unp]; GOTO out; }; IF year # -1 THEN { unp.minute ¬ min; unp.hour ¬ hr; unp.day ¬ day; unp.month ¬ month; unp.year ¬ year; time ¬ BasicTime.Pack[unp]; GOTO out; }; EXITS out => {}; END; IF weekday # unspecified THEN { unp ¬ BasicTime.Unpack[time]; IF unp.weekday # weekday THEN SIGNAL ParseError[dayWeekdayMismatch]; }; }; TempusParse: PUBLIC PROC[ rope: Rope.ROPE, baseTime: BasicTime.GMT, search: BOOLEAN ¬ TRUE] RETURNS [time: BasicTime.GMT, precision: Precision, start, length: NAT] = { time ¬ Parse[rope, baseTime].time; start ¬ length ¬ 0; precision ¬ unspecified; }; Adjust: PUBLIC 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: BasicTime.GMT ¬ BasicTime.earliestGMT, precisionOfResult: Precision ¬ unspecified] RETURNS [time: BasicTime.GMT, precision: Precision] = { unpacked: BasicTime.Unpacked ¬ BasicTime.Unpack[IF baseTime = BasicTime.earliestGMT THEN BasicTime.Now[] ELSE baseTime]; IncrementYears: PROC[by: INT ¬ 1] = { unpacked.year ¬ unpacked.year + by; unpacked.day ¬ MIN[unpacked.day, DaysInMonth[]]; -- one year from February 29 is February 28 }; IncrementMonths: PROC [by: INT ¬ 1] = { IF by >= 0 THEN FOR i: INT IN [0..by) DO IF unpacked.month = December THEN {IncrementYears[]; unpacked.month ¬ January} ELSE unpacked.month ¬ SUCC[unpacked.month]; REPEAT FINISHED => unpacked.day ¬ MIN[unpacked.day, DaysInMonth[]]; -- if it is now March 31 and client says increment one month, that is April 30. If it is now January 29, 30 or 31 and client says increment one month, goes to February 28/29. ENDLOOP ELSE FOR i: INT IN [0..-by) DO IF unpacked.month = January THEN {IncrementYears[-1]; unpacked.month ¬ December} ELSE unpacked.month ¬ PRED[unpacked.month]; ENDLOOP; }; DaysInMonth: PROC RETURNS[[0 .. BasicTime.daysPerMonth]] = { RETURN[SELECT unpacked.month FROM September, April, June, November => 30, February => IF unpacked.year MOD 4 = 0 THEN 29 ELSE 28, ENDCASE => 31]; }; IncrementDays: PROC [by: INT ¬ 1] = { daysInMonth: [0 .. BasicTime.daysPerMonth]; daysInMonth ¬ DaysInMonth[]; IF by >= 0 THEN FOR i: INT IN [0..by) DO IF unpacked.day = daysInMonth THEN { IncrementMonths[]; daysInMonth ¬ DaysInMonth[]; unpacked.day ¬ 1; } ELSE unpacked.day ¬ unpacked.day + 1; ENDLOOP ELSE FOR i: INT IN [0..-by) DO IF unpacked.day = 1 THEN { IncrementMonths[-1]; daysInMonth ¬ DaysInMonth[]; unpacked.day ¬ daysInMonth; } ELSE unpacked.day ¬ unpacked.day - 1; ENDLOOP; }; IncrementHours: PROC [by: INT ¬ 1] = { IF by >= 0 THEN FOR i: INT IN [0..by) DO IF unpacked.hour = 23 THEN {IncrementDays[]; unpacked.hour ¬ 0} ELSE unpacked.hour ¬ unpacked.hour + 1; ENDLOOP ELSE FOR i: INT IN [0..-by) DO IF unpacked.hour = 0 THEN {IncrementDays[-1]; unpacked.hour ¬ 23} ELSE unpacked.hour ¬ unpacked.hour - 1; ENDLOOP; }; IncrementMinutes: PROC [by: INT ¬ 1] = { IF by >= 0 THEN FOR i: INT IN [0..by) DO IF unpacked.minute = 59 THEN {IncrementHours[]; unpacked.minute ¬ 0} ELSE unpacked.minute ¬ unpacked.minute + 1; ENDLOOP ELSE FOR i: INT IN [0..-by) DO IF unpacked.minute = 0 THEN {IncrementHours[-1]; unpacked.minute ¬ 59} ELSE unpacked.minute ¬ unpacked.minute - 1; ENDLOOP; }; IncrementSeconds: PROC [by: INT ¬ 1] = { IF by >= 0 THEN FOR i: INT IN [0..by) DO IF unpacked.second = 59 THEN {IncrementMinutes[]; unpacked.second ¬ 0} ELSE unpacked.second ¬ unpacked.second + 1; ENDLOOP ELSE FOR i: INT IN [0..-by) DO IF unpacked.second = 0 THEN {IncrementMinutes[-1]; unpacked.second ¬ 59} ELSE unpacked.second ¬ unpacked.second - 1; ENDLOOP; }; IF years # LAST[INT] THEN {IncrementYears[years]; precision ¬ years}; IF months # LAST[INT] THEN {IncrementMonths[months]; precision ¬ months}; IF days # LAST[INT] THEN {IncrementDays[days]; precision ¬ days}; IF hours # LAST[INT] THEN {-- IncrementHours[hours]; -- precision ¬ hours}; IF minutes # LAST[INT] THEN {-- IncrementMinutes[minutes]; -- precision ¬ minutes}; IF seconds # LAST[INT] THEN {-- IncrementSeconds[seconds]; -- precision ¬ seconds}; IF precisionOfResult = unspecified THEN precisionOfResult ¬ precision; IF precisionOfResult < seconds THEN unpacked.second ¬ 0; IF precisionOfResult < minutes THEN unpacked.minute ¬ 0; IF precisionOfResult < hours THEN unpacked.hour ¬ 0; IF precisionOfResult < days THEN unpacked.day ¬ 1; IF precisionOfResult < months THEN unpacked.month ¬ January; unpacked.dst ¬ BasicTime.Unpack[BasicTime.Pack[unpacked]].dst; --set dst properly before actual packing time ¬ BasicTime.Pack[unpacked]; <<>> <> IF hours # LAST[INT] THEN time ¬ BasicTime.Update[time, (hours * BasicTime.minutesPerHour * BasicTime.secondsPerMinute)]; IF minutes # LAST[INT] THEN time ¬ BasicTime.Update[time, minutes * BasicTime.secondsPerMinute]; IF seconds # LAST[INT] THEN time ¬ BasicTime.Update[time, seconds]; RETURN[time, precisionOfResult]; }; MakeArrays[]; END.