1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{cmp, fmt};
4
5use glib::translate::*;
6
7use crate::DateTime;
8
9// Validate that the given values result in a valid DateTime
10fn validate(
11 tzoffset: Option<f32>,
12 year: i32,
13 month: Option<i32>,
14 day: Option<i32>,
15 hour: Option<i32>,
16 minute: Option<i32>,
17 seconds: Option<f64>,
18) -> Result<(), glib::BoolError> {
19 skip_assert_initialized!();
20
21 // Check for valid ranges
22 if year <= 0 || year > 9999 {
23 return Err(glib::bool_error!(
24 "Can't create DateTime: Year out of range"
25 ));
26 }
27
28 if let Some(month) = month {
29 if month <= 0 || month > 12 {
30 return Err(glib::bool_error!(
31 "Can't create DateTime: Month out of range"
32 ));
33 }
34 }
35
36 if let Some(day) = day {
37 if day <= 0 || day > 31 {
38 return Err(glib::bool_error!("Can't create DateTime: Day out of range"));
39 }
40 }
41
42 if let Some(hour) = hour {
43 if hour < 0 || hour >= 24 {
44 return Err(glib::bool_error!(
45 "Can't create DateTime: Hour out of range"
46 ));
47 }
48 }
49
50 if let Some(minute) = minute {
51 if minute < 0 || minute >= 60 {
52 return Err(glib::bool_error!(
53 "Can't create DateTime: Minute out of range"
54 ));
55 }
56 }
57
58 if let Some(seconds) = seconds {
59 if seconds < 0.0 || seconds >= 60.0 {
60 return Err(glib::bool_error!(
61 "Can't create DateTime: Seconds out of range"
62 ));
63 }
64 }
65
66 if let Some(tzoffset) = tzoffset {
67 if tzoffset < -12.0 || tzoffset > 12.0 {
68 return Err(glib::bool_error!(
69 "Can't create DateTime: Timezone offset out of range"
70 ));
71 }
72 }
73
74 // If day is provided, month also has to be provided
75 if day.is_some() && month.is_none() {
76 return Err(glib::bool_error!(
77 "Can't create DateTime: Need to provide month if providing day"
78 ));
79 }
80
81 // If hour is provided, day also has to be provided
82 if hour.is_some() && day.is_none() {
83 return Err(glib::bool_error!(
84 "Can't create DateTime: Need to provide day if providing hour"
85 ));
86 }
87
88 // If minutes are provided, hours also need to be provided and the other way around
89 if hour.is_none() && minute.is_some() {
90 return Err(glib::bool_error!(
91 "Can't create DateTime: Need to provide both hour and minute or neither"
92 ));
93 }
94
95 if minute.is_some() && hour.is_none() {
96 return Err(glib::bool_error!(
97 "Can't create DateTime: Need to provide both hour and minute or neither"
98 ));
99 }
100
101 // If seconds or tzoffset are provided then also hours and minutes must be provided
102 if (seconds.is_some() || tzoffset.is_some()) && (hour.is_none() || minute.is_none()) {
103 return Err(glib::bool_error!("Can't create DateTime: Need to provide hour and minute if providing seconds or timezone offset"));
104 }
105
106 Ok(())
107}
108
109impl DateTime {
110 #[doc(alias = "gst_date_time_new")]
111 pub fn new(
112 tzoffset: impl Into<Option<f32>>,
113 year: impl Into<i32>,
114 month: impl Into<Option<i32>>,
115 day: impl Into<Option<i32>>,
116 hour: impl Into<Option<i32>>,
117 minute: impl Into<Option<i32>>,
118 seconds: impl Into<Option<f64>>,
119 ) -> Result<DateTime, glib::BoolError> {
120 assert_initialized_main_thread!();
121
122 let tzoffset = tzoffset.into();
123 let year = year.into();
124 let month = month.into();
125 let day = day.into();
126 let hour = hour.into();
127 let minute = minute.into();
128 let seconds = seconds.into();
129
130 validate(tzoffset, year, month, day, hour, minute, seconds)?;
131
132 unsafe {
133 Option::<_>::from_glib_full(ffi::gst_date_time_new(
134 tzoffset.unwrap_or(0.0),
135 year,
136 month.unwrap_or(-1),
137 day.unwrap_or(-1),
138 hour.unwrap_or(-1),
139 minute.unwrap_or(-1),
140 seconds.unwrap_or(-1.0),
141 ))
142 .ok_or_else(|| glib::bool_error!("Can't create DateTime"))
143 }
144 }
145
146 #[doc(alias = "gst_date_time_new_local_time")]
147 pub fn from_local_time(
148 year: impl Into<i32>,
149 month: impl Into<Option<i32>>,
150 day: impl Into<Option<i32>>,
151 hour: impl Into<Option<i32>>,
152 minute: impl Into<Option<i32>>,
153 seconds: impl Into<Option<f64>>,
154 ) -> Result<DateTime, glib::BoolError> {
155 assert_initialized_main_thread!();
156
157 let year = year.into();
158 let month = month.into();
159 let day = day.into();
160 let hour = hour.into();
161 let minute = minute.into();
162 let seconds = seconds.into();
163
164 validate(None, year, month, day, hour, minute, seconds)?;
165
166 unsafe {
167 Option::<_>::from_glib_full(ffi::gst_date_time_new_local_time(
168 year,
169 month.unwrap_or(-1),
170 day.unwrap_or(-1),
171 hour.unwrap_or(-1),
172 minute.unwrap_or(-1),
173 seconds.unwrap_or(-1.0),
174 ))
175 .ok_or_else(|| glib::bool_error!("Can't create DateTime"))
176 }
177 }
178
179 #[doc(alias = "gst_date_time_new_y")]
180 pub fn from_y(year: i32) -> Result<DateTime, glib::BoolError> {
181 assert_initialized_main_thread!();
182
183 validate(None, year, None, None, None, None, None)?;
184
185 unsafe {
186 Option::<_>::from_glib_full(ffi::gst_date_time_new_y(year))
187 .ok_or_else(|| glib::bool_error!("Can't create DateTime"))
188 }
189 }
190
191 #[doc(alias = "gst_date_time_new_ym")]
192 pub fn from_ym(year: i32, month: i32) -> Result<DateTime, glib::BoolError> {
193 assert_initialized_main_thread!();
194
195 validate(None, year, Some(month), None, None, None, None)?;
196
197 unsafe {
198 Option::<_>::from_glib_full(ffi::gst_date_time_new_ym(year, month))
199 .ok_or_else(|| glib::bool_error!("Can't create DateTime"))
200 }
201 }
202
203 #[doc(alias = "gst_date_time_new_ymd")]
204 pub fn from_ymd(year: i32, month: i32, day: i32) -> Result<DateTime, glib::BoolError> {
205 assert_initialized_main_thread!();
206
207 validate(None, year, Some(month), Some(day), None, None, None)?;
208
209 unsafe {
210 Option::<_>::from_glib_full(ffi::gst_date_time_new_ymd(year, month, day))
211 .ok_or_else(|| glib::bool_error!("Can't create DateTime"))
212 }
213 }
214
215 #[doc(alias = "get_day")]
216 #[doc(alias = "gst_date_time_get_day")]
217 pub fn day(&self) -> Option<i32> {
218 if !self.has_day() {
219 return None;
220 }
221
222 unsafe { Some(ffi::gst_date_time_get_day(self.to_glib_none().0)) }
223 }
224
225 #[doc(alias = "get_hour")]
226 #[doc(alias = "gst_date_time_get_hour")]
227 pub fn hour(&self) -> Option<i32> {
228 if !self.has_time() {
229 return None;
230 }
231
232 unsafe { Some(ffi::gst_date_time_get_hour(self.to_glib_none().0)) }
233 }
234
235 #[doc(alias = "get_microsecond")]
236 #[doc(alias = "gst_date_time_get_microsecond")]
237 pub fn microsecond(&self) -> Option<i32> {
238 if !self.has_second() {
239 return None;
240 }
241
242 unsafe { Some(ffi::gst_date_time_get_microsecond(self.to_glib_none().0)) }
243 }
244
245 #[doc(alias = "get_minute")]
246 #[doc(alias = "gst_date_time_get_minute")]
247 pub fn minute(&self) -> Option<i32> {
248 if !self.has_time() {
249 return None;
250 }
251
252 unsafe { Some(ffi::gst_date_time_get_minute(self.to_glib_none().0)) }
253 }
254
255 #[doc(alias = "get_month")]
256 #[doc(alias = "gst_date_time_get_month")]
257 pub fn month(&self) -> Option<i32> {
258 if !self.has_month() {
259 return None;
260 }
261
262 unsafe { Some(ffi::gst_date_time_get_month(self.to_glib_none().0)) }
263 }
264
265 #[doc(alias = "get_second")]
266 #[doc(alias = "gst_date_time_get_second")]
267 pub fn second(&self) -> Option<i32> {
268 if !self.has_second() {
269 return None;
270 }
271
272 unsafe { Some(ffi::gst_date_time_get_second(self.to_glib_none().0)) }
273 }
274
275 #[doc(alias = "get_time_zone_offset")]
276 #[doc(alias = "gst_date_time_get_time_zone_offset")]
277 pub fn time_zone_offset(&self) -> Option<f32> {
278 if !self.has_time() {
279 return None;
280 }
281
282 unsafe {
283 Some(ffi::gst_date_time_get_time_zone_offset(
284 self.to_glib_none().0,
285 ))
286 }
287 }
288
289 pub fn to_utc(&self) -> Result<DateTime, glib::BoolError> {
290 if !self.has_time() {
291 // No time => no TZ offset
292 return Ok(self.clone());
293 }
294
295 assert!(self.has_year() && self.has_month() && self.has_day() && self.has_time());
296
297 // Can instantiate `gst::DateTime` without seconds using `gst::DateTime::new`
298 // with `-1f64` for the `second` argument
299 // however, the resulting instance can't be translated to `glib::DateTime`
300 if self.has_second() {
301 self.to_g_date_time()
302 .and_then(|d| d.to_utc())
303 .map(|d| d.into())
304 } else {
305 // It would be cheaper to build a `glib::DateTime` directly, unfortunetaly
306 // this would require using `glib::TimeZone::new_offset` which is feature-gated
307 // to `glib/v2_58`. So we need to build a new `gst::DateTime` with `0f64`
308 // and then discard seconds again
309 DateTime::new(
310 self.time_zone_offset(),
311 self.year(),
312 self.month(),
313 self.day(),
314 self.hour(),
315 self.minute(),
316 Some(0.0),
317 )
318 .and_then(|d| d.to_g_date_time())
319 .and_then(|d| d.to_utc())
320 .and_then(|d| {
321 DateTime::new(
322 None, // UTC TZ offset
323 d.year(),
324 Some(d.month()),
325 Some(d.day_of_month()),
326 Some(d.hour()),
327 Some(d.minute()),
328 None, // No second
329 )
330 })
331 }
332 }
333}
334
335impl cmp::PartialOrd for DateTime {
336 // *NOTE 1:* When comparing a partially defined [`DateTime`](struct.DateTime.html) `d1`
337 // such as *"2019/8/20"* with a [`DateTime`](struct.DateTime.html) with a time part `d2`
338 // such as *"2019/8/20 21:10"*:
339 //
340 // - `d1` includes `d2`,
341 // - neither `d1` < `d2` nor `d1` > `d2`,
342 // - and `d1` != `d2`,
343 //
344 // so we can only return `None`.
345 //
346 // This is the reason why [`DateTime`](struct.DateTime.html) neither implements
347 // [`Ord`](https://doc.rust-lang.org/nightly/std/cmp/trait.Ord.html)
348 // nor [`Eq`](https://doc.rust-lang.org/nightly/std/cmp/trait.Eq.html).
349 //
350 // *NOTE 2:* When comparing a [`DateTime`](struct.DateTime.html) `d1` without a TZ offset
351 // such as *"2019/8/20"* with a [`DateTime`](struct.DateTime.html) `d2` with a TZ offset
352 // such as *"2019/8/20 21:10 +02:00"*, we can't tell in which TZ `d1` is expressed and which
353 // time should be considered for an offset, therefore the two [`DateTime`s](struct.DateTime.html)
354 // are compared in the same TZ.
355 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
356 #[inline]
357 #[allow(clippy::unnecessary_wraps)]
358 #[doc(alias = "get_cmp")]
359 fn cmp(delta: i32) -> Option<cmp::Ordering> {
360 skip_assert_initialized!();
361 Some(delta.cmp(&0))
362 }
363
364 if !(self.has_year() && other.has_year()) {
365 // Can't compare anything
366 return None;
367 }
368
369 // Normalize to UTC only if both members have time (see note 2).
370 let (self_norm, other_norm) = if self.has_time() && other.has_time() {
371 (self.to_utc().ok()?, other.to_utc().ok()?)
372 } else {
373 (self.clone(), other.clone())
374 };
375
376 let year_delta = self_norm.year() - other_norm.year();
377 if year_delta != 0 {
378 return cmp(year_delta);
379 }
380
381 // Same year
382
383 if !self.has_month() && !other.has_month() {
384 // Nothing left to compare
385 return cmp(year_delta);
386 }
387
388 if !(self.has_month() && other.has_month()) {
389 // One has month, the other doesn't => can't compare (note 1)
390 return None;
391 }
392
393 let month_delta = self_norm.month().unwrap() - other_norm.month().unwrap();
394 if month_delta != 0 {
395 return cmp(month_delta);
396 }
397
398 // Same year, same month
399
400 if !self.has_day() && !other.has_day() {
401 // Nothing left to compare
402 return Some(cmp::Ordering::Equal);
403 }
404
405 if !(self.has_day() && other.has_day()) {
406 // One has day, the other doesn't => can't compare (note 1)
407 return None;
408 }
409
410 let day_delta = self_norm.day().unwrap() - other_norm.day().unwrap();
411 if day_delta != 0 {
412 return cmp(day_delta);
413 }
414
415 // Same year, same month, same day
416
417 if !self.has_time() && !other.has_time() {
418 // Nothing left to compare
419 return Some(cmp::Ordering::Equal);
420 }
421
422 if !(self.has_time() && other.has_time()) {
423 // One has time, the other doesn't => can't compare (note 1)
424 return None;
425 }
426
427 let hour_delta = self_norm.hour().unwrap() - other_norm.hour().unwrap();
428 if hour_delta != 0 {
429 return cmp(hour_delta);
430 }
431
432 let minute_delta = self_norm.minute().unwrap() - other_norm.minute().unwrap();
433 if minute_delta != 0 {
434 return cmp(minute_delta);
435 }
436
437 // Same year, same month, same day, same time
438
439 if !self.has_second() && !other.has_second() {
440 // Nothing left to compare
441 return Some(cmp::Ordering::Equal);
442 }
443
444 if !(self.has_second() && other.has_second()) {
445 // One has second, the other doesn't => can't compare (note 1)
446 return None;
447 }
448 let second_delta = self_norm.second().unwrap() - other_norm.second().unwrap();
449 if second_delta != 0 {
450 return cmp(second_delta);
451 }
452
453 cmp(self_norm.microsecond().unwrap() - other_norm.microsecond().unwrap())
454 }
455}
456
457impl cmp::PartialEq for DateTime {
458 fn eq(&self, other: &Self) -> bool {
459 self.partial_cmp(other)
460 .map_or_else(|| false, |cmp: Ordering| cmp == cmp::Ordering::Equal)
461 }
462}
463
464impl fmt::Debug for DateTime {
465 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
466 let mut debug_struct = f.debug_struct("DateTime");
467 if self.has_year() {
468 debug_struct.field("year", &self.year());
469 }
470 if self.has_month() {
471 debug_struct.field("month", &self.month());
472 }
473 if self.has_day() {
474 debug_struct.field("day", &self.day());
475 }
476 if self.has_time() {
477 debug_struct.field("hour", &self.hour());
478 debug_struct.field("minute", &self.minute());
479
480 if self.has_second() {
481 debug_struct.field("second", &self.second());
482 debug_struct.field("microsecond", &self.microsecond());
483 }
484
485 debug_struct.field("tz_offset", &self.time_zone_offset());
486 }
487
488 debug_struct.finish()
489 }
490}
491
492impl fmt::Display for DateTime {
493 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
494 f.write_str(
495 self.to_iso8601_string()
496 .unwrap_or_else(|_| "None".into())
497 .as_str(),
498 )
499 }
500}
501
502impl<'a> From<&'a glib::DateTime> for DateTime {
503 fn from(v: &'a glib::DateTime) -> DateTime {
504 skip_assert_initialized!();
505 DateTime::from_g_date_time(dt:v.clone())
506 }
507}
508
509impl From<glib::DateTime> for DateTime {
510 fn from(v: glib::DateTime) -> DateTime {
511 skip_assert_initialized!();
512 DateTime::from_g_date_time(dt:v)
513 }
514}
515
516impl<'a> TryFrom<&'a DateTime> for glib::DateTime {
517 type Error = glib::BoolError;
518
519 fn try_from(v: &'a DateTime) -> Result<glib::DateTime, glib::BoolError> {
520 skip_assert_initialized!();
521 v.to_g_date_time()
522 }
523}
524
525impl TryFrom<DateTime> for glib::DateTime {
526 type Error = glib::BoolError;
527
528 fn try_from(v: DateTime) -> Result<glib::DateTime, glib::BoolError> {
529 skip_assert_initialized!();
530 v.to_g_date_time()
531 }
532}
533
534#[cfg(test)]
535mod tests {
536 use super::*;
537
538 #[allow(clippy::cognitive_complexity)]
539 #[test]
540 fn test_to_utc() {
541 crate::init().unwrap();
542
543 // Hour offset
544 let utc_date_time = DateTime::new(2f32, 2019, 8, 20, 20, 9, 42.123_456f64)
545 .unwrap()
546 .to_utc()
547 .unwrap();
548 assert_eq!(utc_date_time.year(), 2019);
549 assert_eq!(utc_date_time.month().unwrap(), 8);
550 assert_eq!(utc_date_time.day().unwrap(), 20);
551 assert_eq!(utc_date_time.hour().unwrap(), 18);
552 assert_eq!(utc_date_time.minute().unwrap(), 9);
553 assert_eq!(utc_date_time.second().unwrap(), 42);
554 assert_eq!(utc_date_time.microsecond().unwrap(), 123_456);
555
556 // Year, month, day and hour offset
557 let utc_date_time = DateTime::new(2f32, 2019, 1, 1, 0, 0, 42.123_456f64)
558 .unwrap()
559 .to_utc()
560 .unwrap();
561 assert_eq!(utc_date_time.year(), 2018);
562 assert_eq!(utc_date_time.month().unwrap(), 12);
563 assert_eq!(utc_date_time.day().unwrap(), 31);
564 assert_eq!(utc_date_time.hour().unwrap(), 22);
565 assert_eq!(utc_date_time.minute().unwrap(), 0);
566 assert_eq!(utc_date_time.second().unwrap(), 42);
567 assert_eq!(utc_date_time.microsecond().unwrap(), 123_456);
568
569 // Date without an hour (which implies no TZ)
570 let utc_date_time = DateTime::from_ymd(2019, 1, 1).unwrap().to_utc().unwrap();
571 assert_eq!(utc_date_time.year(), 2019);
572 assert_eq!(utc_date_time.month().unwrap(), 1);
573 assert_eq!(utc_date_time.day().unwrap(), 1);
574 assert!(!utc_date_time.has_time());
575 assert!(!utc_date_time.has_second());
576
577 // Date without seconds
578 let utc_date_time = DateTime::new(2f32, 2018, 5, 28, 16, 6, None)
579 .unwrap()
580 .to_utc()
581 .unwrap();
582 assert_eq!(utc_date_time.year(), 2018);
583 assert_eq!(utc_date_time.month().unwrap(), 5);
584 assert_eq!(utc_date_time.day().unwrap(), 28);
585 assert_eq!(utc_date_time.hour().unwrap(), 14);
586 assert_eq!(utc_date_time.minute().unwrap(), 6);
587 assert!(!utc_date_time.has_second());
588 }
589
590 #[test]
591 fn test_partial_ord() {
592 crate::init().unwrap();
593
594 // Different years
595 assert!(
596 DateTime::new(2f32, 2020, 8, 20, 19, 43, 42.123_456f64).unwrap()
597 > DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
598 );
599
600 // Different months (order intentionally reversed)
601 assert!(
602 DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
603 < DateTime::new(2f32, 2019, 9, 19, 19, 43, 42.123_456f64).unwrap()
604 );
605
606 // Different days
607 assert!(
608 DateTime::new(2f32, 2019, 8, 21, 19, 43, 42.123_456f64).unwrap()
609 > DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
610 );
611
612 // Different hours
613 assert!(
614 DateTime::new(2f32, 2019, 8, 20, 19, 44, 42.123_456f64).unwrap()
615 > DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
616 );
617
618 // Different minutes
619 assert!(
620 DateTime::new(2f32, 2019, 8, 20, 19, 43, 44.123_456f64).unwrap()
621 > DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
622 );
623
624 // Different seconds
625 assert!(
626 DateTime::new(2f32, 2019, 8, 20, 19, 43, 43.123_456f64).unwrap()
627 > DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
628 );
629
630 // Different micro-seconds
631 assert!(
632 DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_457f64).unwrap()
633 > DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
634 );
635
636 // Different TZ offsets
637 assert!(
638 DateTime::new(1f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
639 > DateTime::new(2f32, 2019, 8, 20, 19, 43, 42.123_456f64).unwrap()
640 );
641
642 // TZ offset leading to year, month, day, hour offset
643 assert!(
644 DateTime::new(2f32, 2019, 1, 1, 0, 0, 0f64).unwrap()
645 < DateTime::new(1f32, 2018, 12, 31, 23, 59, 0f64).unwrap()
646 );
647
648 // Partially defined `DateTime`
649 assert!(
650 DateTime::from_ymd(2020, 8, 20).unwrap() > DateTime::from_ymd(2019, 8, 20).unwrap()
651 );
652 assert!(
653 DateTime::from_ymd(2019, 9, 20).unwrap() > DateTime::from_ymd(2019, 8, 20).unwrap()
654 );
655 assert!(
656 DateTime::from_ymd(2019, 8, 21).unwrap() > DateTime::from_ymd(2019, 8, 20).unwrap()
657 );
658
659 assert!(DateTime::from_ym(2020, 8).unwrap() > DateTime::from_ym(2019, 8).unwrap());
660 assert!(DateTime::from_ym(2019, 9).unwrap() > DateTime::from_ym(2019, 8).unwrap());
661 assert!(DateTime::from_ym(2019, 9).unwrap() > DateTime::from_ymd(2019, 8, 20).unwrap());
662
663 assert!(DateTime::from_y(2020).unwrap() > DateTime::from_y(2019).unwrap());
664 assert!(DateTime::from_ym(2020, 1).unwrap() > DateTime::from_y(2019).unwrap());
665
666 assert!(
667 DateTime::new(2f32, 2019, 8, 20, 19, 43, 44.123_456f64).unwrap()
668 < DateTime::from_ymd(2020, 8, 20).unwrap()
669 );
670
671 assert!(
672 DateTime::from_ymd(2020, 8, 20).unwrap()
673 > DateTime::new(2f32, 2019, 8, 20, 19, 43, 44.123_456f64).unwrap()
674 );
675
676 // Comparison occurs on the same TZ when the `DateTime` doesn't have time (note 2)
677 assert!(
678 DateTime::from_ymd(2020, 1, 1).unwrap()
679 > DateTime::new(-2f32, 2019, 12, 31, 23, 59, 0f64).unwrap()
680 );
681
682 // In the following cases, the partially defined `DateTime` is a range WRT
683 // the fully defined `DateTime` and this range includes the fully defined `DateTime`,
684 // but we can't tell if it's before or after and they are not equal (note 1)
685 assert!(DateTime::new(2f32, 2019, 8, 20, 19, 43, 44.123_456f64)
686 .unwrap()
687 .partial_cmp(&DateTime::from_ymd(2019, 8, 20).unwrap())
688 .is_none());
689
690 assert!(DateTime::from_ymd(2019, 8, 20)
691 .unwrap()
692 .partial_cmp(&DateTime::new(2f32, 2019, 8, 20, 19, 43, 44.123_456f64).unwrap())
693 .is_none());
694
695 assert!(DateTime::from_ym(2019, 1)
696 .unwrap()
697 .partial_cmp(&DateTime::from_y(2019).unwrap())
698 .is_none());
699 }
700
701 #[test]
702 fn test_eq() {
703 crate::init().unwrap();
704
705 assert_eq!(
706 DateTime::new(2f32, 2018, 5, 28, 16, 6, 42.123_456f64).unwrap(),
707 DateTime::new(2f32, 2018, 5, 28, 16, 6, 42.123_456f64).unwrap()
708 );
709
710 assert_eq!(
711 DateTime::new(2f32, 2018, 5, 28, 16, 6, 0f64).unwrap(),
712 DateTime::new(2f32, 2018, 5, 28, 16, 6, 0f64).unwrap()
713 );
714
715 assert_eq!(
716 DateTime::new(2f32, 2018, 5, 28, 16, 6, None).unwrap(),
717 DateTime::new(2f32, 2018, 5, 28, 16, 6, None).unwrap()
718 );
719
720 assert_eq!(
721 DateTime::from_ymd(2018, 5, 28).unwrap(),
722 DateTime::from_ymd(2018, 5, 28).unwrap()
723 );
724
725 // In the following cases, the partially defined `DateTime` is a range WRT
726 // the fully defined `DateTime` and this range includes the fully defined `DateTime`,
727 // but they are not equal (note 1)
728 assert_ne!(
729 DateTime::from_ymd(2018, 5, 28).unwrap(),
730 DateTime::new(2f32, 2018, 5, 28, 16, 6, None).unwrap()
731 );
732
733 assert_ne!(
734 DateTime::new(2f32, 2018, 5, 28, 16, 6, None).unwrap(),
735 DateTime::from_ym(2018, 5).unwrap()
736 );
737
738 assert_ne!(
739 DateTime::new(2f32, 2018, 5, 28, 16, 6, None).unwrap(),
740 DateTime::from_y(2018).unwrap()
741 );
742 }
743}
744