2020-12-15 10:53:31 +00:00
|
|
|
// Take a look at the license at the top of the repository in the LICENSE file.
|
2017-12-01 09:21:20 +00:00
|
|
|
|
2020-12-06 10:29:12 +00:00
|
|
|
use glib::translate::*;
|
2019-08-21 09:06:03 +00:00
|
|
|
use std::cmp;
|
2020-03-22 10:31:10 +00:00
|
|
|
use std::convert;
|
2017-12-01 09:21:20 +00:00
|
|
|
use std::fmt;
|
2019-08-21 09:06:03 +00:00
|
|
|
|
2020-11-21 13:46:48 +00:00
|
|
|
use crate::DateTime;
|
2017-12-01 09:21:20 +00:00
|
|
|
|
2020-12-06 10:29:12 +00:00
|
|
|
// Validate that the given values result in a valid 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!();
|
|
|
|
|
|
|
|
// Check for valid ranges
|
|
|
|
if year <= 0 || year > 9999 {
|
2020-12-17 22:38:06 +00:00
|
|
|
return Err(glib::bool_error!(
|
2020-12-06 10:29:12 +00:00
|
|
|
"Can't create DateTime: Year out of range"
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(month) = month {
|
|
|
|
if month <= 0 || month > 12 {
|
2020-12-17 22:38:06 +00:00
|
|
|
return Err(glib::bool_error!(
|
2020-12-06 10:29:12 +00:00
|
|
|
"Can't create DateTime: Month out of range"
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(day) = day {
|
|
|
|
if day <= 0 || day > 31 {
|
2020-12-17 22:38:06 +00:00
|
|
|
return Err(glib::bool_error!("Can't create DateTime: Day out of range"));
|
2020-12-06 10:29:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(hour) = hour {
|
|
|
|
if hour < 0 || hour >= 24 {
|
2020-12-17 22:38:06 +00:00
|
|
|
return Err(glib::bool_error!(
|
2020-12-06 10:29:12 +00:00
|
|
|
"Can't create DateTime: Hour out of range"
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(minute) = minute {
|
|
|
|
if minute < 0 || minute >= 60 {
|
2020-12-17 22:38:06 +00:00
|
|
|
return Err(glib::bool_error!(
|
2020-12-06 10:29:12 +00:00
|
|
|
"Can't create DateTime: Minute out of range"
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(seconds) = seconds {
|
|
|
|
if seconds < 0.0 || seconds >= 60.0 {
|
2020-12-17 22:38:06 +00:00
|
|
|
return Err(glib::bool_error!(
|
2020-12-06 10:29:12 +00:00
|
|
|
"Can't create DateTime: Seconds out of range"
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(tzoffset) = tzoffset {
|
|
|
|
if tzoffset < -12.0 || tzoffset > 12.0 {
|
2020-12-17 22:38:06 +00:00
|
|
|
return Err(glib::bool_error!(
|
2020-12-06 10:29:12 +00:00
|
|
|
"Can't create DateTime: Timezone offset out of range"
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If day is provided, month also has to be provided
|
|
|
|
if day.is_some() && month.is_none() {
|
2020-12-17 22:38:06 +00:00
|
|
|
return Err(glib::bool_error!(
|
2020-12-06 10:29:12 +00:00
|
|
|
"Can't create DateTime: Need to provide month if providing day"
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
// If hour is provided, day also has to be provided
|
|
|
|
if hour.is_some() && day.is_none() {
|
2020-12-17 22:38:06 +00:00
|
|
|
return Err(glib::bool_error!(
|
2020-12-06 10:29:12 +00:00
|
|
|
"Can't create DateTime: Need to provide day if providing hour"
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
// If minutes are provided, hours also need to be provided and the other way around
|
|
|
|
if hour.is_none() && minute.is_some() {
|
2020-12-17 22:38:06 +00:00
|
|
|
return Err(glib::bool_error!(
|
2020-12-06 10:29:12 +00:00
|
|
|
"Can't create DateTime: Need to provide both hour and minute or neither"
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
if minute.is_some() && hour.is_none() {
|
2020-12-17 22:38:06 +00:00
|
|
|
return Err(glib::bool_error!(
|
2020-12-06 10:29:12 +00:00
|
|
|
"Can't create DateTime: Need to provide both hour and minute or neither"
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
// If seconds or tzoffset are provided then also hours and minutes must be provided
|
|
|
|
if (seconds.is_some() || tzoffset.is_some()) && (hour.is_none() || minute.is_none()) {
|
2020-12-17 22:38:06 +00:00
|
|
|
return Err(glib::bool_error!("Can't create DateTime: Need to provide hour and minute if providing seconds or timezone offset"));
|
2020-12-06 10:29:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-08-21 09:06:03 +00:00
|
|
|
impl DateTime {
|
2020-12-06 10:29:12 +00:00
|
|
|
pub fn new<
|
|
|
|
TZ: Into<Option<f32>>,
|
|
|
|
Y: Into<i32>,
|
|
|
|
MO: Into<Option<i32>>,
|
|
|
|
D: Into<Option<i32>>,
|
|
|
|
H: Into<Option<i32>>,
|
|
|
|
MI: Into<Option<i32>>,
|
|
|
|
S: Into<Option<f64>>,
|
|
|
|
>(
|
|
|
|
tzoffset: TZ,
|
|
|
|
year: Y,
|
|
|
|
month: MO,
|
|
|
|
day: D,
|
|
|
|
hour: H,
|
|
|
|
minute: MI,
|
|
|
|
seconds: S,
|
|
|
|
) -> 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),
|
|
|
|
))
|
2020-12-17 22:38:06 +00:00
|
|
|
.ok_or_else(|| glib::bool_error!("Can't create DateTime"))
|
2020-12-06 10:29:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn new_local_time<
|
|
|
|
Y: Into<i32>,
|
|
|
|
MO: Into<Option<i32>>,
|
|
|
|
D: Into<Option<i32>>,
|
|
|
|
H: Into<Option<i32>>,
|
|
|
|
MI: Into<Option<i32>>,
|
|
|
|
S: Into<Option<f64>>,
|
|
|
|
>(
|
|
|
|
year: Y,
|
|
|
|
month: MO,
|
|
|
|
day: D,
|
|
|
|
hour: H,
|
|
|
|
minute: MI,
|
|
|
|
seconds: S,
|
|
|
|
) -> 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),
|
|
|
|
))
|
2020-12-17 22:38:06 +00:00
|
|
|
.ok_or_else(|| glib::bool_error!("Can't create DateTime"))
|
2020-12-06 10:29:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn new_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))
|
2020-12-17 22:38:06 +00:00
|
|
|
.ok_or_else(|| glib::bool_error!("Can't create DateTime"))
|
2020-12-06 10:29:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn new_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))
|
2020-12-17 22:38:06 +00:00
|
|
|
.ok_or_else(|| glib::bool_error!("Can't create DateTime"))
|
2020-12-06 10:29:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn new_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))
|
2020-12-17 22:38:06 +00:00
|
|
|
.ok_or_else(|| glib::bool_error!("Can't create DateTime"))
|
2020-12-06 10:29:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-11 19:39:50 +00:00
|
|
|
pub fn day(&self) -> Option<i32> {
|
2020-12-06 10:29:12 +00:00
|
|
|
if !self.has_day() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe { Some(ffi::gst_date_time_get_day(self.to_glib_none().0)) }
|
|
|
|
}
|
|
|
|
|
2021-04-11 19:39:50 +00:00
|
|
|
pub fn hour(&self) -> Option<i32> {
|
2020-12-06 10:29:12 +00:00
|
|
|
if !self.has_time() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe { Some(ffi::gst_date_time_get_hour(self.to_glib_none().0)) }
|
|
|
|
}
|
|
|
|
|
2021-04-11 19:39:50 +00:00
|
|
|
pub fn microsecond(&self) -> Option<i32> {
|
2020-12-06 10:29:12 +00:00
|
|
|
if !self.has_second() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe { Some(ffi::gst_date_time_get_microsecond(self.to_glib_none().0)) }
|
|
|
|
}
|
|
|
|
|
2021-04-11 19:39:50 +00:00
|
|
|
pub fn minute(&self) -> Option<i32> {
|
2020-12-06 10:29:12 +00:00
|
|
|
if !self.has_time() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe { Some(ffi::gst_date_time_get_minute(self.to_glib_none().0)) }
|
|
|
|
}
|
|
|
|
|
2021-04-11 19:39:50 +00:00
|
|
|
pub fn month(&self) -> Option<i32> {
|
2020-12-06 10:29:12 +00:00
|
|
|
if !self.has_month() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe { Some(ffi::gst_date_time_get_month(self.to_glib_none().0)) }
|
|
|
|
}
|
|
|
|
|
2021-04-11 19:39:50 +00:00
|
|
|
pub fn second(&self) -> Option<i32> {
|
2020-12-06 10:29:12 +00:00
|
|
|
if !self.has_second() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe { Some(ffi::gst_date_time_get_second(self.to_glib_none().0)) }
|
|
|
|
}
|
|
|
|
|
2021-04-11 19:39:50 +00:00
|
|
|
pub fn time_zone_offset(&self) -> Option<f32> {
|
2020-12-06 10:29:12 +00:00
|
|
|
if !self.has_time() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
Some(ffi::gst_date_time_get_time_zone_offset(
|
|
|
|
self.to_glib_none().0,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-17 19:00:42 +00:00
|
|
|
pub fn to_utc(&self) -> Result<DateTime, glib::BoolError> {
|
2019-08-21 09:06:03 +00:00
|
|
|
if !self.has_time() {
|
|
|
|
// No time => no TZ offset
|
2019-12-17 19:00:42 +00:00
|
|
|
return Ok(self.clone());
|
2019-08-21 09:06:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
assert!(self.has_year() && self.has_month() && self.has_day() && self.has_time());
|
|
|
|
|
|
|
|
// Can instantiate `gst::DateTime` without seconds using `gst::DateTime::new`
|
|
|
|
// with `-1f64` for the `second` argument
|
|
|
|
// however, the resulting instance can't be translated to `glib::DateTime`
|
|
|
|
if self.has_second() {
|
|
|
|
self.to_g_date_time()
|
2020-12-24 10:46:27 +00:00
|
|
|
.and_then(|d| d.to_utc())
|
2020-12-08 18:59:02 +00:00
|
|
|
.map(|d| d.into())
|
2019-08-21 09:06:03 +00:00
|
|
|
} else {
|
|
|
|
// It would be cheaper to build a `glib::DateTime` direcly, unfortunetaly
|
|
|
|
// this would require using `glib::TimeZone::new_offset` which is feature-gated
|
|
|
|
// to `glib/v2_58`. So we need to build a new `gst::DateTime` with `0f64`
|
|
|
|
// and then discard seconds again
|
|
|
|
DateTime::new(
|
2021-04-11 19:39:50 +00:00
|
|
|
self.time_zone_offset(),
|
|
|
|
self.year(),
|
|
|
|
self.month(),
|
|
|
|
self.day(),
|
|
|
|
self.hour(),
|
|
|
|
self.minute(),
|
2020-12-06 10:29:12 +00:00
|
|
|
Some(0.0),
|
2019-08-21 09:06:03 +00:00
|
|
|
)
|
2020-03-22 10:23:16 +00:00
|
|
|
.and_then(|d| d.to_g_date_time())
|
2020-12-24 10:46:27 +00:00
|
|
|
.and_then(|d| d.to_utc())
|
2020-03-22 10:23:16 +00:00
|
|
|
.and_then(|d| {
|
2019-08-21 09:06:03 +00:00
|
|
|
DateTime::new(
|
2020-12-06 10:29:12 +00:00
|
|
|
None, // UTC TZ offset
|
2021-04-11 19:39:50 +00:00
|
|
|
d.year(),
|
|
|
|
Some(d.month()),
|
|
|
|
Some(d.day_of_month()),
|
|
|
|
Some(d.hour()),
|
|
|
|
Some(d.minute()),
|
2020-12-06 10:29:12 +00:00
|
|
|
None, // No second
|
2019-08-21 09:06:03 +00:00
|
|
|
)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl cmp::PartialOrd for DateTime {
|
2020-04-30 16:51:41 +00:00
|
|
|
// *NOTE 1:* When comparing a partially defined [`DateTime`](struct.DateTime.html) `d1`
|
|
|
|
// such as *"2019/8/20"* with a [`DateTime`](struct.DateTime.html) with a time part `d2`
|
|
|
|
// such as *"2019/8/20 21:10"*:
|
|
|
|
//
|
|
|
|
// - `d1` includes `d2`,
|
|
|
|
// - neither `d1` < `d2` nor `d1` > `d2`,
|
|
|
|
// - and `d1` != `d2`,
|
|
|
|
//
|
|
|
|
// so we can only return `None`.
|
|
|
|
//
|
|
|
|
// This is the reason why [`DateTime`](struct.DateTime.html) neither implements
|
|
|
|
// [`Ord`](https://doc.rust-lang.org/nightly/std/cmp/trait.Ord.html)
|
|
|
|
// nor [`Eq`](https://doc.rust-lang.org/nightly/std/cmp/trait.Eq.html).
|
|
|
|
//
|
|
|
|
// *NOTE 2:* When comparing a [`DateTime`](struct.DateTime.html) `d1` without a TZ offset
|
|
|
|
// such as *"2019/8/20"* with a [`DateTime`](struct.DateTime.html) `d2` with a TZ offset
|
|
|
|
// such as *"2019/8/20 21:10 +02:00"*, we can't tell in which TZ `d1` is expressed and which
|
|
|
|
// time should be considered for an offset, therefore the two [`DateTime`s](struct.DateTime.html)
|
|
|
|
// are compared in the same TZ.
|
2019-08-21 09:06:03 +00:00
|
|
|
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
|
|
|
#[inline]
|
2021-02-09 15:20:25 +00:00
|
|
|
#[allow(clippy::unnecessary_wraps)]
|
2021-04-20 10:23:24 +00:00
|
|
|
fn cmp(delta: i32) -> Option<cmp::Ordering> {
|
2020-03-22 14:18:47 +00:00
|
|
|
skip_assert_initialized!();
|
2019-12-22 07:59:23 +00:00
|
|
|
Some(delta.cmp(&0))
|
2019-08-21 09:06:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if !(self.has_year() && other.has_year()) {
|
|
|
|
// Can't compare anything
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Normalize to UTC only if both members have time (see note 2).
|
|
|
|
let (self_norm, other_norm) = if self.has_time() && other.has_time() {
|
2019-12-17 19:00:42 +00:00
|
|
|
(self.to_utc().ok()?, other.to_utc().ok()?)
|
2019-08-21 09:06:03 +00:00
|
|
|
} else {
|
|
|
|
(self.clone(), other.clone())
|
|
|
|
};
|
|
|
|
|
2021-04-11 19:39:50 +00:00
|
|
|
let year_delta = self_norm.year() - other_norm.year();
|
2019-08-21 09:06:03 +00:00
|
|
|
if year_delta != 0 {
|
2021-04-20 10:24:17 +00:00
|
|
|
return cmp(year_delta);
|
2019-08-21 09:06:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Same year
|
|
|
|
|
|
|
|
if !self.has_month() && !other.has_month() {
|
|
|
|
// Nothing left to compare
|
2021-04-20 10:24:17 +00:00
|
|
|
return cmp(year_delta);
|
2019-08-21 09:06:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if !(self.has_month() && other.has_month()) {
|
|
|
|
// One has month, the other doesn't => can't compare (note 1)
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2021-04-11 19:39:50 +00:00
|
|
|
let month_delta = self_norm.month().unwrap() - other_norm.month().unwrap();
|
2019-08-21 09:06:03 +00:00
|
|
|
if month_delta != 0 {
|
2021-04-20 10:24:17 +00:00
|
|
|
return cmp(month_delta);
|
2019-08-21 09:06:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Same year, same month
|
|
|
|
|
|
|
|
if !self.has_day() && !other.has_day() {
|
|
|
|
// Nothing left to compare
|
|
|
|
return Some(cmp::Ordering::Equal);
|
|
|
|
}
|
|
|
|
|
|
|
|
if !(self.has_day() && other.has_day()) {
|
|
|
|
// One has day, the other doesn't => can't compare (note 1)
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2021-04-11 19:39:50 +00:00
|
|
|
let day_delta = self_norm.day().unwrap() - other_norm.day().unwrap();
|
2019-08-21 09:06:03 +00:00
|
|
|
if day_delta != 0 {
|
2021-04-20 10:24:17 +00:00
|
|
|
return cmp(day_delta);
|
2019-08-21 09:06:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Same year, same month, same day
|
|
|
|
|
|
|
|
if !self.has_time() && !other.has_time() {
|
|
|
|
// Nothing left to compare
|
|
|
|
return Some(cmp::Ordering::Equal);
|
|
|
|
}
|
|
|
|
|
|
|
|
if !(self.has_time() && other.has_time()) {
|
|
|
|
// One has time, the other doesn't => can't compare (note 1)
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2021-04-11 19:39:50 +00:00
|
|
|
let hour_delta = self_norm.hour().unwrap() - other_norm.hour().unwrap();
|
2019-08-21 09:06:03 +00:00
|
|
|
if hour_delta != 0 {
|
2021-04-20 10:24:17 +00:00
|
|
|
return cmp(hour_delta);
|
2019-08-21 09:06:03 +00:00
|
|
|
}
|
|
|
|
|
2021-04-11 19:39:50 +00:00
|
|
|
let minute_delta = self_norm.minute().unwrap() - other_norm.minute().unwrap();
|
2019-08-21 09:06:03 +00:00
|
|
|
if minute_delta != 0 {
|
2021-04-20 10:24:17 +00:00
|
|
|
return cmp(minute_delta);
|
2019-08-21 09:06:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Same year, same month, same day, same time
|
|
|
|
|
|
|
|
if !self.has_second() && !other.has_second() {
|
|
|
|
// Nothing left to compare
|
|
|
|
return Some(cmp::Ordering::Equal);
|
|
|
|
}
|
|
|
|
|
|
|
|
if !(self.has_second() && other.has_second()) {
|
|
|
|
// One has second, the other doesn't => can't compare (note 1)
|
|
|
|
return None;
|
|
|
|
}
|
2021-04-11 19:39:50 +00:00
|
|
|
let second_delta = self_norm.second().unwrap() - other_norm.second().unwrap();
|
2019-08-21 09:06:03 +00:00
|
|
|
if second_delta != 0 {
|
2021-04-20 10:24:17 +00:00
|
|
|
return cmp(second_delta);
|
2019-08-21 09:06:03 +00:00
|
|
|
}
|
|
|
|
|
2021-04-20 10:24:17 +00:00
|
|
|
cmp(self_norm.microsecond().unwrap() - other_norm.microsecond().unwrap())
|
2019-08-21 09:06:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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() {
|
2021-04-11 19:39:50 +00:00
|
|
|
debug_struct.field("year", &self.year());
|
2019-08-21 09:06:03 +00:00
|
|
|
}
|
|
|
|
if self.has_month() {
|
2021-04-11 19:39:50 +00:00
|
|
|
debug_struct.field("month", &self.month());
|
2019-08-21 09:06:03 +00:00
|
|
|
}
|
|
|
|
if self.has_day() {
|
2021-04-11 19:39:50 +00:00
|
|
|
debug_struct.field("day", &self.day());
|
2019-08-21 09:06:03 +00:00
|
|
|
}
|
|
|
|
if self.has_time() {
|
2021-04-11 19:39:50 +00:00
|
|
|
debug_struct.field("hour", &self.hour());
|
|
|
|
debug_struct.field("minute", &self.minute());
|
2019-08-21 09:06:03 +00:00
|
|
|
|
|
|
|
if self.has_second() {
|
2021-04-11 19:39:50 +00:00
|
|
|
debug_struct.field("second", &self.second());
|
|
|
|
debug_struct.field("microsecond", &self.microsecond());
|
2019-08-21 09:06:03 +00:00
|
|
|
}
|
|
|
|
|
2021-04-11 19:39:50 +00:00
|
|
|
debug_struct.field("tz_offset", &self.time_zone_offset());
|
2019-08-21 09:06:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
debug_struct.finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-01 09:21:20 +00:00
|
|
|
impl fmt::Display for DateTime {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2017-12-01 17:02:53 +00:00
|
|
|
f.write_str(
|
|
|
|
self.to_iso8601_string()
|
2019-12-17 19:00:42 +00:00
|
|
|
.unwrap_or_else(|_| "None".into())
|
2017-12-01 17:02:53 +00:00
|
|
|
.as_str(),
|
|
|
|
)
|
2017-12-01 09:21:20 +00:00
|
|
|
}
|
|
|
|
}
|
2019-08-21 09:06:03 +00:00
|
|
|
|
2020-12-08 18:59:02 +00:00
|
|
|
impl<'a> From<&'a glib::DateTime> for DateTime {
|
|
|
|
fn from(v: &'a glib::DateTime) -> DateTime {
|
2020-03-22 14:18:47 +00:00
|
|
|
skip_assert_initialized!();
|
2020-06-11 10:27:43 +00:00
|
|
|
DateTime::from_g_date_time(v)
|
2020-03-22 10:31:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-08 18:59:02 +00:00
|
|
|
impl From<glib::DateTime> for DateTime {
|
|
|
|
fn from(v: glib::DateTime) -> DateTime {
|
2020-03-22 14:18:47 +00:00
|
|
|
skip_assert_initialized!();
|
2020-06-11 10:27:43 +00:00
|
|
|
DateTime::from_g_date_time(&v)
|
2020-03-22 10:31:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> convert::TryFrom<&'a DateTime> for glib::DateTime {
|
|
|
|
type Error = glib::BoolError;
|
|
|
|
|
|
|
|
fn try_from(v: &'a DateTime) -> Result<glib::DateTime, glib::BoolError> {
|
2020-03-22 14:18:47 +00:00
|
|
|
skip_assert_initialized!();
|
2020-03-22 10:31:10 +00:00
|
|
|
v.to_g_date_time()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl convert::TryFrom<DateTime> for glib::DateTime {
|
|
|
|
type Error = glib::BoolError;
|
|
|
|
|
|
|
|
fn try_from(v: DateTime) -> Result<glib::DateTime, glib::BoolError> {
|
2020-03-22 14:18:47 +00:00
|
|
|
skip_assert_initialized!();
|
2020-03-22 10:31:10 +00:00
|
|
|
v.to_g_date_time()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-21 09:06:03 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
2019-12-22 07:59:23 +00:00
|
|
|
#[allow(clippy::cognitive_complexity)]
|
2019-08-21 09:06:03 +00:00
|
|
|
#[test]
|
|
|
|
fn test_to_utc() {
|
2020-11-21 13:46:48 +00:00
|
|
|
crate::init().unwrap();
|
2019-08-21 09:06:03 +00:00
|
|
|
|
|
|
|
// Hour offset
|
|
|
|
let utc_date_time = DateTime::new(2f32, 2019, 8, 20, 20, 9, 42.123_456f64)
|
2020-03-22 10:23:16 +00:00
|
|
|
.unwrap()
|
2019-08-21 09:06:03 +00:00
|
|
|
.to_utc()
|
|
|
|
.unwrap();
|
2021-04-11 19:39:50 +00:00
|
|
|
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);
|
2019-08-21 09:06:03 +00:00
|
|
|
|
|
|
|
// Year, month, day and hour offset
|
|
|
|
let utc_date_time = DateTime::new(2f32, 2019, 1, 1, 0, 0, 42.123_456f64)
|
2020-03-22 10:23:16 +00:00
|
|
|
.unwrap()
|
2019-08-21 09:06:03 +00:00
|
|
|
.to_utc()
|
|
|
|
.unwrap();
|
2021-04-11 19:39:50 +00:00
|
|
|
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);
|
2019-08-21 09:06:03 +00:00
|
|
|
|
|
|
|
// Date without an hour (which implies no TZ)
|
2020-03-22 10:23:16 +00:00
|
|
|
let utc_date_time = DateTime::new_ymd(2019, 1, 1).unwrap().to_utc().unwrap();
|
2021-04-11 19:39:50 +00:00
|
|
|
assert_eq!(utc_date_time.year(), 2019);
|
|
|
|
assert_eq!(utc_date_time.month().unwrap(), 1);
|
|
|
|
assert_eq!(utc_date_time.day().unwrap(), 1);
|
2019-08-21 09:06:03 +00:00
|
|
|
assert!(!utc_date_time.has_time());
|
|
|
|
assert!(!utc_date_time.has_second());
|
|
|
|
|
|
|
|
// Date without seconds
|
2020-12-06 10:29:12 +00:00
|
|
|
let utc_date_time = DateTime::new(2f32, 2018, 5, 28, 16, 6, None)
|
2020-03-22 10:23:16 +00:00
|
|
|
.unwrap()
|
2019-08-21 09:06:03 +00:00
|
|
|
.to_utc()
|
|
|
|
.unwrap();
|
2021-04-11 19:39:50 +00:00
|
|
|
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);
|
2019-08-21 09:06:03 +00:00
|
|
|
assert!(!utc_date_time.has_second());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_partial_ord() {
|
2020-11-21 13:46:48 +00:00
|
|
|
crate::init().unwrap();
|
2019-08-21 09:06:03 +00:00
|
|
|
|
|
|
|
// Different years
|
|
|
|
assert!(
|
2020-03-22 10:23:16 +00:00
|
|
|
DateTime::new(2f32, 2020, 8, 20, 19, 43, 42.123_456f64).unwrap()
|
|
|
|
> DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
|
2019-08-21 09:06:03 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
// Different months (order intentionally reversed)
|
|
|
|
assert!(
|
2020-03-22 10:23:16 +00:00
|
|
|
DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
|
|
|
|
< DateTime::new(2f32, 2019, 9, 19, 19, 43, 42.123_456f64).unwrap()
|
2019-08-21 09:06:03 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
// Different days
|
|
|
|
assert!(
|
2020-03-22 10:23:16 +00:00
|
|
|
DateTime::new(2f32, 2019, 8, 21, 19, 43, 42.123_456f64).unwrap()
|
|
|
|
> DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
|
2019-08-21 09:06:03 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
// Different hours
|
|
|
|
assert!(
|
2020-03-22 10:23:16 +00:00
|
|
|
DateTime::new(2f32, 2019, 8, 20, 19, 44, 42.123_456f64).unwrap()
|
|
|
|
> DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
|
2019-08-21 09:06:03 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
// Different minutes
|
|
|
|
assert!(
|
2020-03-22 10:23:16 +00:00
|
|
|
DateTime::new(2f32, 2019, 8, 20, 19, 43, 44.123_456f64).unwrap()
|
|
|
|
> DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
|
2019-08-21 09:06:03 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
// Different seconds
|
|
|
|
assert!(
|
2020-03-22 10:23:16 +00:00
|
|
|
DateTime::new(2f32, 2019, 8, 20, 19, 43, 43.123_456f64).unwrap()
|
|
|
|
> DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
|
2019-08-21 09:06:03 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
// Different micro-seconds
|
|
|
|
assert!(
|
2020-03-22 10:23:16 +00:00
|
|
|
DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_457f64).unwrap()
|
|
|
|
> DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
|
2019-08-21 09:06:03 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
// Different TZ offsets
|
|
|
|
assert!(
|
2020-03-22 10:23:16 +00:00
|
|
|
DateTime::new(1f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
|
|
|
|
> DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
|
2019-08-21 09:06:03 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
// TZ offset leading to year, month, day, hour offset
|
|
|
|
assert!(
|
2020-03-22 10:23:16 +00:00
|
|
|
DateTime::new(2f32, 2019, 1, 1, 0, 0, 0f64).unwrap()
|
|
|
|
< DateTime::new(1f32, 2018, 12, 31, 23, 59, 0f64).unwrap()
|
2019-08-21 09:06:03 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
// Partially defined `DateTime`
|
2020-03-22 10:23:16 +00:00
|
|
|
assert!(DateTime::new_ymd(2020, 8, 20).unwrap() > DateTime::new_ymd(2019, 8, 20).unwrap());
|
|
|
|
assert!(DateTime::new_ymd(2019, 9, 20).unwrap() > DateTime::new_ymd(2019, 8, 20).unwrap());
|
|
|
|
assert!(DateTime::new_ymd(2019, 8, 21).unwrap() > DateTime::new_ymd(2019, 8, 20).unwrap());
|
2019-08-21 09:06:03 +00:00
|
|
|
|
2020-03-22 10:23:16 +00:00
|
|
|
assert!(DateTime::new_ym(2020, 8).unwrap() > DateTime::new_ym(2019, 8).unwrap());
|
|
|
|
assert!(DateTime::new_ym(2019, 9).unwrap() > DateTime::new_ym(2019, 8).unwrap());
|
|
|
|
assert!(DateTime::new_ym(2019, 9).unwrap() > DateTime::new_ymd(2019, 8, 20).unwrap());
|
2019-08-21 09:06:03 +00:00
|
|
|
|
2020-03-22 10:23:16 +00:00
|
|
|
assert!(DateTime::new_y(2020).unwrap() > DateTime::new_y(2019).unwrap());
|
|
|
|
assert!(DateTime::new_ym(2020, 1).unwrap() > DateTime::new_y(2019).unwrap());
|
2019-08-21 09:06:03 +00:00
|
|
|
|
|
|
|
assert!(
|
2020-03-22 10:23:16 +00:00
|
|
|
DateTime::new(2f32, 2019, 8, 20, 19, 43, 44.123_456f64).unwrap()
|
|
|
|
< DateTime::new_ymd(2020, 8, 20).unwrap()
|
2019-08-21 09:06:03 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
assert!(
|
2020-03-22 10:23:16 +00:00
|
|
|
DateTime::new_ymd(2020, 8, 20).unwrap()
|
|
|
|
> DateTime::new(2f32, 2019, 8, 20, 19, 43, 44.123_456f64).unwrap()
|
2019-08-21 09:06:03 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
// Comparison occurs on the same TZ when the `DateTime` doesn't have time (note 2)
|
2020-03-22 10:23:16 +00:00
|
|
|
assert!(
|
|
|
|
DateTime::new_ymd(2020, 1, 1).unwrap()
|
|
|
|
> DateTime::new(-2f32, 2019, 12, 31, 23, 59, 0f64).unwrap()
|
|
|
|
);
|
2019-08-21 09:06:03 +00:00
|
|
|
|
|
|
|
// In the following cases, the partially defined `DateTime` is a range WRT
|
|
|
|
// the fully defined `DateTime` and this range includes the fully defined `DateTime`,
|
|
|
|
// but we can't tell if it's before or after and they are not equal (note 1)
|
|
|
|
assert!(DateTime::new(2f32, 2019, 8, 20, 19, 43, 44.123_456f64)
|
2020-03-22 10:23:16 +00:00
|
|
|
.unwrap()
|
|
|
|
.partial_cmp(&DateTime::new_ymd(2019, 8, 20).unwrap())
|
2019-08-21 09:06:03 +00:00
|
|
|
.is_none());
|
|
|
|
|
|
|
|
assert!(DateTime::new_ymd(2019, 8, 20)
|
2020-03-22 10:23:16 +00:00
|
|
|
.unwrap()
|
|
|
|
.partial_cmp(&DateTime::new(2f32, 2019, 8, 20, 19, 43, 44.123_456f64).unwrap())
|
2019-08-21 09:06:03 +00:00
|
|
|
.is_none());
|
|
|
|
|
|
|
|
assert!(DateTime::new_ym(2019, 1)
|
2020-03-22 10:23:16 +00:00
|
|
|
.unwrap()
|
|
|
|
.partial_cmp(&DateTime::new_y(2019).unwrap())
|
2019-08-21 09:06:03 +00:00
|
|
|
.is_none());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_eq() {
|
2020-11-21 13:46:48 +00:00
|
|
|
crate::init().unwrap();
|
2019-08-21 09:06:03 +00:00
|
|
|
|
|
|
|
assert_eq!(
|
2020-03-22 10:23:16 +00:00
|
|
|
DateTime::new(2f32, 2018, 5, 28, 16, 6, 42.123_456f64).unwrap(),
|
|
|
|
DateTime::new(2f32, 2018, 5, 28, 16, 6, 42.123_456f64).unwrap()
|
2019-08-21 09:06:03 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
2020-03-22 10:23:16 +00:00
|
|
|
DateTime::new(2f32, 2018, 5, 28, 16, 6, 0f64).unwrap(),
|
|
|
|
DateTime::new(2f32, 2018, 5, 28, 16, 6, 0f64).unwrap()
|
2019-08-21 09:06:03 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
2020-12-06 10:29:12 +00:00
|
|
|
DateTime::new(2f32, 2018, 5, 28, 16, 6, None).unwrap(),
|
|
|
|
DateTime::new(2f32, 2018, 5, 28, 16, 6, None).unwrap()
|
2019-08-21 09:06:03 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
2020-03-22 10:23:16 +00:00
|
|
|
DateTime::new_ymd(2018, 5, 28).unwrap(),
|
|
|
|
DateTime::new_ymd(2018, 5, 28).unwrap()
|
2019-08-21 09:06:03 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
// In the following cases, the partially defined `DateTime` is a range WRT
|
|
|
|
// the fully defined `DateTime` and this range includes the fully defined `DateTime`,
|
|
|
|
// but they are not equal (note 1)
|
|
|
|
assert_ne!(
|
2020-03-22 10:23:16 +00:00
|
|
|
DateTime::new_ymd(2018, 5, 28).unwrap(),
|
2020-12-06 10:29:12 +00:00
|
|
|
DateTime::new(2f32, 2018, 5, 28, 16, 6, None).unwrap()
|
2019-08-21 09:06:03 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
assert_ne!(
|
2020-12-06 10:29:12 +00:00
|
|
|
DateTime::new(2f32, 2018, 5, 28, 16, 6, None).unwrap(),
|
2020-03-22 10:23:16 +00:00
|
|
|
DateTime::new_ym(2018, 5).unwrap()
|
2019-08-21 09:06:03 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
assert_ne!(
|
2020-12-06 10:29:12 +00:00
|
|
|
DateTime::new(2f32, 2018, 5, 28, 16, 6, None).unwrap(),
|
2020-03-22 10:23:16 +00:00
|
|
|
DateTime::new_y(2018).unwrap()
|
2019-08-21 09:06:03 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|