use std::{cmp, fmt};
use glib::translate::*;
use crate::{ffi, DateTime};
fn validate(
tzoffset: Option<f32>,
year: i32,
month: Option<i32>,
day: Option<i32>,
hour: Option<i32>,
minute: Option<i32>,
seconds: Option<f64>,
) -> Result<(), glib::BoolError> {
skip_assert_initialized!();
if year <= 0 || year > 9999 {
return Err(glib::bool_error!(
"Can't create DateTime: Year out of range"
));
}
if let Some(month) = month {
if month <= 0 || month > 12 {
return Err(glib::bool_error!(
"Can't create DateTime: Month out of range"
));
}
}
if let Some(day) = day {
if day <= 0 || day > 31 {
return Err(glib::bool_error!("Can't create DateTime: Day out of range"));
}
}
if let Some(hour) = hour {
if hour < 0 || hour >= 24 {
return Err(glib::bool_error!(
"Can't create DateTime: Hour out of range"
));
}
}
if let Some(minute) = minute {
if minute < 0 || minute >= 60 {
return Err(glib::bool_error!(
"Can't create DateTime: Minute out of range"
));
}
}
if let Some(seconds) = seconds {
if seconds < 0.0 || seconds >= 60.0 {
return Err(glib::bool_error!(
"Can't create DateTime: Seconds out of range"
));
}
}
if let Some(tzoffset) = tzoffset {
if tzoffset < -12.0 || tzoffset > 12.0 {
return Err(glib::bool_error!(
"Can't create DateTime: Timezone offset out of range"
));
}
}
if day.is_some() && month.is_none() {
return Err(glib::bool_error!(
"Can't create DateTime: Need to provide month if providing day"
));
}
if hour.is_some() && day.is_none() {
return Err(glib::bool_error!(
"Can't create DateTime: Need to provide day if providing hour"
));
}
if hour.is_none() && minute.is_some() {
return Err(glib::bool_error!(
"Can't create DateTime: Need to provide both hour and minute or neither"
));
}
if minute.is_some() && hour.is_none() {
return Err(glib::bool_error!(
"Can't create DateTime: Need to provide both hour and minute or neither"
));
}
if (seconds.is_some() || tzoffset.is_some()) && (hour.is_none() || minute.is_none()) {
return Err(glib::bool_error!("Can't create DateTime: Need to provide hour and minute if providing seconds or timezone offset"));
}
Ok(())
}
impl DateTime {
#[doc(alias = "gst_date_time_new")]
pub fn new(
tzoffset: impl Into<Option<f32>>,
year: impl Into<i32>,
month: impl Into<Option<i32>>,
day: impl Into<Option<i32>>,
hour: impl Into<Option<i32>>,
minute: impl Into<Option<i32>>,
seconds: impl Into<Option<f64>>,
) -> Result<DateTime, glib::BoolError> {
assert_initialized_main_thread!();
let tzoffset = tzoffset.into();
let year = year.into();
let month = month.into();
let day = day.into();
let hour = hour.into();
let minute = minute.into();
let seconds = seconds.into();
validate(tzoffset, year, month, day, hour, minute, seconds)?;
unsafe {
Option::<_>::from_glib_full(ffi::gst_date_time_new(
tzoffset.unwrap_or(0.0),
year,
month.unwrap_or(-1),
day.unwrap_or(-1),
hour.unwrap_or(-1),
minute.unwrap_or(-1),
seconds.unwrap_or(-1.0),
))
.ok_or_else(|| glib::bool_error!("Can't create DateTime"))
}
}
#[doc(alias = "gst_date_time_new_local_time")]
pub fn from_local_time(
year: impl Into<i32>,
month: impl Into<Option<i32>>,
day: impl Into<Option<i32>>,
hour: impl Into<Option<i32>>,
minute: impl Into<Option<i32>>,
seconds: impl Into<Option<f64>>,
) -> Result<DateTime, glib::BoolError> {
assert_initialized_main_thread!();
let year = year.into();
let month = month.into();
let day = day.into();
let hour = hour.into();
let minute = minute.into();
let seconds = seconds.into();
validate(None, year, month, day, hour, minute, seconds)?;
unsafe {
Option::<_>::from_glib_full(ffi::gst_date_time_new_local_time(
year,
month.unwrap_or(-1),
day.unwrap_or(-1),
hour.unwrap_or(-1),
minute.unwrap_or(-1),
seconds.unwrap_or(-1.0),
))
.ok_or_else(|| glib::bool_error!("Can't create DateTime"))
}
}
#[doc(alias = "gst_date_time_new_y")]
pub fn from_y(year: i32) -> Result<DateTime, glib::BoolError> {
assert_initialized_main_thread!();
validate(None, year, None, None, None, None, None)?;
unsafe {
Option::<_>::from_glib_full(ffi::gst_date_time_new_y(year))
.ok_or_else(|| glib::bool_error!("Can't create DateTime"))
}
}
#[doc(alias = "gst_date_time_new_ym")]
pub fn from_ym(year: i32, month: i32) -> Result<DateTime, glib::BoolError> {
assert_initialized_main_thread!();
validate(None, year, Some(month), None, None, None, None)?;
unsafe {
Option::<_>::from_glib_full(ffi::gst_date_time_new_ym(year, month))
.ok_or_else(|| glib::bool_error!("Can't create DateTime"))
}
}
#[doc(alias = "gst_date_time_new_ymd")]
pub fn from_ymd(year: i32, month: i32, day: i32) -> Result<DateTime, glib::BoolError> {
assert_initialized_main_thread!();
validate(None, year, Some(month), Some(day), None, None, None)?;
unsafe {
Option::<_>::from_glib_full(ffi::gst_date_time_new_ymd(year, month, day))
.ok_or_else(|| glib::bool_error!("Can't create DateTime"))
}
}
#[doc(alias = "get_day")]
#[doc(alias = "gst_date_time_get_day")]
pub fn day(&self) -> Option<i32> {
if !self.has_day() {
return None;
}
unsafe { Some(ffi::gst_date_time_get_day(self.to_glib_none().0)) }
}
#[doc(alias = "get_hour")]
#[doc(alias = "gst_date_time_get_hour")]
pub fn hour(&self) -> Option<i32> {
if !self.has_time() {
return None;
}
unsafe { Some(ffi::gst_date_time_get_hour(self.to_glib_none().0)) }
}
#[doc(alias = "get_microsecond")]
#[doc(alias = "gst_date_time_get_microsecond")]
pub fn microsecond(&self) -> Option<i32> {
if !self.has_second() {
return None;
}
unsafe { Some(ffi::gst_date_time_get_microsecond(self.to_glib_none().0)) }
}
#[doc(alias = "get_minute")]
#[doc(alias = "gst_date_time_get_minute")]
pub fn minute(&self) -> Option<i32> {
if !self.has_time() {
return None;
}
unsafe { Some(ffi::gst_date_time_get_minute(self.to_glib_none().0)) }
}
#[doc(alias = "get_month")]
#[doc(alias = "gst_date_time_get_month")]
pub fn month(&self) -> Option<i32> {
if !self.has_month() {
return None;
}
unsafe { Some(ffi::gst_date_time_get_month(self.to_glib_none().0)) }
}
#[doc(alias = "get_second")]
#[doc(alias = "gst_date_time_get_second")]
pub fn second(&self) -> Option<i32> {
if !self.has_second() {
return None;
}
unsafe { Some(ffi::gst_date_time_get_second(self.to_glib_none().0)) }
}
#[doc(alias = "get_time_zone_offset")]
#[doc(alias = "gst_date_time_get_time_zone_offset")]
pub fn time_zone_offset(&self) -> Option<f32> {
if !self.has_time() {
return None;
}
unsafe {
Some(ffi::gst_date_time_get_time_zone_offset(
self.to_glib_none().0,
))
}
}
pub fn to_utc(&self) -> Result<DateTime, glib::BoolError> {
if !self.has_time() {
return Ok(self.clone());
}
assert!(self.has_year() && self.has_month() && self.has_day() && self.has_time());
if self.has_second() {
self.to_g_date_time()
.and_then(|d| d.to_utc())
.map(|d| d.into())
} else {
DateTime::new(
self.time_zone_offset(),
self.year(),
self.month(),
self.day(),
self.hour(),
self.minute(),
Some(0.0),
)
.and_then(|d| d.to_g_date_time())
.and_then(|d| d.to_utc())
.and_then(|d| {
DateTime::new(
None, d.year(),
Some(d.month()),
Some(d.day_of_month()),
Some(d.hour()),
Some(d.minute()),
None, )
})
}
}
}
impl cmp::PartialOrd for DateTime {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
#[inline]
#[allow(clippy::unnecessary_wraps)]
#[doc(alias = "get_cmp")]
fn cmp(delta: i32) -> Option<cmp::Ordering> {
skip_assert_initialized!();
Some(delta.cmp(&0))
}
if !(self.has_year() && other.has_year()) {
return None;
}
let (self_norm, other_norm) = if self.has_time() && other.has_time() {
(self.to_utc().ok()?, other.to_utc().ok()?)
} else {
(self.clone(), other.clone())
};
let year_delta = self_norm.year() - other_norm.year();
if year_delta != 0 {
return cmp(year_delta);
}
if !self.has_month() && !other.has_month() {
return cmp(year_delta);
}
if !(self.has_month() && other.has_month()) {
return None;
}
let month_delta = self_norm.month().unwrap() - other_norm.month().unwrap();
if month_delta != 0 {
return cmp(month_delta);
}
if !self.has_day() && !other.has_day() {
return Some(cmp::Ordering::Equal);
}
if !(self.has_day() && other.has_day()) {
return None;
}
let day_delta = self_norm.day().unwrap() - other_norm.day().unwrap();
if day_delta != 0 {
return cmp(day_delta);
}
if !self.has_time() && !other.has_time() {
return Some(cmp::Ordering::Equal);
}
if !(self.has_time() && other.has_time()) {
return None;
}
let hour_delta = self_norm.hour().unwrap() - other_norm.hour().unwrap();
if hour_delta != 0 {
return cmp(hour_delta);
}
let minute_delta = self_norm.minute().unwrap() - other_norm.minute().unwrap();
if minute_delta != 0 {
return cmp(minute_delta);
}
if !self.has_second() && !other.has_second() {
return Some(cmp::Ordering::Equal);
}
if !(self.has_second() && other.has_second()) {
return None;
}
let second_delta = self_norm.second().unwrap() - other_norm.second().unwrap();
if second_delta != 0 {
return cmp(second_delta);
}
cmp(self_norm.microsecond().unwrap() - other_norm.microsecond().unwrap())
}
}
impl cmp::PartialEq for DateTime {
fn eq(&self, other: &Self) -> bool {
self.partial_cmp(other)
.map_or_else(|| false, |cmp| cmp == cmp::Ordering::Equal)
}
}
impl fmt::Debug for DateTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut debug_struct = f.debug_struct("DateTime");
if self.has_year() {
debug_struct.field("year", &self.year());
}
if self.has_month() {
debug_struct.field("month", &self.month());
}
if self.has_day() {
debug_struct.field("day", &self.day());
}
if self.has_time() {
debug_struct.field("hour", &self.hour());
debug_struct.field("minute", &self.minute());
if self.has_second() {
debug_struct.field("second", &self.second());
debug_struct.field("microsecond", &self.microsecond());
}
debug_struct.field("tz_offset", &self.time_zone_offset());
}
debug_struct.finish()
}
}
impl fmt::Display for DateTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(
self.to_iso8601_string()
.unwrap_or_else(|_| "None".into())
.as_str(),
)
}
}
impl<'a> From<&'a glib::DateTime> for DateTime {
fn from(v: &'a glib::DateTime) -> DateTime {
skip_assert_initialized!();
DateTime::from_g_date_time(v.clone())
}
}
impl From<glib::DateTime> for DateTime {
fn from(v: glib::DateTime) -> DateTime {
skip_assert_initialized!();
DateTime::from_g_date_time(v)
}
}
impl<'a> TryFrom<&'a DateTime> for glib::DateTime {
type Error = glib::BoolError;
fn try_from(v: &'a DateTime) -> Result<glib::DateTime, glib::BoolError> {
skip_assert_initialized!();
v.to_g_date_time()
}
}
impl TryFrom<DateTime> for glib::DateTime {
type Error = glib::BoolError;
fn try_from(v: DateTime) -> Result<glib::DateTime, glib::BoolError> {
skip_assert_initialized!();
v.to_g_date_time()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[allow(clippy::cognitive_complexity)]
#[test]
fn test_to_utc() {
crate::init().unwrap();
let utc_date_time = DateTime::new(2f32, 2019, 8, 20, 20, 9, 42.123_456f64)
.unwrap()
.to_utc()
.unwrap();
assert_eq!(utc_date_time.year(), 2019);
assert_eq!(utc_date_time.month().unwrap(), 8);
assert_eq!(utc_date_time.day().unwrap(), 20);
assert_eq!(utc_date_time.hour().unwrap(), 18);
assert_eq!(utc_date_time.minute().unwrap(), 9);
assert_eq!(utc_date_time.second().unwrap(), 42);
assert_eq!(utc_date_time.microsecond().unwrap(), 123_456);
let utc_date_time = DateTime::new(2f32, 2019, 1, 1, 0, 0, 42.123_456f64)
.unwrap()
.to_utc()
.unwrap();
assert_eq!(utc_date_time.year(), 2018);
assert_eq!(utc_date_time.month().unwrap(), 12);
assert_eq!(utc_date_time.day().unwrap(), 31);
assert_eq!(utc_date_time.hour().unwrap(), 22);
assert_eq!(utc_date_time.minute().unwrap(), 0);
assert_eq!(utc_date_time.second().unwrap(), 42);
assert_eq!(utc_date_time.microsecond().unwrap(), 123_456);
let utc_date_time = DateTime::from_ymd(2019, 1, 1).unwrap().to_utc().unwrap();
assert_eq!(utc_date_time.year(), 2019);
assert_eq!(utc_date_time.month().unwrap(), 1);
assert_eq!(utc_date_time.day().unwrap(), 1);
assert!(!utc_date_time.has_time());
assert!(!utc_date_time.has_second());
let utc_date_time = DateTime::new(2f32, 2018, 5, 28, 16, 6, None)
.unwrap()
.to_utc()
.unwrap();
assert_eq!(utc_date_time.year(), 2018);
assert_eq!(utc_date_time.month().unwrap(), 5);
assert_eq!(utc_date_time.day().unwrap(), 28);
assert_eq!(utc_date_time.hour().unwrap(), 14);
assert_eq!(utc_date_time.minute().unwrap(), 6);
assert!(!utc_date_time.has_second());
}
#[test]
fn test_partial_ord() {
crate::init().unwrap();
assert!(
DateTime::new(2f32, 2020, 8, 20, 19, 43, 42.123_456f64).unwrap()
> DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
);
assert!(
DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
< DateTime::new(2f32, 2019, 9, 19, 19, 43, 42.123_456f64).unwrap()
);
assert!(
DateTime::new(2f32, 2019, 8, 21, 19, 43, 42.123_456f64).unwrap()
> DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
);
assert!(
DateTime::new(2f32, 2019, 8, 20, 19, 44, 42.123_456f64).unwrap()
> DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
);
assert!(
DateTime::new(2f32, 2019, 8, 20, 19, 43, 44.123_456f64).unwrap()
> DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
);
assert!(
DateTime::new(2f32, 2019, 8, 20, 19, 43, 43.123_456f64).unwrap()
> DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
);
assert!(
DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_457f64).unwrap()
> DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
);
assert!(
DateTime::new(1f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
> DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
);
assert!(
DateTime::new(2f32, 2019, 1, 1, 0, 0, 0f64).unwrap()
< DateTime::new(1f32, 2018, 12, 31, 23, 59, 0f64).unwrap()
);
assert!(
DateTime::from_ymd(2020, 8, 20).unwrap() > DateTime::from_ymd(2019, 8, 20).unwrap()
);
assert!(
DateTime::from_ymd(2019, 9, 20).unwrap() > DateTime::from_ymd(2019, 8, 20).unwrap()
);
assert!(
DateTime::from_ymd(2019, 8, 21).unwrap() > DateTime::from_ymd(2019, 8, 20).unwrap()
);
assert!(DateTime::from_ym(2020, 8).unwrap() > DateTime::from_ym(2019, 8).unwrap());
assert!(DateTime::from_ym(2019, 9).unwrap() > DateTime::from_ym(2019, 8).unwrap());
assert!(DateTime::from_ym(2019, 9).unwrap() > DateTime::from_ymd(2019, 8, 20).unwrap());
assert!(DateTime::from_y(2020).unwrap() > DateTime::from_y(2019).unwrap());
assert!(DateTime::from_ym(2020, 1).unwrap() > DateTime::from_y(2019).unwrap());
assert!(
DateTime::new(2f32, 2019, 8, 20, 19, 43, 44.123_456f64).unwrap()
< DateTime::from_ymd(2020, 8, 20).unwrap()
);
assert!(
DateTime::from_ymd(2020, 8, 20).unwrap()
> DateTime::new(2f32, 2019, 8, 20, 19, 43, 44.123_456f64).unwrap()
);
assert!(
DateTime::from_ymd(2020, 1, 1).unwrap()
> DateTime::new(-2f32, 2019, 12, 31, 23, 59, 0f64).unwrap()
);
assert!(DateTime::new(2f32, 2019, 8, 20, 19, 43, 44.123_456f64)
.unwrap()
.partial_cmp(&DateTime::from_ymd(2019, 8, 20).unwrap())
.is_none());
assert!(DateTime::from_ymd(2019, 8, 20)
.unwrap()
.partial_cmp(&DateTime::new(2f32, 2019, 8, 20, 19, 43, 44.123_456f64).unwrap())
.is_none());
assert!(DateTime::from_ym(2019, 1)
.unwrap()
.partial_cmp(&DateTime::from_y(2019).unwrap())
.is_none());
}
#[test]
fn test_eq() {
crate::init().unwrap();
assert_eq!(
DateTime::new(2f32, 2018, 5, 28, 16, 6, 42.123_456f64).unwrap(),
DateTime::new(2f32, 2018, 5, 28, 16, 6, 42.123_456f64).unwrap()
);
assert_eq!(
DateTime::new(2f32, 2018, 5, 28, 16, 6, 0f64).unwrap(),
DateTime::new(2f32, 2018, 5, 28, 16, 6, 0f64).unwrap()
);
assert_eq!(
DateTime::new(2f32, 2018, 5, 28, 16, 6, None).unwrap(),
DateTime::new(2f32, 2018, 5, 28, 16, 6, None).unwrap()
);
assert_eq!(
DateTime::from_ymd(2018, 5, 28).unwrap(),
DateTime::from_ymd(2018, 5, 28).unwrap()
);
assert_ne!(
DateTime::from_ymd(2018, 5, 28).unwrap(),
DateTime::new(2f32, 2018, 5, 28, 16, 6, None).unwrap()
);
assert_ne!(
DateTime::new(2f32, 2018, 5, 28, 16, 6, None).unwrap(),
DateTime::from_ym(2018, 5).unwrap()
);
assert_ne!(
DateTime::new(2f32, 2018, 5, 28, 16, 6, None).unwrap(),
DateTime::from_y(2018).unwrap()
);
}
}