This program is written as a demo program of elucidative programming in connection to the paper An Elucidative Programming Environment for Scheme intended for the Nordic Workshop on Programmming Language Research, NWPER'2000 in Lillehammer, Norway, June 2000.
As an introductory observation, it is worthwhile to notice that the number of seconds since January 1, 1970 can is returned by the Scheme function current-time (taking no parameters) in some Scheme systems (SCM, for instance). The Scheme system gets this number from the underlying operating system, of course.
As an example, the current time was 948450887 at the moment of when these words were written. Furthermore
(time-decode 948450887) = (2000 1 21 10 34 47)See details about time-decode in the next section. Please be aware of the problems of time zones and daylight saving time, cf. section 3.1.
Scheme is weak with respect to static typing
so let us here give the signature of the function:
time-decode: integer -> (year x month x day x hour x minute x second)
The type year represents an integer greater than or equal to 1970, month is the interval [1..12],
day is [1..31], hour is [0..23], and both minute and second belong to [0..59].
The vector on the right hand side will be represented as a list in our program.
As a matter of terminology, the integer at the left hand side of the signature will be called the second counter. We say that an integer value is T-normalized if it is contained in the interval of type T. Thus, for instance, the integer 25 is not hour-normalized, but 1 is (leaving us with 24 hours which can be changed to one extra day).
In the one extreme we can imagine the conversions to be done via a (presumably complicated) formula which realizes an efficient calculation of the result, using arithmetic operators. In the other extreme we can imagine a simple solution based on successive counting and subtracting of well-defined time intervals such as years (with and without leap days), months (in four variations), days, and hours from the second counter. This would be a less efficient solution, but if reliability and trust are emphasized, this will provide for a transparent and easy to follow solution.
In the solution presented in section 2 we will allow "counting", but for some simple sub-problems we use solutions based on "calculation" with formulas involving simple arithmetic operators.
Now the function years-and-seconds solves the problem. The underlying counting is an iterative process, which we in Scheme handle by a tail-recursive function. This function is called cycle-years. The "cycling" starts from base-year. (cycle-years n y r) maintains the invariant
n + r = Cwhere C is the start value of r. n is increased and r is decreased by the the number of seconds in the actual year y. y is increased by one for each iteration. When the rest r becomes less than the seconds in the actual year, we return y and the rest seconds (as a list of two elements).
It is easy to find the unnormalized number of days, and the normalized hours, minutes, and seconds from r. This is done by quotient and modulo calculations. The function how-many-days-hours-minutes-seconds does that. We first find the (non day-normalized) number of days by dividing the parameter n by seconds-in-a-day (). The remainder, called n-rest-1 () is used to find the hour-normalized number of hours by division of n-rest-1 by seconds-in-an-hour (). Again the remainder, n-rest-2 () is found, and this quantum is used to find the minute-normalized number of minutes (). Finally the number of seconds are found in the last modulo calculation (). We use a sequential name-biding form let* to find the results in a sequential fashion. Still we are entirely within the functional paradigm, of course. The function returns the list of days, hours, minutes, and seconds.
(day-and-month-help n m y c)involves that in year y and month m we are taking
(days-in-month m y)days from c and adding a month to m. The function days-in-month relies on a table of month lengths (for a normal year) called month-length-normal-year. The iteration goes on as long as the rest days, c, is less than the number of days in the actual month m (in the year y).
In the function day-and-month we pass the expression (+ 1 day-count) to the formal day counter parameter c in day-and-month-help. The reason is that one day into January brings us to some point in time at January 2, not January 1. Thus, because c will be the day of the month, c can never be 0. In the tail-recursive call, we know that c > (days-in-month m y), thus ensuring this property recursively.
The easiest counter measure is to add or subtract an hour (3600 seconds) in time-decode just after the parameter n (the second counter) has been passed as parameter.
This concludes the time conversion example.
It would be possible - and natural - to continue the example with the reverse transformation, by calculation of the weekday, and by transformation of time intervals. Most of these can be found in the LAML library called Time, cf. the LAML libraries.