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