Skip to content

Commit

Permalink
Add CFDate conversions to/from SystemTime
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Jan 11, 2025
1 parent 897af46 commit 3fe19b2
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 1 deletion.
82 changes: 81 additions & 1 deletion framework-crates/objc2-core-foundation/src/date.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,41 @@
#![cfg(feature = "CFBase")]
use core::{cmp::Ordering, ptr};

use crate::{CFDate, CFDateCompare};
use crate::{kCFAbsoluteTimeIntervalSince1970, CFDate, CFDateCompare};

impl CFDate {
/// Create a `CFDate` from a [`SystemTime`].
///
/// Nanosecond precision may be lost.
///
/// [`SystemTime`]: std::time::SystemTime
#[cfg(feature = "std")]
pub fn from_system_time(time: &std::time::SystemTime) -> crate::CFRetained<Self> {
let since_1970 = match time.duration_since(std::time::UNIX_EPOCH) {
Ok(duration) => duration.as_secs_f64(),
Err(err) => -err.duration().as_secs_f64(),
} as core::ffi::c_double;

let since_2001 = since_1970 - unsafe { kCFAbsoluteTimeIntervalSince1970 };
unsafe { crate::CFDateCreate(None, since_2001).expect("failed creating CFDate") }
}

/// Try to construct a [`SystemTime`] from the `CFDate`.
///
/// Nanosecond precision may be lost.
///
/// Returns `None` if the `CFDate` is too large to fit inside
/// [`SystemTime`].
///
/// [`SystemTime`]: std::time::SystemTime
#[cfg(feature = "std")]
pub fn to_system_time(&self) -> Option<std::time::SystemTime> {
let since_2001 = unsafe { crate::CFDateGetAbsoluteTime(self) };
let since_1970 = (since_2001 + unsafe { kCFAbsoluteTimeIntervalSince1970 }) as f64;

std::time::UNIX_EPOCH.checked_add(std::time::Duration::try_from_secs_f64(since_1970).ok()?)
}
}

impl PartialOrd for CFDate {
#[inline]
Expand All @@ -23,6 +57,9 @@ impl Ord for CFDate {

#[cfg(test)]
mod test {
use core::ffi::c_double;
use std::time::{Duration, SystemTime};

use crate::{CFAbsoluteTimeGetCurrent, CFDateCreate, CFDateGetAbsoluteTime};

use super::*;
Expand All @@ -38,4 +75,47 @@ mod test {
assert_eq!(now, now);
assert_ne!(now, past);
}

#[test]
fn system_time_roundtrip() {
let date1 = unsafe { CFDateCreate(None, CFAbsoluteTimeGetCurrent()).unwrap() };
let date2 = CFDate::from_system_time(&date1.to_system_time().unwrap());
let diff = unsafe { CFDateGetAbsoluteTime(&date1) - CFDateGetAbsoluteTime(&date2) };
assert!(diff <= 1.0); // Some precision is lost
}

#[test]
fn system_time_cmp() {
let std_now_first = SystemTime::now();
let cf_now_first = unsafe { CFDateCreate(None, CFAbsoluteTimeGetCurrent() + 1.0).unwrap() };
let std_now_second = std_now_first.checked_add(Duration::from_secs(2)).unwrap();
let cf_now_second =
unsafe { CFDateCreate(None, CFAbsoluteTimeGetCurrent() + 3.0).unwrap() };

assert!(std_now_first <= std_now_second);
assert!(cf_now_first <= cf_now_second);

assert!(std_now_first <= cf_now_first.to_system_time().unwrap());
assert!(cf_now_first.to_system_time().unwrap() <= std_now_second);

assert!(cf_now_first <= CFDate::from_system_time(&std_now_second));
assert!(CFDate::from_system_time(&std_now_second) <= cf_now_second);
}

#[test]
fn system_time_from_odd() {
let time = SystemTime::UNIX_EPOCH
.checked_sub(Duration::from_secs(10))
.unwrap();
let _ = CFDate::from_system_time(&time);
}

#[test]
fn system_time_unrepresentable() {
let date = unsafe { CFDateCreate(None, c_double::MIN).unwrap() };
assert_eq!(date.to_system_time(), None);

let date = unsafe { CFDateCreate(None, c_double::MAX).unwrap() };
assert_eq!(date.to_system_time(), None);
}
}
2 changes: 2 additions & 0 deletions framework-crates/objc2-core-foundation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ mod number;
mod retained;
#[cfg(feature = "CFString")]
mod string;
#[cfg(feature = "CFTimeZone")]
mod timezone;
mod type_traits;
#[cfg(feature = "CFUUID")]
mod uuid;
Expand Down
17 changes: 17 additions & 0 deletions framework-crates/objc2-core-foundation/src/timezone.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#![cfg(feature = "CFDate")]

#[cfg(test)]
mod tests {
use crate::{CFTimeZoneCopyDefault, CFTimeZoneCopySystem, CFTimeZoneGetName};

#[test]
fn cmp() {
let system = unsafe { CFTimeZoneCopySystem().unwrap() };
let default = unsafe { CFTimeZoneCopyDefault().unwrap() };
assert_eq!(system, default);
assert_eq!(
unsafe { CFTimeZoneGetName(&system) }.unwrap(),
unsafe { CFTimeZoneGetName(&default) }.unwrap(),
);
}
}

0 comments on commit 3fe19b2

Please sign in to comment.