// Copyright 2013 The Servo Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Core Foundation date objects. pub use core_foundation_sys::date::*; use core_foundation_sys::base::kCFAllocatorDefault; use base::TCFType; #[cfg(feature = "with-chrono")] use chrono::NaiveDateTime; declare_TCFType!{ /// A date. CFDate, CFDateRef } impl_TCFType!(CFDate, CFDateRef, CFDateGetTypeID); impl_CFTypeDescription!(CFDate); impl_CFComparison!(CFDate, CFDateCompare); impl CFDate { #[inline] pub fn new(time: CFAbsoluteTime) -> CFDate { unsafe { let date_ref = CFDateCreate(kCFAllocatorDefault, time); TCFType::wrap_under_create_rule(date_ref) } } #[inline] pub fn now() -> CFDate { CFDate::new(unsafe { CFAbsoluteTimeGetCurrent() }) } #[inline] pub fn abs_time(&self) -> CFAbsoluteTime { unsafe { CFDateGetAbsoluteTime(self.0) } } #[cfg(feature = "with-chrono")] pub fn naive_utc(&self) -> NaiveDateTime { let ts = unsafe { self.abs_time() + kCFAbsoluteTimeIntervalSince1970 }; let (secs, nanos) = if ts.is_sign_positive() { (ts.trunc() as i64, ts.fract()) } else { // nanoseconds can't be negative in NaiveDateTime (ts.trunc() as i64 - 1, 1.0 - ts.fract().abs()) }; NaiveDateTime::from_timestamp(secs, (nanos * 1e9).floor() as u32) } #[cfg(feature = "with-chrono")] pub fn from_naive_utc(time: NaiveDateTime) -> CFDate { let secs = time.timestamp(); let nanos = time.timestamp_subsec_nanos(); let ts = unsafe { secs as f64 + (nanos as f64 / 1e9) - kCFAbsoluteTimeIntervalSince1970 }; CFDate::new(ts) } } #[cfg(test)] mod test { use super::CFDate; use std::cmp::Ordering; #[cfg(feature = "with-chrono")] use chrono::NaiveDateTime; #[cfg(feature = "with-chrono")] fn approx_eq(a: f64, b: f64) -> bool { use std::f64; let same_sign = a.is_sign_positive() == b.is_sign_positive(); let equal = ((a - b).abs() / f64::min(a.abs() + b.abs(), f64::MAX)) < f64::EPSILON; (same_sign && equal) } #[test] fn date_comparison() { let now = CFDate::now(); let past = CFDate::new(now.abs_time() - 1.0); assert_eq!(now.cmp(&past), Ordering::Greater); assert_eq!(now.cmp(&now), Ordering::Equal); assert_eq!(past.cmp(&now), Ordering::Less); } #[test] fn date_equality() { let now = CFDate::now(); let same_time = CFDate::new(now.abs_time()); assert_eq!(now, same_time); } #[test] #[cfg(feature = "with-chrono")] fn date_chrono_conversion_positive() { let date = CFDate::now(); let datetime = date.naive_utc(); let converted = CFDate::from_naive_utc(datetime); assert!(approx_eq(date.abs_time(), converted.abs_time())); } #[test] #[cfg(feature = "with-chrono")] fn date_chrono_conversion_negative() { use super::kCFAbsoluteTimeIntervalSince1970; let ts = unsafe { kCFAbsoluteTimeIntervalSince1970 - 420.0 }; let date = CFDate::new(ts); let datetime: NaiveDateTime = date.naive_utc(); let converted = CFDate::from_naive_utc(datetime); assert!(approx_eq(date.abs_time(), converted.abs_time())); } }