1 // Copyright 2013 The Servo Project Developers. See the COPYRIGHT 2 // file at the top-level directory of this distribution. 3 // 4 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or 5 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license 6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your 7 // option. This file may not be copied, modified, or distributed 8 // except according to those terms. 9 10 //! Core Foundation date objects. 11 12 pub use core_foundation_sys::date::*; 13 use core_foundation_sys::base::kCFAllocatorDefault; 14 15 use base::TCFType; 16 17 #[cfg(feature = "with-chrono")] 18 use chrono::NaiveDateTime; 19 20 21 declare_TCFType!{ 22 /// A date. 23 CFDate, CFDateRef 24 } 25 impl_TCFType!(CFDate, CFDateRef, CFDateGetTypeID); 26 impl_CFTypeDescription!(CFDate); 27 impl_CFComparison!(CFDate, CFDateCompare); 28 29 impl CFDate { 30 #[inline] new(time: CFAbsoluteTime) -> CFDate31 pub fn new(time: CFAbsoluteTime) -> CFDate { 32 unsafe { 33 let date_ref = CFDateCreate(kCFAllocatorDefault, time); 34 TCFType::wrap_under_create_rule(date_ref) 35 } 36 } 37 38 #[inline] now() -> CFDate39 pub fn now() -> CFDate { 40 CFDate::new(unsafe { CFAbsoluteTimeGetCurrent() }) 41 } 42 43 #[inline] abs_time(&self) -> CFAbsoluteTime44 pub fn abs_time(&self) -> CFAbsoluteTime { 45 unsafe { 46 CFDateGetAbsoluteTime(self.0) 47 } 48 } 49 50 #[cfg(feature = "with-chrono")] naive_utc(&self) -> NaiveDateTime51 pub fn naive_utc(&self) -> NaiveDateTime { 52 let ts = unsafe { 53 self.abs_time() + kCFAbsoluteTimeIntervalSince1970 54 }; 55 let (secs, nanos) = if ts.is_sign_positive() { 56 (ts.trunc() as i64, ts.fract()) 57 } else { 58 // nanoseconds can't be negative in NaiveDateTime 59 (ts.trunc() as i64 - 1, 1.0 - ts.fract().abs()) 60 }; 61 NaiveDateTime::from_timestamp(secs, (nanos * 1e9).floor() as u32) 62 } 63 64 #[cfg(feature = "with-chrono")] from_naive_utc(time: NaiveDateTime) -> CFDate65 pub fn from_naive_utc(time: NaiveDateTime) -> CFDate { 66 let secs = time.timestamp(); 67 let nanos = time.timestamp_subsec_nanos(); 68 let ts = unsafe { 69 secs as f64 + (nanos as f64 / 1e9) - kCFAbsoluteTimeIntervalSince1970 70 }; 71 CFDate::new(ts) 72 } 73 } 74 75 #[cfg(test)] 76 mod test { 77 use super::CFDate; 78 use std::cmp::Ordering; 79 80 #[cfg(feature = "with-chrono")] 81 use chrono::NaiveDateTime; 82 83 #[cfg(feature = "with-chrono")] approx_eq(a: f64, b: f64) -> bool84 fn approx_eq(a: f64, b: f64) -> bool { 85 use std::f64; 86 87 let same_sign = a.is_sign_positive() == b.is_sign_positive(); 88 let equal = ((a - b).abs() / f64::min(a.abs() + b.abs(), f64::MAX)) < f64::EPSILON; 89 (same_sign && equal) 90 } 91 92 #[test] date_comparison()93 fn date_comparison() { 94 let now = CFDate::now(); 95 let past = CFDate::new(now.abs_time() - 1.0); 96 assert_eq!(now.cmp(&past), Ordering::Greater); 97 assert_eq!(now.cmp(&now), Ordering::Equal); 98 assert_eq!(past.cmp(&now), Ordering::Less); 99 } 100 101 #[test] date_equality()102 fn date_equality() { 103 let now = CFDate::now(); 104 let same_time = CFDate::new(now.abs_time()); 105 assert_eq!(now, same_time); 106 } 107 108 #[test] 109 #[cfg(feature = "with-chrono")] date_chrono_conversion_positive()110 fn date_chrono_conversion_positive() { 111 let date = CFDate::now(); 112 let datetime = date.naive_utc(); 113 let converted = CFDate::from_naive_utc(datetime); 114 assert!(approx_eq(date.abs_time(), converted.abs_time())); 115 } 116 117 #[test] 118 #[cfg(feature = "with-chrono")] date_chrono_conversion_negative()119 fn date_chrono_conversion_negative() { 120 use super::kCFAbsoluteTimeIntervalSince1970; 121 122 let ts = unsafe { 123 kCFAbsoluteTimeIntervalSince1970 - 420.0 124 }; 125 let date = CFDate::new(ts); 126 let datetime: NaiveDateTime = date.naive_utc(); 127 let converted = CFDate::from_naive_utc(datetime); 128 assert!(approx_eq(date.abs_time(), converted.abs_time())); 129 } 130 } 131