[local-time-devel] [PATCH] Make local-time operate correctly with timezones

Antoni Piotr Oleksicki apoleksicki at o2.pl
Tue Jul 28 12:39:33 UTC 2009


Hi Daniel

We have (together with Maciej Katafiasz) been working on making
local-time work correctly with regard to timezones. The changes are
quite extensive, but it seems to give correct results now in about
every operation we could think of. That includes overflowing into or
from DST. To achieve that we had to add timezone/offset arguments to
several functions. The biggest changes are in the ADJUST-TIMESTAMP
helper functions (%OFFSET-TIMESTAMP-PATY and %SET-TIMESTAMP-PART).
Additionally we corrected the rather schizophrenic nature of
WITH-DECODED-TIMESTAMP, which took timezone but not offset, unlike
everything else.

The changes are API compatible, so there shouldn't be any breakage to
existing code. The semantics did change, but as far as we can tell the
old semantics were buggy, so it's unlikely that any client would want
them this way. Note that the changes include the patch from Thomas
Munro to ENCODE-TIMESTAMP, together with the DST ambiguity. That
ambiguity carries over to ADJUST-TIMESTAMP when used with symbolic
arguments (month and year) but not the precise ones i.e. nsec, sec,
day, hour, minute. If you don't like the ambiguity and have a good
idea how to solve it, we'll be glad to implement that. But personally
we think it's fine simply to document that explicitly.

Below is a simple test suite that fails in the upstream version but
works fine with our changes. It's by no means exhaustive, but it does
touch most of the interesting cases.

===============================================================
 (in-package :local-time)

(reread-timezone-repository)
(defvar *my-timezone* (find-timezone-by-location-name "Europe/Copenhagen"))

(let ((*default-timezone* *my-timezone*))
 (defvar *timestamp1* (encode-timestamp 0 0 0 12 15 6 2009))
 (defvar *timestamp2* (encode-timestamp 0 0 0  2 25 6 2009)))

;;; Version after changes
(defun check-timestamp (timestamp nsec sec minute hour day month year)
  (with-decoded-timestamp (:nsec nsec* :sec sec* :minute minute* :hour hour*
                           :day day* :month month* :year year*
:timezone *my-timezone*)
      timestamp
    (unless (and (= nsec nsec*) (= sec sec*) (= minute minute*) (= hour hour*)
                 (= day day*) (= month month*) (= year year*))
      (cerror "Continue with testing" "Got timestamp
@~d-~2,'0d-~2,'0dT~2,'0d:~2,'0d:~2,'0d.~4,'0d, expected
@~d-~2,'0d-~2,'0dT~2,'0d:~2,'0d:~2,'0d.~4,'0d instead"
              year* month* day* hour* minute* sec* nsec* year month
day hour minute sec nsec))))

(defun minimize1 ()
  (check-timestamp
   (timestamp-minimize-part *timestamp1* :day)
   0 0 0 0 1 6 2009))

(defun minimize2 ()
  (check-timestamp
   (timestamp-minimize-part *timestamp1* :month)
   0 0 0 0 1 1 2009))

(defun adjust1 ()
  (check-timestamp
   (adjust-timestamp *timestamp1* (set :month 1))
   0 0 0 12 15 1 2009))

(defun adjust2 ()
  (check-timestamp
   (adjust-timestamp *timestamp1* (offset :month 5))
   0 0 0 12 15 11 2009))

(defun adjust3 ()
  (check-timestamp
   (adjust-timestamp *timestamp2* (offset :month 4))
   0 0 0 2 25 10 2009))

(defun adjust4 ()
  (check-timestamp
   (adjust-timestamp *timestamp2* (offset :month 4) (offset :day 1))
   0 0 0 2 26 10 2009))

(defun adjust5 ()
  (check-timestamp
   (adjust-timestamp *timestamp1* (set :month 11))
   0 0 0 12 15 11 2009))

(defun adjust6 ()
  (check-timestamp
   (adjust-timestamp *timestamp2* (set :month 10))
   0 0 0 2 25 10 2009))

(defun adjust7 ()
  (check-timestamp
   (adjust-timestamp *timestamp2* (set :month 10) (set :day-of-month 26))
   0 0 0 2 26 10 2009))

(defun test ()
  (let ((*default-timezone* *my-timezone*))
    (adjust1)
    (adjust2)
    (adjust3)
    (adjust4)
    (adjust5)
    (adjust6)
    (adjust7)
    (minimize1)
    (minimize2)))

============================================================

Additionally the bundle includes a trivial patch to improve
readcase-friendliness  by using symbols instead of strings in a few
places. We hope you'll like the changes. If you have  any comments,
we'll be glad  to implement them. Also, as you've mentioned you're no
longer actively working on the project and on the other hand we'll be
making more changes in the future and we'd like to avoid maintaining
an unnecessary fork, we'd happy to take over the maintainership if
you're so inclined.

The patch is attached in another mail, due to mailman's size limitations.

Cheers
Antoni and Maciej




More information about the local-time-devel mailing list