It is often important to time operations or to keep track of elapsed time between user responses. This could be done in order to:
- determine the relative efficiency of sorting procedures
- time user responses in a game or learning environment
- calculate the number of elapsed days for interest calculations
- place a time or date "stamp" in a document, such as a letter
Some computing systems have only an elapsed time clock. These only keep track of how much time has passed since the system was started up for the last time. Better (not all) systems have a battery operated real time clock that keeps track of the current year, month, day, hour, minute, and second. They may also have fractions of a second, information on the difference between local time and Universal Time, and on whether daylight savings time is in effect. The details of what information is available will vary from one system to another.
If there is a clock present, it may be possible for a client program to set the clock as well as get information from it. In a UNIX system, this is unlikely, as users are generally not allowed access to system features. On personal computers, the opposite is the rule--the user can do almost anything.
In order to take as many as possible of these variations in effect, ISO Modula-2 mandates the following low-level module for setting and examining the system clock. Note that some items are implementation defined.
DEFINITION MODULE SysClock; (* ========================================= Definition Module from ISO Modula-2 ISO/IEC IS10515 by JTC1/SC22/WG13 Original specification and design of SysClock Copyright © 1990-1991 by R. Sutcliffe Assigned to ISO for standards work Language and Module designs © 1992 - 1995by BSI, D.J. Andrews, B.J. Cornelius, R. B. Henry R. Sutcliffe, D.P. Ward, and M. Woodman =========================================== *) (* Facilities for accessing a system clock that records the date and time of day *) CONST maxSecondParts = 0; (* this number is implementation defined *) TYPE Month = [1 .. 12]; Day = [1 .. 31]; Hour = [0 .. 23]; Min = [0 .. 59]; Sec = [0 .. 59]; Fraction = [0 .. maxSecondParts]; UTCDiff = [-780 .. 720]; DateTime = RECORD year: CARDINAL; month: Month; day: Day; hour: Hour; minute: Min; second: Sec; fractions: Fraction; (* parts of a second *) zone: UTCDiff; (* Time zone differential factor which is the number of minutes to add to local time to obtain UTC. *) summerTimeFlag: BOOLEAN; (* Interpretation of flag depends on local usage. *) END; PROCEDURE CanGetClock (): BOOLEAN; (* Returns TRUE if a system clock can be read; FALSE otherwise *) PROCEDURE CanSetClock (): BOOLEAN; (* Returns TRUE if a system clock can be set; FALSE otherwise *) PROCEDURE IsValidDateTime (userData: DateTime): BOOLEAN; (* Returns TRUE if the value of userData represents a valid date and time; FALSE otherwise *) PROCEDURE GetClock (VAR userData: DateTime); (* If possible, assigns system date and time of day to userData *) PROCEDURE SetClock (userData: DateTime); (* If possible, sets the system clock to the values of userData *) END SysClock.
No other facilities are provided in the ISO standard library, as needs vary from one application and system to another. However, if the clock is available, it is not difficult to write procedures to output the last date and/or time read from the clock.
MODULE TestClock; (* by R. Sutcliffe to illustrate SysClock *) FROM SysClock IMPORT DateTime , CanGetClock, GetClock; FROM STextIO IMPORT WriteString, WriteLn, WriteChar; FROM SWholeIO IMPORT WriteCard; PROCEDURE Pad (number : CARDINAL); BEGIN IF number < 10 THEN WriteChar("0"); END; END Pad; PROCEDURE WriteDateTime (dateTime : DateTime); BEGIN WriteString ("Date: "); WriteCard (dateTime.year, 1); Pad (dateTime.month); WriteCard (dateTime.month, 0); WriteChar (" "); Pad (dateTime.day); WriteCard (dateTime.day, 1); WriteString (" Time: "); Pad (10* dateTime.hour); Pad (dateTime.hour); WriteCard (dateTime.hour, 1); Pad (dateTime.minute); WriteCard (dateTime.minute, 1); WriteString (" : "); Pad (dateTime.second); WriteCard (dateTime.second, 1); END WriteDateTime; VAR dateTime : DateTime; count : CARDINAL; BEGIN IF CanGetClock() THEN FOR count := 1 TO 10 DO GetClock (dateTime); WriteDateTime (dateTime); WriteLn; END; ELSE WriteString ("No clock available"); END; END TestClock.
When this program was run, the following output was produced.
Date: 1996 11 08 Time: 0911 : 05 Date: 1996 11 08 Time: 0911 : 05 Date: 1996 11 08 Time: 0911 : 05 Date: 1996 11 08 Time: 0911 : 05 Date: 1996 11 08 Time: 0911 : 06 Date: 1996 11 08 Time: 0911 : 06 Date: 1996 11 08 Time: 0911 : 06 Date: 1996 11 08 Time: 0911 : 06 Date: 1996 11 08 Time: 0911 : 06 Date: 1996 11 08 Time: 0911 : 06
There are a number of possibilities for expanding the primitive I/O for date and time in this module. Consider the following formats for date input and output:
January 7, 1996 month day year long month name with comma Jan 7 1996 month day year short month name without comma 1/7/96 month day year all numeric with slashes 01/07/1996 month day year numeric, slashes, leading zeros, century 7th Jan 1996 day month year day with suffix, short month name with spaces 1996 01 07 year month day ISO format with century, spaces, leading zeros
These possibilities could be encapsulated in a conversion module in the following way:
TYPE separator = (space, slash, comma); order = (ymd, dmy, mdy); monthNames = (numeric, short, long); Date = RECORD year : CARDINAL; month : Month; day : Day; END; Format = RECORD sep : separator; ord : order; useNames : monthNames; useSuffix : BOOLEAN; END; PROCEDURE FormatDate (date: Date, format : Format; VAR result : ARRAY OF CHAR);
The task of designing and writing a suite of formatting and I/O modules for date and time has been left for the exercises.
The most common arithmetic that might have to be done on dates and times is to find the elapsed time or date (or both) between two events or to add or subtract a period to a time or a date. For instance, one could define a module:
DEFINITION MODULE DateMath; FROM SysClock IMPORT DateTime, Month, Day; TYPE Date = RECORD year : CARDINAL; month : Month; day : Day; END; PROCEDURE IsValid (da : Date) : BOOLEAN; PROCEDURE AssignDateToDateTime (da : Date) : DateTime; PROCEDURE AssignDateTimeToDate (dt : DateTime) : Date; PROCEDURE Inc (VAR da : Date; delta : Date); (* Increase the given date by the year, month and day specified in delta *) PROCEDURE Dec (VAR da : Date; delta : Date); (* Decrease the given date by the year, month and day specified in delta *) PROCEDURE DateSpan (from, to : Date) : Date; (* Increase the given date by the year, month and day specified in delta *) END DateMath.
A very similar module could be written for doing mathematics on times. Doing so, and implementing both, is left as an exercise for the reader.