diff --git a/src/datetime.rs b/src/datetime.rs index 34bbaf3..e8dd6c1 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -8,6 +8,7 @@ use std::cmp::Ordering; use std::ops::{Add, Sub}; #[cfg(feature = "rustc-serialize")] use std::ops::Deref; +use std::time::{SystemTime, UNIX_EPOCH}; use oldtime::Duration as OldDuration; use {Weekday, Timelike, Datelike}; @@ -421,6 +422,45 @@ impl str::FromStr for DateTime { } } +impl From for DateTime { + fn from(t: SystemTime) -> DateTime { + let (sec, nsec) = match t.duration_since(UNIX_EPOCH) { + Ok(dur) => (dur.as_secs() as i64, dur.subsec_nanos()), + Err(e) => { // unlikely but should be handled + let dur = e.duration(); + let (sec, nsec) = (dur.as_secs() as i64, dur.subsec_nanos()); + if nsec == 0 { + (-sec, 0) + } else { + (-sec - 1, 1_000_000_000 - nsec) + } + }, + }; + Utc.timestamp(sec, nsec) + } +} + +impl From for DateTime { + fn from(t: SystemTime) -> DateTime { + DateTime::::from(t).with_timezone(&Local) + } +} + +impl From> for SystemTime { + fn from(dt: DateTime) -> SystemTime { + use std::time::Duration; + + let sec = dt.timestamp(); + let nsec = dt.timestamp_subsec_nanos(); + if sec < 0 { + // unlikely but should be handled + UNIX_EPOCH - Duration::new(-sec as u64, 0) + Duration::new(0, nsec) + } else { + UNIX_EPOCH + Duration::new(sec as u64, nsec) + } + } +} + #[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] fn test_encodable_json(to_string_utc: FUtc, to_string_fixed: FFixed) where FUtc: Fn(&DateTime) -> Result, @@ -875,6 +915,7 @@ mod tests { use naive::{NaiveTime, NaiveDate}; use offset::{TimeZone, Utc, Local, FixedOffset}; use oldtime::Duration; + use std::time::{SystemTime, UNIX_EPOCH}; #[test] #[allow(non_snake_case)] @@ -1044,4 +1085,30 @@ mod tests { assert_eq!(1234, datetime.timestamp_subsec_micros()); assert_eq!(1234567, datetime.timestamp_subsec_nanos()); } + + #[test] + fn test_from_system_time() { + use std::time::Duration; + + let epoch = Utc.ymd(1970, 1, 1).and_hms(0, 0, 0); + + // SystemTime -> DateTime + assert_eq!(DateTime::::from(UNIX_EPOCH), epoch); + assert_eq!(DateTime::::from(UNIX_EPOCH + Duration::new(999_999_999, 999_999_999)), + Utc.ymd(2001, 9, 9).and_hms_nano(1, 46, 39, 999_999_999)); + assert_eq!(DateTime::::from(UNIX_EPOCH - Duration::new(999_999_999, 999_999_999)), + Utc.ymd(1938, 4, 24).and_hms_nano(22, 13, 20, 1)); + + // DateTime -> SystemTime + assert_eq!(SystemTime::from(epoch), UNIX_EPOCH); + assert_eq!(SystemTime::from(Utc.ymd(2001, 9, 9).and_hms_nano(1, 46, 39, 999_999_999)), + UNIX_EPOCH + Duration::new(999_999_999, 999_999_999)); + assert_eq!(SystemTime::from(Utc.ymd(1938, 4, 24).and_hms_nano(22, 13, 20, 1)), + UNIX_EPOCH - Duration::new(999_999_999, 999_999_999)); + + // DateTime -> SystemTime (via `with_timezone`) + assert_eq!(SystemTime::from(epoch.with_timezone(&Local)), UNIX_EPOCH); + assert_eq!(SystemTime::from(epoch.with_timezone(&FixedOffset::east(32400))), UNIX_EPOCH); + assert_eq!(SystemTime::from(epoch.with_timezone(&FixedOffset::west(28800))), UNIX_EPOCH); + } }