Pyfisch’s Website > Blog

HTTP Date/Time Handling

written by Pyfisch on

TL;DR: I’ve written a httpdate crate to parse and format dates found in HTTP. It has no dependencies and uses std::time::SystemTime to store all times.

HTTP communicates some timestamps. To do this it uses an old format called IMF-fixdate: Sat, 15 Oct 2016 11:48:13 GMT. (For compatibility with ancient endpoints it supports parsing two more formats.) While Rust has the excellent chrono crate and the deprecated but usable time crate (chrono still depends on it) for time handling they are both overkill for the task. These crates do a lot of things that are generally useful if you work with dates but in this case are not needed like timezones, extensive formatting and parsing methods and a lot more.

An old 24 hour clock. Parsing HTTP dates is a trivial task compared to building a clock. By DeFacto CC BY-SA 4.0, via Wikimedia Commons

I wrote a small crate called httpdate to parse a timestamp to a std::time::SystemTime and format it as required for HTTP. It is both less code (more then ten thousand lines vs about 300 lines) so it compiles in an instant and it is faster because it is written specially for HTTP timestamps (not that it would matter in normal use but some micro-benchmarks will be faster).

Usage

Using httpdate is very straightforward:

With parse_http_date(s: &str) -> Result<SystemTime, ()> a date from HTTP is parsed. The function tries to parse the string in all supported formats. With fmt_http_date(d: SystemTime) -> String a system time is formatted to an IMF-fixdate.

Using std::time::SystemTime

Rusts standard system time is used to store the timestamps from HTTP internally. From the documentation:

Although a SystemTime cannot be directly inspected, the UNIX_EPOCH constant is provided in this module as an anchor in time to learn information about a SystemTime. By calculating the duration from this fixed point in time, a SystemTime can be converted to a human-readable time, or perhaps some other string representation.

To use a SystemTime I first convert it to the number of seconds since the epoch (sub seconds are discarded in HTTP):

let secs_since_epoch = v // the system time
  .duration_since(UNIX_EPOCH) // get the duration from the epoch
  .unwrap() // Dates prior to 1970 are not expected
            // common HTTP are either the current date,
            // last-modified dates or events in the future
            // like cache expiration.
            // Only if you do time-travel you may have problems.
  .as_secs(); // Convert the duration to an integer.

A SystemTime is constructed by adding a duration to the epoch:

let time: SystemTime = UNIX_EPOCH + Duration::from_secs(1482490056);

Internals

The crate converts strings to an internal DateTime struct first. This structure has fields for time (second, minute, hour) and date (day, month, year, day of week). The result is then converted to a SystemTime. To get a HTTP date from a SystemTime the above steps are applied the other way, but calculating the day for a given moment is more compex.