Lambda on the Move

A (non-)personal blog about programming, Symbian, and little else.
newest · all · tags · dates · titles · feedRSS feed · author

older · newer

Times Are Hard for Racketeers, too

APIs dealing with dates and times must be hard to get right. I'm not aware of any programming language whose standard library for dealing with times is both intuitive and comprehensive. Racket has built-in libraries that are second to none, but even it presently leaves something to be desired when it comes to support for handling dates and times.

For example, Racket's racket/date module (in its current incarnation) includes a date->string function, but no string->date function. In other words, it doesn't include a function for parsing time strings.

I recently needed time parsing as I wanted to specify times to a Racket program as strings such as "Sun, 06 Jan 2013 02:21:31 +0100", while using objects (such as Racket's date structure) as in-memory storage format in order to make the individual time components (such as month and year) readily queryable.

For the parsing support I turned to SRFI 19: Time Data Types and Procedures, which Racket implements in the form of the srfi/19 module. Said module does include a string->date function, albeit for a different date structure (tm:date).

One possibility for parsing and formatting times then is to use both racket/date and srfi/19 modules, and to convert between their respective date structures. A restriction there is that Racket date objects do not appear to be readily constructible from time components, but converting via seconds since Unix epoch (i.e., Unix time) is workable.

The code below shows how to convert between RFC 2822 time strings, Racket's native date objects, and Unix times (integers). All of the functions return times in UTC, regardless of representation.

#lang racket

(require (prefix-in d. racket/date))
(require (prefix-in s. srfi/19))

(define (date->unix-time d) ;; struct date -> integer
  (d.date->seconds d #f))

(define (unix-time->date t) ;; integer -> struct date
  (seconds->date t #f))

(define (rfc2822->unix-time s) ;; string -> integer
  (let ((d (s.string->date s "~a, ~d ~b ~Y ~H:~M:~S ~z")))
    (s.time-second (s.date->time-utc d))))

(define (date->rfc2822 d) ;; struct date -> string
  (parameterize ((d.date-display-format 'rfc2822))
    (d.date->string d #t)))

(define (unix-time->rfc2822 t) ;; integer -> string
  (date->rfc2822 (unix-time->date t)))

(define (rfc2822->date s) ;; string -> struct date
  (unix-time->date (rfc2822->unix-time s)))

(provide rfc2822->date rfc2822->unix-time
         unix-time->rfc2822 date->rfc2822)

Note that 'rfc2822 is one of the possible date-display-format choices supported by racket/date, and my personal favorite due to its readability. It is also widely supported. For example, try date -R on the Linux command line, or Time.now.rfc2822 in Ruby. For Emacs one can define support easily enough.

(defun insert-current-time-in-rfc2822 ()
  (interactive)
  (insert (format-time-string "%a, %d %b %Y %T %z")))

Addendum (12 May 2013)

Thanks to Asumu Takikawa's recent work on Racket date and srfi/19 compatibility, starting from Racket 5.3.4 the srfi/19 string->date function returns a racket/base compatible date (apparently provided that the format string has day, month, and year components). Going forward then for code like the above there should be no need to use srfi/19 functions other than string->date. The above code should still work, though.

Note about Racket 5.3.4

Well actually, it seems that specifically with the 5.3.4 Racket release the above rfc2822->unix-time function does not work. It appears that using string->date with the format directive "~a" (and possibly some others) causes an error. One fix is to change line 1470 of srfi/19/time.rkt to say

(do-nothing (lambda (val object) object))

to avoid the complaint about 0 values in a context where 1 is expected.

Written on Sunday, 6 January 2013, 01:21:31 UTC by Tero Hasu.
Edited on Sunday, 12 May 2013, 19:34:50 UTC: Added note about changes in Racket 5.3.4
You may send comments by e-mail.
Tagged as APIs, Emacs, Lisp, Racket, Scheme.
Related posts: Opening Racket Modules in Emacs, On Racket Support in Emacs Org-Mode, Dictionary-Enabled Racket Support for Emacs.