17.8 The Date and Time

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.

17.8.1 The Module SysClock

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.

17.8.2 Time and Date I/O

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.

17.8.3 Time and Date Arithmetic

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.


Contents