Copyright 1996-2003 John D. Polstra.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgment:
* This product includes software developed by John D. Polstra.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
MODULE RCSDate;
IMPORT
Date, Fmt, RCSError, Text, Time, TokScan;
PROCEDURE Compare(a, b: T): [-1..1] =
BEGIN
WITH aLen = Text.Length(a), bLen = Text.Length(b) DO
IF aLen = bLen THEN
RETURN Text.Compare(a, b);
ELSE
IF aLen < bLen THEN RETURN -1 ELSE RETURN 1 END;
END;
END;
END Compare;
PROCEDURE Equal(a, b: T): BOOLEAN =
BEGIN
RETURN Text.Equal(a, b);
END Equal;
PROCEDURE FromTime(t: Time.T): T =
VAR
date := Date.FromTime(t, Date.UTC);
BEGIN
IF 1900 <= date.year AND date.year < 2000 THEN
DEC(date.year, 1900);
END;
RETURN Fmt.Pad(Fmt.Int(date.year), 2, '0')
& "." & Fmt.Pad(Fmt.Int(ORD(date.month)+1), 2, '0')
& "." & Fmt.Pad(Fmt.Int(date.day), 2, '0')
& "." & Fmt.Pad(Fmt.Int(date.hour), 2, '0')
& "." & Fmt.Pad(Fmt.Int(date.minute), 2, '0')
& "." & Fmt.Pad(Fmt.Int(date.second), 2, '0');
END FromTime;
PROCEDURE ToTime(d: T): Time.T
Date.ToTime
is badly broken.
RAISES {RCSError.E} =
CONST
DaysNormal = ARRAY [1..12] OF CARDINAL{ (* To beginning of month. *)
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
};
DaysLeap = ARRAY [1..12] OF CARDINAL{
0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335
};
Epoch = 1970;
EndOfTime = 2100;
FirstLeap = (Epoch + 3) DIV 4 * 4;
VAR
ts: TokScan.T;
year, month, day, hour, minute, second: CARDINAL;
numLeap: CARDINAL; (* Leap years to beginning of specified year. *)
t: CARDINAL;
BEGIN
TRY
ts := TokScan.New(d, SET OF CHAR{'.'});
year := ts.getInt("year");
month := ts.getInt("month");
day := ts.getInt("day");
hour := ts.getInt("hour");
minute := ts.getInt("minute");
second := ts.getInt("second");
ts.getEnd();
IF year < 100 THEN INC(year, 1900) END;
IF NOT (Epoch <= year AND year < EndOfTime) THEN
RAISE RCSError.E("Year " & Fmt.Int(year) & " out of range");
END;
numLeap := (year - FirstLeap + 3) DIV 4;
t := (year - Epoch)*365 + numLeap; (* Days to beginning of year. *)
IF year MOD 4 = 0 THEN
INC(t, DaysLeap[month]);
ELSE
INC(t, DaysNormal[month]);
END;
INC(t, day - 1);
t := ((t*24 + hour)*60 + minute)*60 + second;
RETURN FLOAT(t, Time.T);
EXCEPT TokScan.Error(msg) =>
RAISE RCSError.E(msg);
END;
END ToTime;
PROCEDURE Valid(d: T): BOOLEAN =
CONST
Digits = SET OF CHAR{'0'..'9'};
VAR
length: CARDINAL;
firstDot: CARDINAL;
ts: TokScan.T;
year, month, day, hour, minute, second: CARDINAL;
BEGIN
length := Text.Length(d);
CASE length OF
| 17 => firstDot := 2;
| 19 => firstDot := 4;
ELSE
RETURN FALSE;
END;
FOR pos := 0 TO length-1 DO
WITH ch = Text.GetChar(d, pos) DO
IF pos >= firstDot AND (pos-firstDot) MOD 3 = 0 THEN
IF ch # '.' THEN RETURN FALSE END;
ELSE
IF NOT ch IN Digits THEN RETURN FALSE END;
END;
END;
END;
ts := TokScan.New(d, SET OF CHAR{'.'});
TRY
year := ts.getInt();
month := ts.getInt();
day := ts.getInt();
hour := ts.getInt();
minute := ts.getInt();
second := ts.getInt();
ts.getEnd();
EXCEPT ELSE
RETURN FALSE;
END;
IF length = 17 THEN
IF NOT 70 <= year THEN RETURN FALSE END;
ELSE
IF NOT (2000 <= year AND year < 2100) THEN RETURN FALSE END;
END;
IF NOT (1 <= month AND month <= 12) THEN RETURN FALSE END;
IF NOT (1 <= day AND day <= 31) THEN RETURN FALSE END;
IF NOT (0 <= hour AND hour <= 23) THEN RETURN FALSE END;
IF NOT (0 <= minute AND minute <= 59) THEN RETURN FALSE END;
IF NOT (0 <= second AND second <= 59) THEN RETURN FALSE END;
RETURN TRUE;
END Valid;
BEGIN
END RCSDate.