From 5c76007d0ef3b957799b4ef05a6d6e6ba76c7d11 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Sat, 5 Nov 2022 15:48:05 -0700 Subject: [PATCH] Add `XmpDateTime::convert_to_local_time` and `XmpDateTime::convert_to_utc` --- src/ffi.cpp | 32 +++++++ src/ffi.rs | 2 + src/tests/xmp_date_time.rs | 181 +++++++++++++++++++++++++++++++++++++ src/xmp_date_time.rs | 38 ++++++++ 4 files changed, 253 insertions(+) diff --git a/src/ffi.cpp b/src/ffi.cpp index 276aae1..c021bd1 100644 --- a/src/ffi.cpp +++ b/src/ffi.cpp @@ -1385,6 +1385,38 @@ extern "C" { #endif } + void CXmpDateTimeConvertToLocalTime(XMP_DateTime* dt, CXmpError* outError) { + #ifndef NOOP_FFI + try { + if (dt) { + SXMPUtils::ConvertToLocalTime(dt); + } + } + catch (XMP_Error& e) { + copyErrorForResult(e, outError); + } + catch (...) { + signalUnknownError(outError); + } + #endif + } + + void CXmpDateTimeConvertToUTCTime(XMP_DateTime* dt, CXmpError* outError) { + #ifndef NOOP_FFI + try { + if (dt) { + SXMPUtils::ConvertToUTCTime(dt); + } + } + catch (XMP_Error& e) { + copyErrorForResult(e, outError); + } + catch (...) { + signalUnknownError(outError); + } + #endif + } + const char* CXmpDateTimeToString(const XMP_DateTime* dt, CXmpError* outError) { #ifndef NOOP_FFI try { diff --git a/src/ffi.rs b/src/ffi.rs index 3c19342..5a32c0b 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -548,6 +548,8 @@ extern "C" { pub(crate) fn CXmpDateTimeCurrent(dt: *mut CXmpDateTime, out_error: *mut CXmpError); pub(crate) fn CXmpDateTimeSetTimeZone(dt: *mut CXmpDateTime, out_error: *mut CXmpError); + pub(crate) fn CXmpDateTimeConvertToLocalTime(dt: *mut CXmpDateTime, out_error: *mut CXmpError); + pub(crate) fn CXmpDateTimeConvertToUTCTime(dt: *mut CXmpDateTime, out_error: *mut CXmpError); pub(crate) fn CXmpDateTimeToString( dt: *const CXmpDateTime, diff --git a/src/tests/xmp_date_time.rs b/src/tests/xmp_date_time.rs index 2d833f6..b191b6d 100644 --- a/src/tests/xmp_date_time.rs +++ b/src/tests/xmp_date_time.rs @@ -121,6 +121,187 @@ mod set_local_time_zone { } } +mod convert_to_local_time { + use crate::{XmpDate, XmpDateTime, XmpTime, XmpTimeZone}; + + #[test] + fn no_existing_tz() { + let mut dt = XmpDateTime { + date: Some(XmpDate { + year: 2022, + month: 11, + day: 5, + }), + time: Some(XmpTime { + hour: 14, + minute: 40, + second: 35, + nanosecond: 42, + time_zone: None, + }), + }; + + dt.convert_to_local_time().unwrap(); + + assert_eq!( + dt.date.unwrap(), + XmpDate { + year: 2022, + month: 11, + day: 5 + } + ); + + let time = dt.time.unwrap(); + assert_eq!(time.hour, 14); + assert_eq!(time.minute, 40); + assert_eq!(time.second, 35); + assert_eq!(time.nanosecond, 42); + assert!(time.time_zone.is_none()); + } + + #[test] + fn existing_tz() { + let mut dt = XmpDateTime { + date: Some(XmpDate { + year: 2022, + month: 11, + day: 5, + }), + time: Some(XmpTime { + hour: 14, + minute: 40, + second: 35, + nanosecond: 42, + time_zone: Some(XmpTimeZone { hour: 1, minute: 2 }), + // Use an unusual time zone so we can determine if + // *something* changed. + }), + }; + + dt.convert_to_local_time().unwrap(); + + // Since we don't know when writing this test what time + // zone will be in effect when running this test, we do some + // basic sanity checks to ensure that *something* changed. + + println!("Updated date time = {:#?}", dt); + + assert_eq!(dt.date.unwrap().year, 2022); + + let time = dt.time.unwrap(); + + assert_ne!( + time, + XmpTime { + hour: 14, + minute: 40, + second: 35, + nanosecond: 42, + time_zone: Some(XmpTimeZone { hour: 1, minute: 2 }), + } + ); + + assert_ne!(time.minute, 40); + } +} + +mod convert_to_utc { + use crate::{XmpDate, XmpDateTime, XmpTime, XmpTimeZone}; + + #[test] + fn no_existing_tz() { + let original_dt = XmpDateTime { + date: Some(XmpDate { + year: 2022, + month: 11, + day: 5, + }), + time: Some(XmpTime { + hour: 14, + minute: 40, + second: 35, + nanosecond: 42, + time_zone: None, + }), + }; + + let mut dt = original_dt.clone(); + dt.convert_to_utc().unwrap(); + + assert_eq!(original_dt, dt); + } + + #[test] + fn existing_tz() { + let mut dt = XmpDateTime { + date: Some(XmpDate { + year: 2022, + month: 11, + day: 5, + }), + time: Some(XmpTime { + hour: 19, + minute: 40, + second: 35, + nanosecond: 42, + time_zone: Some(XmpTimeZone { + hour: -7, + minute: 0, + }), + }), + }; + + dt.convert_to_utc().unwrap(); + + println!("Updated date time = {:#?}", dt); + + assert_eq!( + dt, + XmpDateTime { + date: Some(XmpDate { + year: 2022, + month: 11, + day: 6, + }), + time: Some(XmpTime { + hour: 2, + minute: 40, + second: 35, + nanosecond: 42, + time_zone: Some(XmpTimeZone { hour: 0, minute: 0 }), + }), + } + ); + } + + #[test] + fn already_utc() { + let original_dt = XmpDateTime { + date: Some(XmpDate { + year: 2022, + month: 11, + day: 5, + }), + time: Some(XmpTime { + hour: 19, + minute: 40, + second: 35, + nanosecond: 42, + time_zone: Some(XmpTimeZone { hour: 0, minute: 0 }), + }), + }; + + let mut dt = original_dt.clone(); + + dt.convert_to_utc().unwrap(); + + println!("Updated date time = {:#?}", dt); + + assert_eq!(original_dt, dt); + } +} + mod from_ffi { use crate::{ffi, XmpDate, XmpDateTime, XmpTime, XmpTimeZone}; diff --git a/src/xmp_date_time.rs b/src/xmp_date_time.rs index 257df23..b2a48e3 100644 --- a/src/xmp_date_time.rs +++ b/src/xmp_date_time.rs @@ -120,6 +120,44 @@ impl XmpDateTime { Ok(()) } + /// Translate the value to the local time zone. + /// + /// If the time zone is not the local zone, the time is adjusted and the + /// time zone set to be local. The value is not modified if the time zone is + /// already the local zone or if the value has no time zone. + pub fn convert_to_local_time(&mut self) -> XmpResult<()> { + let mut dt = self.as_ffi(); + let mut err = ffi::CXmpError::default(); + + unsafe { + ffi::CXmpDateTimeConvertToLocalTime(&mut dt, &mut err); + } + + XmpError::raise_from_c(&err)?; + + self.update_from_ffi(&dt); + Ok(()) + } + + /// Translates the value to UTC (Coordinated Universal Time). + /// + /// If the time zone is not UTC, the time is adjusted and the time zone set + /// to be UTC. The value is not modified if the time zone is already UTC or + /// if the value has no time zone. + pub fn convert_to_utc(&mut self) -> XmpResult<()> { + let mut dt = self.as_ffi(); + let mut err = ffi::CXmpError::default(); + + unsafe { + ffi::CXmpDateTimeConvertToUTCTime(&mut dt, &mut err); + } + + XmpError::raise_from_c(&err)?; + + self.update_from_ffi(&dt); + Ok(()) + } + pub(crate) fn from_ffi(dt: &ffi::CXmpDateTime) -> Self { let mut result = Self::default(); result.update_from_ffi(dt);