Previous: Business Days, Up: Date Arithmetic

Time zones and daylight saving time are a complicated business. The conversions to and from Julian and Unix-style dates automatically compute the correct time zone and daylight saving adjustment to use, provided they can figure out this information. This section describes Calc's time zone adjustment algorithm in detail, in case you want to do conversions in different time zones or in case Calc's algorithms can't determine the right correction to use.

Adjustments for time zones and daylight saving time are done by
`t U`, `t J`, `t N`, and `t C`, but not by any other
commands. In particular, ‘`<may 1 1991> - <apr 1 1991>`’ evaluates
to exactly 30 days even though there is a daylight-saving
transition in between. This is also true for Julian pure dates:
‘`julian(<may 1 1991>) - julian(<apr 1 1991>)`’. But Julian
and Unix date/times will adjust for daylight saving time: using Calc's
default daylight saving time rule (see the explanation below),
‘`julian(<12am may 1 1991>) - julian(<12am apr 1 1991>)`’
evaluates to ‘`29.95833`’ (that's 29 days and 23 hours)
because one hour was lost when daylight saving commenced on
April 7, 1991.

In brief, the idiom ‘`julian( date1) - julian(date2)`’
computes the actual number of 24-hour periods between two dates, whereas
‘

The `calc-time-zone`

[`tzone`

] command converts the time
zone specified by its numeric prefix argument into a number of
seconds difference from Greenwich mean time (GMT). If the argument
is a number, the result is simply that value multiplied by 3600.
Typical arguments for North America are 5 (Eastern) or 8 (Pacific). If
Daylight Saving time is in effect, one hour should be subtracted from
the normal difference.

If you give a prefix of plain `C-u`, `calc-time-zone`

(like other
date arithmetic commands that include a time zone argument) takes the
zone argument from the top of the stack. (In the case of `t J`
and `t U`, the normal argument is then taken from the second-to-top
stack position.) This allows you to give a non-integer time zone
adjustment. The time-zone argument can also be an HMS form, or
it can be a variable which is a time zone name in upper- or lower-case.
For example ‘`tzone(PST) = tzone(8)`’ and ‘`tzone(pdt) = tzone(7)`’
(for Pacific standard and daylight saving times, respectively).

North American and European time zone names are defined as follows; note that for each time zone there is one name for standard time, another for daylight saving time, and a third for “generalized” time in which the daylight saving adjustment is computed from context.

YST PST MST CST EST AST NST GMT WET MET MEZ 9 8 7 6 5 4 3.5 0 -1 -2 -2 YDT PDT MDT CDT EDT ADT NDT BST WETDST METDST MESZ 8 7 6 5 4 3 2.5 -1 -2 -3 -3 YGT PGT MGT CGT EGT AGT NGT BGT WEGT MEGT MEGZ 9/8 8/7 7/6 6/5 5/4 4/3 3.5/2.5 0/-1 -1/-2 -2/-3 -2/-3

To define time zone names that do not appear in the above table,
you must modify the Lisp variable `math-tzone-names`

. This
is a list of lists describing the different time zone names; its
structure is best explained by an example. The three entries for
Pacific Time look like this:

( ( "PST" 8 0 ) ; Name as an upper-case string, then standard ( "PDT" 8 -1 ) ; adjustment, then daylight saving adjustment. ( "PGT" 8 "PST" "PDT" ) ) ; Generalized time zone.

With no arguments, `calc-time-zone`

or ‘`tzone()`’ will by
default get the time zone and daylight saving information from the
calendar (see Calendar/Diary). To use a different time zone, or if the
calendar does not give the desired result, you can set the Calc variable
`TimeZone`

(which is by default `nil`

) to an appropriate
time zone name. (The easiest way to do this is to edit the
`TimeZone`

variable using Calc's `s T` command, then use the
`s p` (`calc-permanent-variable`

) command to save the value of
`TimeZone`

permanently.)
If the time zone given by `TimeZone`

is a generalized time zone,
e.g., `EGT`

, Calc examines the date being converted to tell whether
to use standard or daylight saving time. But if the current time zone
is explicit, e.g., `EST`

or `EDT`

, then that adjustment is
used exactly and Calc's daylight saving algorithm is not consulted.
The special time zone name `local`

is equivalent to no argument; i.e., it uses the information obtained
from the calendar.

The `t J` and `t U`

commands with no numeric prefix
arguments do the same thing as ‘`tzone()`’; namely, use the
information from the calendar if `TimeZone`

is `nil`

,
otherwise use the time zone given by `TimeZone`

.

When Calc computes the daylight saving information itself (i.e., when
the `TimeZone`

variable is set), it will by default consider
daylight saving time to begin at 2 a.m. on the second Sunday of March
(for years from 2007 on) or on the last Sunday in April (for years
before 2007), and to end at 2 a.m. on the first Sunday of
November. (for years from 2007 on) or the last Sunday in October (for
years before 2007). These are the rules that have been in effect in
much of North America since 1966 and take into account the rule change
that began in 2007. If you are in a country that uses different rules
for computing daylight saving time, you have two choices: Write your own
daylight saving hook, or control time zones explicitly by setting the
`TimeZone`

variable and/or always giving a time-zone argument for
the conversion functions.

The Lisp variable `math-daylight-savings-hook`

holds the
name of a function that is used to compute the daylight saving
adjustment for a given date. The default is
`math-std-daylight-savings`

, which computes an adjustment
(either 0 or *-1*) using the North American rules given above.

The daylight saving hook function is called with four arguments:
The date, as a floating-point number in standard Calc format;
a six-element list of the date decomposed into year, month, day,
hour, minute, and second, respectively; a string which contains
the generalized time zone name in upper-case, e.g., `"WEGT"`

;
and a special adjustment to be applied to the hour value when
converting into a generalized time zone (see below).

The Lisp function `math-prev-weekday-in-month`

is useful for
daylight saving computations. This is an internal version of
the user-level `pwday`

function described in the previous
section. It takes four arguments: The floating-point date value,
the corresponding six-element date list, the day-of-month number,
and the weekday number (0–6).

The default daylight saving hook ignores the time zone name, but a more sophisticated hook could use different algorithms for different time zones. It would also be possible to use different algorithms depending on the year number, but the default hook always uses the algorithm for 1987 and later. Here is a listing of the default daylight saving hook:

(defun math-std-daylight-savings (date dt zone bump) (cond ((< (nth 1 dt) 4) 0) ((= (nth 1 dt) 4) (let ((sunday (math-prev-weekday-in-month date dt 7 0))) (cond ((< (nth 2 dt) sunday) 0) ((= (nth 2 dt) sunday) (if (>= (nth 3 dt) (+ 3 bump)) -1 0)) (t -1)))) ((< (nth 1 dt) 10) -1) ((= (nth 1 dt) 10) (let ((sunday (math-prev-weekday-in-month date dt 31 0))) (cond ((< (nth 2 dt) sunday) -1) ((= (nth 2 dt) sunday) (if (>= (nth 3 dt) (+ 2 bump)) 0 -1)) (t 0)))) (t 0)) )

The `bump`

parameter is equal to zero when Calc is converting
from a date form in a generalized time zone into a GMT date value.
It is *-1* when Calc is converting in the other direction. The
adjustments shown above ensure that the conversion behaves correctly
and reasonably around the 2 a.m. transition in each direction.

There is a “missing” hour between 2 a.m. and 3 a.m. at the
beginning of daylight saving time; converting a date/time form that
falls in this hour results in a time value for the following hour,
from 3 a.m. to 4 a.m. At the end of daylight saving time, the
hour from 1 a.m. to 2 a.m. repeats itself; converting a date/time
form that falls in this hour results in a time value for the first
manifestation of that time (*not* the one that occurs one hour
later).

If `math-daylight-savings-hook`

is `nil`

, then the
daylight saving adjustment is always taken to be zero.

In algebraic formulas, ‘`tzone( zone, date)`’
computes the time zone adjustment for a given zone name at a
given date. The

The ‘`dsadj( date, zone)`’ function computes the
daylight saving adjustment that is appropriate for

`PDT`

or `PST`

) the