BZLIB/DATE & BZLIB/DATE-TZ are now available on planet. They provide additional date manipulation capability on top of SRFI/19, including
timezone manipulation.
They are released under LGPL.
Usage and Installation
(require (planet bzlib/date))
(require (planet bzlib/date-tz))
bzlib/date provides date manipulations.
bzlib/date provides timezone manipulations. Their usages are separately discussed below.
bzlib/date
To create a date object, you can use
bulid-date, which provides a more natural year/month/day/hour/minute/second order of the parameters:
(build-date <year> <month> <day> <hour> <minute> <second> #:tz <offset>)
And it would do the right thing if you enter February 31st:
(build-date 2009 2 31) ;; => #(struct:tm:date 0 0 0 0 3 3 2009 0)
By default, the tz offset is 0, which equates to GMT (see below for timezone support). Only
year,
month, and
day are required.
Date Comparisons
The following function compares two dates to determine their orders:
(date>? <d1> <g2>)
(date<? <d1> <g2>)
(date>=? <d1> <g2>)
(date<=? <d1> <g2>)
(day=? <d1> <g2>)
(date!=? <d1> <g2>)
(date===? <d1> <g2>)
day=? only compares the year/month/day values, and
date===? means they are the same date with the same tz offset.
Conversions
You can convert between date and seconds with
(date->seconds <date>) and
(seconds->date <seconds>).
You can add to a date with
(date+ <date> <number-of-days>). The number of day can be a non-integer.
You can find out the gaps between two dates with
(date- <date1> <date2>).
You can create an alarm event with date via
(date->alarm <date>) or
(date->future-alarm <date>). The difference between the two is that
date->future-alarm will return false if the date is in the past.
Dealing with Weekdays
To find out the weekday of a particular date, you can use
(week-day <date>).
To find out the date of the nth-weekday (e.g., first sunday, 3rd wednesday, last Friday) of a particular month, use
nth-week-day:
(nth-week-day <year> <month> <week-day> <nth> <hour> <minute> <second> #:tz <offset>)
For the
week-day argument, use 0 for Sunday, and 6 for Saturday. For the
nth argument, use 1, 2, 3, 4, 5, or
'last.
hour,
minute,
second, and
offset are optional (same as build-date, and the other functions below that have them).
To find out the date of a particular weekday relative to another date, use one of the following:
week-day>=mday
week-day<=mday
week-day>mday
week-day<mday
They all share the same arguments, which are
year,
month, week-day, month-day, hour, minute, second, and offset.
The usage is something like:
;; the sunday after May 15th, 2009
(week-day>mday 2009 5 0 15) ;; 5/17/2009
;; the friday before September 22nd, 2008
(week-day<mday 2009 9 5 22) ;; 9/19/2009
The hour, minute, second, and offset parameters are there for you to customize the return values:
;; the sunday after May 15th, 2009
(week-day>mday 2009 5 0 15 15 0 0 #:tz -28800) ;; 5/17/2009 15:00:00-28800
;; the friday before September 22nd, 2008
(week-day<mday 2009 9 5 22 8 30 25 #:tz 14400) ;; 9/19/2009 08:00:00+14400
bzlib/date-tz
By default, you need to parameterize the current-tz parameter, which defaults to America/Los_Angeles. The timezone names are the available names from the olson database.
To determine the offset of any date for a particular timezone, use tz-offset:
(parameterize ((current-tz "America/New_York"))
(tz-offset (build-date 2008 3 9))) ;; => -18800
(parameterize ((current-tz "America/New_York"))
(tz-offset (build-date 2008 3 10))) ;; => -14400
If you want to separate between the standard offset and the daylight saving offset, you can use tz-standard-offset or tz-daylight-saving-offset:
(let ((d1 (build-date 2008 3 9))
(d2 (build-date 2008 3 10)))
(parameterize ((current-tz "America/New_York"))
(values (tz-standard-offset d1)
(tz-daylight-saving-offset d1)
(tz-daylight-saving-offset d2))))
;; => -18800 (std)
;; => 0 (dst on 3/9/2008)
;; => 3600 (dst on 3/10/2008)
Conversion
To reset a date's tz offset, you can use the helper function date->tz, which will reset the offset for you:
(let ((date (build-date 2008 3 10 #:tz -18800)))
(parameterize ((current-tz "America/New_York"))
(date->tz date)))
;; => #(struct:tm:date 0 0 0 0 10 3 2008 -14400)
This function is meant for you to fix the offsets for dates that belong to a particular timezone but did not correctly account for the offset - it does not switch the timezone for you.
Couple other functions makes it even easier to work with timezone.
(build-date/tz <year> <month> ...)
(date+/tz <date> <number-of-days>)
They basically creates the date object and calls date->tz so the offset is properly adjusted based on the timezone.
Besides using current-tz, you can also pass it explicitly to tz-offset, tz-standard-offset, tz-daylight-saving-offset, date->tz, build-date/tz, and date+/tz. You pass it in in the following forms:
(tz-offset <date> "America/Los_Angeles")
(tz-daylight-saving-offset <date> "Asia/Kolkata")
(tz-standard-offset <date> "Europe/London")
(date->tz <date> "Europe/London")
(build-date/tz <year> <month> <day> #:tz "America/New_York")
(date+/tz <date> <number-of-days> "America/Los_Angeles")
Convert from One Timezone to Another
To covert the timezone of a date so you get the same date in a different timezone, use tz-convert:
(tz-convert <date> >from-timezone> <to-timezone>)
All parameters are required.
(tz-convert (build-date 2008 3 10 15) "America/New_York" "America/Los_Angeles")
;; => #(struct:tm:date 0 0 0 12 10 3 2008 -25200) ;; 2008/3/10 12:00:00-25200
(tz-convert (build-date 2008 3 10 15) "America/New_York" "GMT")
;; ==> #(struct:tm:date 0 0 0 19 10 3 2008 0) ;; 2008/10/10 19:00:00+00:00
That's it for now - enjoy.