use std::fmt;
use crate::{
ffi::{self, CXmpString},
XmpError, XmpResult,
};
#[derive(Clone, Default, Debug, Eq, PartialEq)]
pub struct XmpDateTime {
pub date: Option<XmpDate>,
pub time: Option<XmpTime>,
}
#[derive(Clone, Default, Debug, Eq, PartialEq)]
pub struct XmpDate {
pub year: i32,
pub month: i32,
pub day: i32,
}
#[derive(Clone, Default, Debug, Eq, PartialEq)]
pub struct XmpTime {
pub hour: i32,
pub minute: i32,
pub second: i32,
pub nanosecond: i32,
pub time_zone: Option<XmpTimeZone>,
}
#[derive(Clone, Default, Debug, Eq, PartialEq)]
pub struct XmpTimeZone {
pub hour: i32,
pub minute: i32,
}
impl XmpDateTime {
pub fn current() -> XmpResult<Self> {
let mut dt = ffi::CXmpDateTime::default();
let mut err = ffi::CXmpError::default();
unsafe { ffi::CXmpDateTimeCurrent(&mut dt, &mut err) };
XmpError::raise_from_c(&err)?;
Ok(Self::from_ffi(&dt))
}
pub fn set_local_time_zone(&mut self) -> XmpResult<()> {
let mut dt = self.as_ffi();
let mut err = ffi::CXmpError::default();
unsafe {
ffi::CXmpDateTimeSetTimeZone(&mut dt, &mut err);
}
XmpError::raise_from_c(&err)?;
self.update_from_ffi(&dt);
Ok(())
}
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(())
}
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);
result
}
pub(crate) fn update_from_ffi(&mut self, dt: &ffi::CXmpDateTime) {
self.date = if dt.has_date {
Some(XmpDate {
year: dt.year,
month: dt.month,
day: dt.day,
})
} else {
None
};
self.time = if dt.has_time {
Some(XmpTime {
hour: dt.hour,
minute: dt.minute,
second: dt.second,
nanosecond: dt.nanosecond,
time_zone: if dt.has_time_zone {
Some(XmpTimeZone {
hour: if dt.tz_sign < 0 {
-dt.tz_hour
} else {
dt.tz_hour
},
minute: dt.tz_minute,
})
} else {
None
},
})
} else {
None
};
}
pub(crate) fn as_ffi(&self) -> ffi::CXmpDateTime {
let mut result = ffi::CXmpDateTime::default();
if let Some(date) = &self.date {
result.has_date = true;
result.year = date.year;
result.month = date.month;
result.day = date.day;
}
if let Some(time) = &self.time {
result.has_time = true;
result.hour = time.hour;
result.minute = time.minute;
result.second = time.second;
result.nanosecond = time.nanosecond;
if let Some(tz) = &time.time_zone {
result.has_time_zone = true;
match tz.hour {
h if h < 0 => {
result.tz_sign = -1;
result.tz_hour = -h;
}
0 if tz.minute == 0 => {
result.tz_sign = 0;
result.tz_hour = 0;
}
h => {
result.tz_sign = 1;
result.tz_hour = h;
}
};
result.tz_minute = tz.minute;
}
}
result
}
}
impl fmt::Display for XmpDateTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut err = ffi::CXmpError::default();
unsafe {
match CXmpString::from_ptr(ffi::CXmpDateTimeToString(&self.as_ffi(), &mut err))
.map(|s| s)
{
Some(s) => {
write!(f, "{}", s)
}
None => {
let err = XmpError::raise_from_c(&err);
write!(f, "(unable to format date: {:#?})", err)
}
}
}
}
}