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 (
One possibility for parsing and formatting times then is to use both
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)
'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
srfi/19 compatibility, starting from Racket 5.3.4 the
string->date function returns a
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 (updated 13 Oct 2015): In some Racket versions (starting with 5.3.4, but by now fixed) the
rfc2822->unix-time function given above does not work, due to a minor issue with
srfi/19, one that causes an error when
string->date is used with the format directive
"~a" (and possibly some others). If you're using an affected version, one fix is to change the file “srfi/19/time.rkt” (somewhere around line 1470) to say
(do-nothing (lambda (val object) object)), to avoid the complaint about 0 values in a context where 1 is expected.