DEFINITION MODULE MyClock;

        (********************************************************)
        (*                                                      *)
        (*       Conversion of date and time to string          *)
        (*                                                      *)
        (*  Programmer:         P. Moylan                       *)
        (*  Started:            26 June 1998                    *)
        (*  Last edited:        14 April 2025                   *)
        (*  Status:             OK                              *)
        (*                                                      *)
        (*  The reason for the multiplicity of conversion       *)
        (*  functions all doing approximately the same job      *)
        (*  is that different standards require different       *)
        (*  formats.  The recent tendency is to converge on     *)
        (*  the format used by CurrentDateAndTimeGMT (see       *)
        (*  below), but we still have to support older          *)
        (*  specifications.                                     *)
        (*                                                      *)
        (********************************************************)

(************************************************************************)
(*                                                                      *)
(*  NOTE: several of these procedures refer to a packed date/time       *)
(*  format as used by the file system.  In this packed code, the date   *)
(*  is encoded into 16 bits as                                          *)
(*          yyyyyyymmmmddddd                                            *)
(*  and the time is encoded into 16 bits as                             *)
(*          hhhhhmmmmmmsssss                                            *)
(* where all fields have their obvious meaning except that              *)
(*    -- with only 7 bits available to encode the year, we actually     *)
(*       store (year-1980) as an unsigned number that will overflow     *)
(*       in 2108. That's long enough for our present purposes.          *)
(*    -- in the seconds field we store (seconds/2), thus achieving      *)
(*       a 2-second resolution.                                         *)
(*                                                                      *)
(************************************************************************)

IMPORT SysClock;


PROCEDURE UnpackDateTime (date, time: CARDINAL): SysClock.DateTime;

    (* Converts packed date/time to a DateTime record. *)

PROCEDURE CorrectToGMT (VAR (*INOUT*) date: SysClock.DateTime);

    (* Adjusts a local time to the equivalent UTC. *)

PROCEDURE StringToPackedDateTime (str: ARRAY OF CHAR;  pos: CARDINAL;  UTC: BOOLEAN;
                                  VAR (*OUT*) date, time: CARDINAL): BOOLEAN;

    (* Takes a 14-character string YYYYMMDDHHMMSS at str[pos] and       *)
    (* converts it to a date and time in the packed format used by the  *)
    (* file system.  The date/time are assumed to be UTC if UTC, and    *)
    (* local time otherwise.  Returns FALSE on a format error.          *)

PROCEDURE DTFormatCorrect (str, format: ARRAY OF CHAR): BOOLEAN;

    (* Returns TRUE iff the input string str is consistent with the     *)
    (* format string.  We don't do really strict checking, just enough  *)
    (* to rule out the badly formatted cases.                           *)

PROCEDURE StringToDateTime (str, format: ARRAY OF CHAR): SysClock.DateTime;

    (* The input string str must be consistent with the format string,  *)
    (* as defined in procedure FormatDateTime, but we just use a best   *)
    (* guess if there is a mismatch.                                    *)
    (* Special case: the letters 'GMT' may appear in str matching 'zz'  *)
    (* or 'zzzz' or 'zz:zz' in the format string.                       *)

PROCEDURE PackedDateTimeToString (date, time: CARDINAL;
                                  VAR (*OUT*) result: ARRAY OF CHAR);

    (* The date and time parameters are in the packed format used by the *)
    (* file system.  We convert them to a date/time string in the format *)
    (*         01-Jan-2000 00:00:00 +1000                                *)
    (* The result array must have room for at least 26 characters.       *)

PROCEDURE PackedDateTimeToGMT (date, time: CARDINAL;
                                  VAR (*OUT*) result: ARRAY OF CHAR);

    (* The date and time parameters are in the packed format used by the *)
    (* file system.  We convert them to a date/time string in the format *)
    (*        Sat, 04 Apr 2015 16:00:19 GMT"                            *)
    (* This is the format preferred for internet applications (RFC1123).*)
    (* The result array must have room for at least 29 characters.      *)

PROCEDURE OurTimezone (VAR (*OUT*) result: ARRAY OF CHAR);

    (* Encodes our time zone in the format +10:00                   *)
    (* The result array must have room for at least 6 characters.   *)
    (* Returns empty string if time zone is undefined.              *)

PROCEDURE FormatPackedDateTime (date, time: CARDINAL; format: ARRAY OF CHAR;
                                    VAR (*OUT*) result: ARRAY OF CHAR);

    (* Packed date/time to string. See format codes below.  *)

PROCEDURE FormatDateTime (Time: SysClock.DateTime;  format: ARRAY OF CHAR;
                            GMT: BOOLEAN;  VAR (*OUT*) result: ARRAY OF CHAR);

    (* Puts the current date and time into result according to the      *)
    (* given format specification.  The format codes are:               *)
    (*      dd          day, numeric                                    *)
    (*      ddd         day, three-letter name                          *)
    (*      mm          month, numeric                                  *)
    (*      mmm         month, three-letter name                        *)
    (*      yy          year, including Y2K bug                         *)
    (*      yyyy        year, all four digits                           *)
    (*      HH          hours                                           *)
    (*      MM          minutes                                         *)
    (*      SS          seconds                                         *)
    (*      zz          first time: sign and hours of time zone         *)
    (*      zz          second time: minutes part of time zone          *)
    (* Time is in GMT if GMT=TRUE; local time otherwise.                *)

PROCEDURE FormatDateTime12hr (Time: SysClock.DateTime;  format: ARRAY OF CHAR;
                            GMT: BOOLEAN;  VAR (*OUT*) result: ARRAY OF CHAR);

    (* Like FormatDateTime, but with a 12-hour clock. There is one      *)
    (* extra format code: 'a' after hours, minutes, or seconds is       *)
    (* translated to a one-character result 'a' or 'p', for AM/PM.      *)

PROCEDURE FormatCurrentDateTime (format: ARRAY OF CHAR;  GMT: BOOLEAN;
                                    VAR (*OUT*) result: ARRAY OF CHAR);

    (* Puts the current date and time into result according to the      *)
    (* given format specification.  The format codes are:               *)
    (*      dd          day, numeric                                    *)
    (*      ddd         day, three-letter name                          *)
    (*      mm          month, numeric                                  *)
    (*      mmm         month, three-letter name                        *)
    (*      yy          year, including Y2K bug                         *)
    (*      yyyy        year, all four digits                           *)
    (*      HH          hours                                           *)
    (*      MM          minutes                                         *)
    (*      SS          seconds                                         *)
    (*      zz          first time: sign and hours of time zone         *)
    (*      zz          second time: minutes part of time zone          *)
    (* Time is in GMT if GMT=TRUE; local time otherwise.                *)

PROCEDURE CurrentDateAndTime (VAR (*OUT*) result: ARRAY OF CHAR);

    (* Encodes the current date and time in the format                  *)
    (*         Sat, 30 Jul 2023 00:00:00 +1000                          *)
    (* The result array must have room for at least 31 characters.      *)
    (* This is the format used in mail headers.                         *)

PROCEDURE CurrentDateAndTimeGMT (VAR (*OUT*) result: ARRAY OF CHAR);

    (* Encodes the current date and time in the format                  *)
    (*        Sat, 04 Apr 2015 16:00:19 GMT                             *)
    (* This is the format preferred for internet applications (RFC1123).*)
    (* The result array must have room for at least 29 characters.      *)

PROCEDURE CompareDateStrings (VAR (*IN*) A, B: ARRAY OF CHAR): INTEGER;

    (* Both inputs are strings of the form                                      *)
    (*        Sat, 04 Apr 2015 16:00:19 TZ"                                     *)
    (* where the day name is optional, and the optional TZ field is either GMT  *)
    (* or something like -02:30.  (If the field is missing, we assume GMT.)     *)
    (* The result is -1 if A is earlier than B, 0 if they are the same, and     *)
    (* +1 if A is later than B.                                                 *)

PROCEDURE PackedCurrentDateTime(): CARDINAL;

    (* Encodes the current date and time as a 32-bit integer, using     *)
    (* 16 bits for date and 16 bits for time.                           *)

PROCEDURE CurrentTimeToString (VAR (*OUT*) result: ARRAY OF CHAR);

    (* Encodes the current date and time in the format                  *)
    (*        2000-01-26 17:32:10                                       *)
    (* The result array must have room for at least 19 characters.      *)

PROCEDURE AppendDateString (VAR (*INOUT*) result: ARRAY OF CHAR);

    (* Appends yyyymmdd to result, where yyyymmdd is a digit string     *)
    (* (without any punctuation) representing the current date.         *)

PROCEDURE AppendDateTimeString (VAR (*INOUT*) result: ARRAY OF CHAR);

    (* Appends yyyymmddhhmm to result, where yyyymmddhhmm is a digit string     *)
    (* (without any punctuation) representing the current date and time.        *)

PROCEDURE AppendTimeString (VAR (*INOUT*) result: ARRAY OF CHAR);

    (* Appends a 10-digit numeric string to result.  The number is time-based,  *)
    (* but we are less concerned with its exact value that with having a        *)
    (* high probability that the value will be different on each call.          *)

PROCEDURE AppendSyslogDateTimeString (VAR (*INOUT*) result: ARRAY OF CHAR);

    (* Appends Mmm dd hh:mm:ss to result (where Mmm is a month abbreviation and *)
    (* the other fields are numeric) representing the current date and time.    *)

END MyClock.

