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.
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, theUNIX_EPOCH
constant is provided in this module as an anchor in time to learn information about aSystemTime
. By calculating the duration from this fixed point in time, aSystemTime
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.