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" )))