(*
    Title:	TimeDate - Implementation
    LastEdit:	"Mon Oct  1 16:20:07 1984"
    Author:	Mick Jordan
		Acorn Computers Ltd

    16032 Panos version
*)

IMPLEMENTATION MODULE TimeDate;


IMPORT TimeAndDate;


MODULE SystemTime;
(*  this module only produces valid times after Jan 1 1904 *)


  IMPORT TimeAndDate;
  EXPORT TIME, GetTime;


  TYPE TIME =
    RECORD
      time, date: CARDINAL;
    END;


  CONST low=0; high=1;
    secondsperminute = 60;
    minutesperday = 1440;
    ticksperday = 33750;
    daysPer4Years = 1461;
    daysInLeapYear = 366;
    daysInYear = 365;


  VAR dmonth: ARRAY [1 .. 12] OF CARDINAL;


  PROCEDURE GetTime(VAR tim : TIME);

    VAR ticks, daysSince1904, secsToday, minsToday: CARDINAL;
      panosTime: TimeAndDate.TimeStamp;
      yr, mth, dy: CARDINAL;
      r: INTEGER;

    BEGIN
      r := TimeAndDate.BinaryTime(panosTime);
      IF r >= 0 THEN

	(* get high order 32 bits *)
	ticks := (panosTime.LS DIV 256) + (panosTime.MS*65536*256);
	secsToday := (((ticks MOD ticksperday)*256)
	    + (panosTime.LS MOD 256)) DIV 100;
	minsToday := secsToday DIV secondsperminute;   (* minutes *)

	(* 1900 was not a leap year, so avoid it *)
	IF (ticks DIV ticksperday) < daysPer4Years THEN
	  daysSince1904 := 0;
	ELSE daysSince1904 := (ticks DIV ticksperday) - daysPer4Years + 1;
	END;
	dy := daysSince1904+1;	(* from a base of 1 *)

	(*  Groups of 4 years - 3 ordinary + 1 leap.*)
	yr := 4 + 4 * (daysSince1904 DIV daysPer4Years);
	dy := dy MOD daysPer4Years;

	(*  Single years. *)
	LOOP
	  IF (yr MOD 4) = 0 THEN
	    IF dy > daysInLeapYear THEN
	      dy := dy - daysInLeapYear;
	    ELSE EXIT
	    END
	  ELSE
	    IF dy > daysInYear THEN
	      dy := dy - daysInYear;
	    ELSE EXIT
	    END
	  END;
	  yr := yr + 1
	END;

	(*  Months. *)
	mth := 1;
	LOOP
	  IF mth = 2 THEN
	    IF (yr MOD 4) = 0 THEN  
	      (* Leap February. *)
	      IF dy > 29 THEN
		dy := dy - 29
	      ELSE EXIT
	      END
	    ELSE
	      IF dy > 28 THEN
		dy := dy - 28
	      ELSE EXIT
	      END
	    END
	  ELSE
	    IF dy > dmonth[mth] THEN
	      dy := dy - dmonth[mth]
	    ELSE EXIT
	    END
	  END;
	  mth := mth + 1
	END;

	WITH tim DO
	  time := ((minsToday DIV 60) * 64 + (minsToday MOD 60)) * 64 +
			   (secsToday MOD 60);
	  date := ((yr * 16) + mth) * 32 + dy;
	END;

      ELSE tim.time := 0; tim.date := 0;
      END;
    END GetTime;


  BEGIN
    dmonth[1] := 31;	dmonth[2] := 28;    dmonth[3] := 31;
    dmonth[4] := 30;	dmonth[5] := 31;    dmonth[6] := 30;
    dmonth[7] := 31;	dmonth[8] := 31;    dmonth[9] := 30;
    dmonth[10] := 31;	dmonth[11] := 30;   dmonth[12] := 31
  END SystemTime;


(* -----------------portable exported procedures of TimeDate -------------- *)

VAR monthstrings: ARRAY [0 .. 12*3] OF CHAR;


PROCEDURE Time(): CARDINAL;

  VAR t: TIME;

  BEGIN
    GetTime(t);
    RETURN t.time
  END Time;


PROCEDURE Date(): CARDINAL;

  VAR t: TIME;

  BEGIN
    GetTime(t);
    RETURN t.date;
  END Date;


PROCEDURE Plant2(VAR v: ARRAY OF CHAR; i: CARDINAL; val: CARDINAL);

  VAR f: CARDINAL;

  BEGIN
    IF val>9 THEN
      f := val DIV 10;
      val := val MOD 10;
    ELSE f := 0;
    END;
    v[i] := CHAR(ORD('0') + f);
    v[i+1] := CHAR(ORD('0') + val);
  END Plant2;


PROCEDURE TimeString(time: CARDINAL; VAR ts: ARRAY OF CHAR);
  BEGIN
    ts[2] := ':'; ts[5] := ':'; ts[8] := 0C;
    Plant2(ts, 0, time DIV (64*64));
    Plant2(ts, 3, (time DIV 64) MOD 64);
    Plant2(ts, 6, time MOD 64);
  END TimeString;


PROCEDURE DateString(date: CARDINAL; VAR ds: ARRAY OF CHAR);

  VAR mx, i: CARDINAL;

  BEGIN
    mx :=(((date DIV 32) MOD 16) - 1)*3;
    ds[2] := '-'; ds[6] := '-'; ds[9] := 0C;
    Plant2(ds, 0, date MOD 32);
    FOR i := 0 TO 2 DO
      ds[3+i] := monthstrings[mx+i];
    END (* for *);
    Plant2(ds, 7, (date DIV (32*16)) MOD 128);
  END DateString;


BEGIN
  monthstrings := "JanFebMarAprMayJunJulAugSepOctNovDec";
END TimeDate.
