Decoding unix time

Now that I can read unix time with the newly made unix module I also need a way to make it accessible for humans. The unix time is a rather simple time. It is the amount of seconds elapsed as of January 1, 1970 (an event referred to as 'the epoch'). Until 3600 that number had some meaning to humans but the current datenumber is completely meaningless. See also https://www.epochconverter.com/
What is the time today? Unix says 1582062091 or, in human speak, January 18,300, 1970. C has an asctime function but I haven't got a clue how to access it yet. So in the meantime I made an epoch translator that turns 30 bits of date into a human readable date.

The algorithm

The number may be huge but it is also simple:

Getting from days to a year/month/day is some work but not a problem for a computer Below is the years algorithm:
WHILE  days > (365 + Leap (year))  DO
  days := days - 365 - Leap (year);
  INC (year)
END;
   
The leap year rules are rather complicated: If a year is divisible by 4 it is a leap year, unless it is a century, unless unless it is a quadruple century (divisible by 400). Now that could be a very interesting formula (as used by most C sources) but let's just take a look at the important dates.

The epoch was in 1970. The first century break was the year 2000. But since 2000 is a multiple of 400, it IS a simple leap year. The next one is 2100. This would not be a leap year. But the chances of any one of us getting so old are zero. Just ignore that date. So the only rule for us is 'divisible by 4'. Period.

Getting from days to months is not too complex with some cheating. First create an array with for each months the days between January 1 and that month. Then compare the day count with the elements in that array. Done.

The epoch program

Below is the source of the program. It has a silly name, just ignore it.

MODULE marchd;

(*   Create March Dates where start of year = March 1 and end of year is March 365 (or 366)	*)

IMPORT In, Out;

CONST	Daysecs		= 24 * 3600;

VAR	year, month, day, days,
	minute, hour, seconds,
	secs2d, i		: INTEGER;
	ytod			: ARRAY 14 OF INTEGER;		(* Year to day	*)
	Done			: BOOLEAN;


PROCEDURE  Leap (yr : INTEGER) : INTEGER;

BEGIN
  IF  year MOD 4 = 0  THEN  RETURN 1  END;
  RETURN 0
END Leap;


PROCEDURE Init;

BEGIN
  ytod [1]  := 0;               ytod [2]  := 31;                ytod [3]  := 60;
  ytod [4]  := 91;              ytod [5]  := 121;               ytod [6]  := 152;
  ytod [7]  := 182;             ytod [8]  := 213;               ytod [9]  := 244;
  ytod [10] := 274;             ytod [11] := 305;               ytod [12] := 335	
END Init;


BEGIN
  Init;
  year := 1970;
  In.Int (seconds);
  days := seconds DIV Daysecs;
  secs2d := seconds MOD Daysecs;
  hour := secs2d DIV 3600;
  minute := (secs2d MOD 3600) DIV 60;
  
  WHILE  days > (365 + Leap (year))  DO
    days := days - 365 - Leap (year);
    INC (year)
  END;
  
  Done := FALSE;
  i := 12;
  IF  (Leap (year) = 1) & (days > 59)  THEN  INC (days)  END;  
  WHILE  ~Done  DO
    IF  days > ytod [i]  THEN
      month := i;
      day := days - ytod [i];
      Done := TRUE
    ELSE
      DEC (i)
    END
  END;
    
  Out.String ("Date is ");
  Out.Int (year, 5);
  Out.Int (month, 4);
  Out.Int (day, 4);
  Out.Int (hour, 4);
  Out.Int (minute, 4);		Out.Ln
END marchd.
   
I guess this program is not too difficult to comprehend. The only special line is
IF  (Leap (year) = 1) & (days > 59)  THEN  INC (days)  END;
When we are in a leapyear AND beyond 28 February, THEN add one day to the daycounter so the rest of the routine needs no adjustment.

Page created 18 Feb 2020,