Parsing a time string into a Unix timestamp


#1

I’m using the time crate and strptime to parse timestamps of the form 2015-02-16T00:00:00-0500. First, strptime seems to be ignoring timezone offsets, but that has been a problem for a while.

Converting a Tm to a Timespec seems to be dependent on the timezone of the host machine. If I run this code:

#![feature(env)]
extern crate time;

use time::strptime;
use std::env::args;

fn main() {
    for ref timestr in args().skip(1) {
        let tm = strptime(timestr, "%FT%T%z").unwrap();
        println!("{}\n{:?}\n{:?}\n", timestr, tm, tm.to_timespec());
    }
}

Like so:

$ TZ=UTC ./timespec 2015-02-16T00:00:00-0500
2015-02-16T00:00:00-0500
Tm { tm_sec: 0, tm_min: 0, tm_hour: 0, tm_mday: 16, tm_mon: 1, tm_year: 115, tm_wday: 0, tm_yday: 0, tm_isdst: 0, tm_utcoff: -18000, tm_nsec: 0 }
Timespec { sec: 1424044800, nsec: 0 }

$ TZ=EST ./timespec 2015-02-16T00:00:00-0500
2015-02-16T00:00:00-0500
Tm { tm_sec: 0, tm_min: 0, tm_hour: 0, tm_mday: 16, tm_mon: 1, tm_year: 115, tm_wday: 0, tm_yday: 0, tm_isdst: 0, tm_utcoff: -18000, tm_nsec: 0 }
Timespec { sec: 1424062800, nsec: 0 }

It yeilds different results. My understanding is that these are particular points in time and should not vary based on where you are. The date(1) command seems to agree:

$ TZ=UTC date --date 2015-02-16T00:00:00-0500 +%s
1424062800
$ TZ=EST date --date 2015-02-16T00:00:00-0500 +%s
1424062800

Looking at the source of to_timespec(), I’m not sure what it’s trying to do:

impl Tm {
    /// Convert time to the seconds from January 1, 1970
    pub fn to_timespec(&self) -> Timespec {
        unsafe {
            let sec = match self.tm_utcoff {
                0 => rustrt::rust_time_timegm(self),
                _     => rustrt::rust_time_mktime(self)
            };

            Timespec::new(sec, self.tm_nsec)
        }
    }

How can I parse a time string into a Unix timestamp correctly and consistently? (This is with rustc 1.0.0-nightly (b63cee4a1 2015-02-14 17:01:11 +0000) and time 0.1.17.)