sqldatetime/
interval.rs

1//! Interval implementation.
2
3use crate::common::{
4    HOURS_PER_DAY, MINUTES_PER_HOUR, MONTHS_PER_YEAR, SECONDS_PER_MINUTE, USECONDS_MAX,
5    USECONDS_PER_DAY, USECONDS_PER_HOUR, USECONDS_PER_MINUTE, USECONDS_PER_SECOND,
6};
7use crate::error::{Error, Result};
8use crate::format::{LazyFormat, NaiveDateTime};
9use crate::interval::Sign::{Negative, Positive};
10use crate::{Date, Time};
11use crate::{DateTime, Formatter};
12use std::cmp::Ordering;
13use std::convert::TryFrom;
14use std::fmt::Display;
15use std::ops::Neg;
16
17const INTERVAL_MAX_YEAR: i32 = 178_000_000;
18const INTERVAL_MAX_DAY: i32 = 100_000_000;
19
20pub(crate) const INTERVAL_MAX_MONTH: i32 = INTERVAL_MAX_YEAR * (MONTHS_PER_YEAR as i32);
21pub(crate) const INTERVAL_MAX_USECONDS: i64 = INTERVAL_MAX_DAY as i64 * USECONDS_PER_DAY;
22
23#[derive(Debug, PartialEq, Eq, Copy, Clone)]
24pub enum Sign {
25    Positive = 1,
26    Negative = -1,
27}
28
29/// `Year-Month Interval` represents the duration of a period of time,
30/// has an interval precision that includes a YEAR field or a MONTH field, or both.
31#[allow(clippy::upper_case_acronyms)]
32#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
33#[repr(transparent)]
34pub struct IntervalYM(i32);
35
36impl IntervalYM {
37    /// The smallest interval that can be represented by `IntervalYM`, i.e. `-178000000-00`.
38    pub const MIN: Self = unsafe { IntervalYM::from_ym_unchecked(178000000, 0).negate() };
39
40    /// The largest interval that can be represented by `IntervalYM`, i.e. `178000000-00`.
41    pub const MAX: Self = unsafe { IntervalYM::from_ym_unchecked(178000000, 0) };
42
43    /// The zero value of interval, i.e. `00-00`.
44    pub const ZERO: Self = IntervalYM(0);
45
46    /// Creates a `IntervalYM` from the given year and month.
47    ///
48    /// # Safety
49    /// This function is unsafe because the values are not checked for validity!
50    /// Before using it, check that the values are all correct.
51    #[inline]
52    pub const unsafe fn from_ym_unchecked(year: u32, month: u32) -> Self {
53        IntervalYM((year * MONTHS_PER_YEAR + month) as i32)
54    }
55
56    /// Creates a `IntervalYM` from the given months without checking validity.
57    ///
58    /// # Safety
59    /// This function is unsafe because the `months` is not checked for validity!
60    /// Before using it, check that the value is correct.
61    #[inline(always)]
62    pub const unsafe fn from_months_unchecked(months: i32) -> Self {
63        IntervalYM(months)
64    }
65
66    /// Creates a `IntervalYM` from the given year and month.
67    #[inline]
68    pub const fn try_from_ym(year: u32, month: u32) -> Result<Self> {
69        if year >= INTERVAL_MAX_YEAR as u32 && (year != INTERVAL_MAX_YEAR as u32 || month != 0) {
70            return Err(Error::IntervalOutOfRange);
71        }
72
73        if month >= MONTHS_PER_YEAR {
74            return Err(Error::InvalidMonth);
75        }
76
77        Ok(unsafe { IntervalYM::from_ym_unchecked(year, month) })
78    }
79
80    /// Creates a `IntervalYM` from the given months.
81    #[inline]
82    pub const fn try_from_months(months: i32) -> Result<Self> {
83        if IntervalYM::is_valid_months(months) {
84            Ok(unsafe { IntervalYM::from_months_unchecked(months) })
85        } else {
86            Err(Error::IntervalOutOfRange)
87        }
88    }
89
90    /// Checks if the given year and month are valid.
91    #[inline]
92    pub const fn is_valid_ym(year: u32, month: u32) -> bool {
93        if year >= INTERVAL_MAX_YEAR as u32 && (year != INTERVAL_MAX_YEAR as u32 || month != 0) {
94            return false;
95        }
96
97        if month >= MONTHS_PER_YEAR {
98            return false;
99        }
100
101        true
102    }
103
104    /// Check if the `months` is valid for IntervalYM
105    #[inline]
106    pub(crate) const fn is_valid_months(months: i32) -> bool {
107        months <= INTERVAL_MAX_MONTH && months >= -INTERVAL_MAX_MONTH
108    }
109
110    /// Gets the months of `IntervalYM`.
111    #[inline(always)]
112    pub const fn months(self) -> i32 {
113        self.0
114    }
115
116    /// Extracts `(sign, year, month)` from the interval.
117    #[inline]
118    pub const fn extract(self) -> (Sign, u32, u32) {
119        if self.0.is_negative() {
120            let year = -self.0 as u32 / MONTHS_PER_YEAR;
121            (Negative, year, -self.0 as u32 - year * MONTHS_PER_YEAR)
122        } else {
123            let year = self.0 as u32 / MONTHS_PER_YEAR;
124            (Positive, year, self.0 as u32 - year * MONTHS_PER_YEAR)
125        }
126    }
127
128    /// Formats `IntervalYM` by given format string.
129    #[inline]
130    pub fn format<S: AsRef<str>>(self, fmt: S) -> Result<impl Display> {
131        let fmt = Formatter::try_new(fmt)?;
132        Ok(LazyFormat::new(fmt, self))
133    }
134
135    /// Parses `IntervalYM` from given string and format.
136    #[inline]
137    pub fn parse<S1: AsRef<str>, S2: AsRef<str>>(input: S1, fmt: S2) -> Result<Self> {
138        let fmt = Formatter::try_new(fmt)?;
139        fmt.parse(input)
140    }
141
142    #[inline]
143    pub(crate) const fn negate(self) -> IntervalYM {
144        unsafe { IntervalYM::from_months_unchecked(-self.months()) }
145    }
146
147    /// `IntervalYM` adds `IntervalYM`
148    #[inline]
149    pub const fn add_interval_ym(self, interval: IntervalYM) -> Result<IntervalYM> {
150        let result = self.months().checked_add(interval.months());
151        match result {
152            Some(i) => IntervalYM::try_from_months(i),
153            None => Err(Error::IntervalOutOfRange),
154        }
155    }
156
157    /// `IntervalYM` subtracts `IntervalYM`
158    #[inline]
159    pub const fn sub_interval_ym(self, interval: IntervalYM) -> Result<IntervalYM> {
160        self.add_interval_ym(interval.negate())
161    }
162
163    /// `IntervalYM` multiplies `f64`
164    #[inline]
165    pub fn mul_f64(self, number: f64) -> Result<IntervalYM> {
166        let months = self.months() as f64;
167        let result = months * number;
168
169        if result.is_infinite() {
170            Err(Error::NumericOverflow)
171        } else if result.is_nan() {
172            Err(Error::InvalidNumber)
173        } else {
174            IntervalYM::try_from_months(result as i32)
175        }
176    }
177
178    /// `IntervalYM` divides `f64`
179    #[inline]
180    pub fn div_f64(self, number: f64) -> Result<IntervalYM> {
181        if number == 0.0 {
182            return Err(Error::DivideByZero);
183        }
184        let months = self.months() as f64;
185        let result = months / number;
186
187        if result.is_infinite() {
188            Err(Error::NumericOverflow)
189        } else if result.is_nan() {
190            Err(Error::InvalidNumber)
191        } else {
192            IntervalYM::try_from_months(result as i32)
193        }
194    }
195}
196
197impl From<IntervalYM> for NaiveDateTime {
198    #[inline]
199    fn from(interval: IntervalYM) -> Self {
200        let (sign, year, month) = interval.extract();
201        let negative = sign == Negative;
202        NaiveDateTime {
203            year: year as i32,
204            month,
205            negative,
206            ..NaiveDateTime::new()
207        }
208    }
209}
210
211impl TryFrom<NaiveDateTime> for IntervalYM {
212    type Error = Error;
213
214    #[inline]
215    fn try_from(dt: NaiveDateTime) -> Result<Self> {
216        if dt.negative {
217            Ok(-IntervalYM::try_from_ym(-dt.year as u32, dt.month)?)
218        } else {
219            IntervalYM::try_from_ym(dt.year as u32, dt.month)
220        }
221    }
222}
223
224impl Neg for IntervalYM {
225    type Output = IntervalYM;
226
227    #[inline]
228    fn neg(self) -> Self::Output {
229        self.negate()
230    }
231}
232
233impl DateTime for IntervalYM {
234    #[inline(always)]
235    fn year(&self) -> Option<i32> {
236        Some(self.months() / MONTHS_PER_YEAR as i32)
237    }
238
239    #[inline(always)]
240    fn month(&self) -> Option<i32> {
241        Some(self.months() % MONTHS_PER_YEAR as i32)
242    }
243
244    #[inline(always)]
245    fn day(&self) -> Option<i32> {
246        None
247    }
248
249    #[inline(always)]
250    fn hour(&self) -> Option<i32> {
251        None
252    }
253
254    #[inline(always)]
255    fn minute(&self) -> Option<i32> {
256        None
257    }
258
259    #[inline(always)]
260    fn second(&self) -> Option<f64> {
261        None
262    }
263
264    #[inline(always)]
265    fn date(&self) -> Option<Date> {
266        None
267    }
268}
269
270/// `Day-Time Interval` represents the duration of a period of time,
271/// has an interval precision that includes DAY, HOUR, MINUTE, SECOND, MICROSECOND.
272#[allow(clippy::upper_case_acronyms)]
273#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
274#[repr(transparent)]
275pub struct IntervalDT(i64);
276
277impl IntervalDT {
278    /// The smallest interval that can be represented by `IntervalDT`, i.e. `-100000000 00:00:00.000000`.
279    pub const MIN: Self =
280        unsafe { IntervalDT::from_dhms_unchecked(100000000, 0, 0, 0, 0).negate() };
281
282    /// The largest interval that can be represented by `IntervalDT`, i.e. `100000000 00:00:00.000000`.
283    pub const MAX: Self = unsafe { IntervalDT::from_dhms_unchecked(100000000, 0, 0, 0, 0) };
284
285    /// The zero value of interval, i.e. `0 00:00:00.000000`.
286    pub const ZERO: Self = IntervalDT(0);
287
288    /// Creates a `IntervalDT` from the given day, hour, minute, second and microsecond.
289    ///
290    /// # Safety
291    /// This function is unsafe because the values are not checked for validity!
292    /// Before using it, check that the values are all correct.
293    #[inline]
294    pub const unsafe fn from_dhms_unchecked(
295        day: u32,
296        hour: u32,
297        minute: u32,
298        sec: u32,
299        usec: u32,
300    ) -> Self {
301        let time = hour as i64 * USECONDS_PER_HOUR
302            + minute as i64 * USECONDS_PER_MINUTE
303            + sec as i64 * USECONDS_PER_SECOND
304            + usec as i64;
305        let us = day as i64 * USECONDS_PER_DAY + time;
306        IntervalDT(us)
307    }
308
309    /// Creates a `IntervalDT` from the given day, hour, minute, second and microsecond.
310    #[inline]
311    pub const fn try_from_dhms(
312        day: u32,
313        hour: u32,
314        minute: u32,
315        sec: u32,
316        usec: u32,
317    ) -> Result<Self> {
318        if day >= INTERVAL_MAX_DAY as u32
319            && (day != INTERVAL_MAX_DAY as u32 || hour != 0 || minute != 0 || sec != 0 || usec != 0)
320        {
321            return Err(Error::IntervalOutOfRange);
322        }
323
324        if hour >= HOURS_PER_DAY {
325            return Err(Error::TimeOutOfRange);
326        }
327
328        if minute >= MINUTES_PER_HOUR {
329            return Err(Error::InvalidMinute);
330        }
331
332        if sec >= SECONDS_PER_MINUTE {
333            return Err(Error::InvalidSecond);
334        }
335
336        if usec > USECONDS_MAX {
337            return Err(Error::InvalidFraction);
338        }
339
340        Ok(unsafe { IntervalDT::from_dhms_unchecked(day, hour, minute, sec, usec) })
341    }
342
343    /// Creates a `IntervalDT` from the given microseconds without checking the validity.
344    ///
345    /// # Safety
346    /// This function is unsafe because the microsecond value is not checked for validity!
347    /// Before using it, check that the value is all correct.
348    #[inline(always)]
349    pub const unsafe fn from_usecs_unchecked(usecs: i64) -> Self {
350        IntervalDT(usecs)
351    }
352
353    /// Creates a `IntervalDT` from the given microseconds.
354    #[inline]
355    pub const fn try_from_usecs(usecs: i64) -> Result<Self> {
356        if IntervalDT::is_valid_usecs(usecs) {
357            Ok(unsafe { IntervalDT::from_usecs_unchecked(usecs) })
358        } else {
359            Err(Error::IntervalOutOfRange)
360        }
361    }
362
363    /// Checks if the given day, hour, minute, second and microsecond fields are valid.
364    #[inline]
365    pub const fn is_valid(day: u32, hour: u32, minute: u32, sec: u32, usec: u32) -> bool {
366        if day >= INTERVAL_MAX_DAY as u32
367            && (day != INTERVAL_MAX_DAY as u32 || hour != 0 || minute != 0 || sec != 0 || usec != 0)
368        {
369            return false;
370        }
371
372        if hour >= HOURS_PER_DAY {
373            return false;
374        }
375
376        if minute >= MINUTES_PER_HOUR {
377            return false;
378        }
379
380        if sec >= SECONDS_PER_MINUTE {
381            return false;
382        }
383
384        if usec > USECONDS_MAX {
385            return false;
386        }
387
388        true
389    }
390
391    #[inline]
392    pub(crate) const fn is_valid_usecs(usecs: i64) -> bool {
393        usecs <= INTERVAL_MAX_USECONDS && usecs >= -INTERVAL_MAX_USECONDS
394    }
395
396    /// Gets the microseconds of `IntervalDT`.
397    #[inline(always)]
398    pub const fn usecs(self) -> i64 {
399        self.0
400    }
401
402    /// Extracts `(sign, day, hour, minute, second, microsecond)` from the interval.
403    #[inline]
404    pub const fn extract(self) -> (Sign, u32, u32, u32, u32, u32) {
405        let (sign, day, mut time) = if self.0.is_negative() {
406            let day = -self.0 / USECONDS_PER_DAY;
407            (Negative, day, -self.0 - day * USECONDS_PER_DAY)
408        } else {
409            let day = self.0 / USECONDS_PER_DAY;
410            (Positive, day, self.0 - day * USECONDS_PER_DAY)
411        };
412
413        let hour = time / USECONDS_PER_HOUR;
414        time -= hour * USECONDS_PER_HOUR;
415
416        let minute = time / USECONDS_PER_MINUTE;
417        time -= minute * USECONDS_PER_MINUTE;
418
419        let sec = time / USECONDS_PER_SECOND;
420        let usec = time - sec * USECONDS_PER_SECOND;
421
422        (
423            sign,
424            day as u32,
425            hour as u32,
426            minute as u32,
427            sec as u32,
428            usec as u32,
429        )
430    }
431
432    /// Formats `IntervalDT` by given format string.
433    #[inline]
434    pub fn format<S: AsRef<str>>(self, fmt: S) -> Result<impl Display> {
435        let fmt = Formatter::try_new(fmt)?;
436        Ok(LazyFormat::new(fmt, self))
437    }
438
439    /// Parses `IntervalDT` from given string and format.
440    #[inline]
441    pub fn parse<S1: AsRef<str>, S2: AsRef<str>>(input: S1, fmt: S2) -> Result<Self> {
442        let fmt = Formatter::try_new(fmt)?;
443        fmt.parse(input)
444    }
445
446    #[inline]
447    pub(crate) const fn negate(self) -> IntervalDT {
448        unsafe { IntervalDT::from_usecs_unchecked(-self.usecs()) }
449    }
450
451    /// `IntervalDT` adds `IntervalDT`
452    #[inline]
453    pub const fn add_interval_dt(self, interval: IntervalDT) -> Result<IntervalDT> {
454        let result = self.usecs().checked_add(interval.usecs());
455        match result {
456            Some(i) => IntervalDT::try_from_usecs(i),
457            None => Err(Error::IntervalOutOfRange),
458        }
459    }
460
461    /// `IntervalDT` subtracts `IntervalDT`
462    #[inline]
463    pub const fn sub_interval_dt(self, interval: IntervalDT) -> Result<IntervalDT> {
464        self.add_interval_dt(interval.negate())
465    }
466
467    /// `IntervalDT` multiplies `f64`
468    #[inline]
469    pub fn mul_f64(self, number: f64) -> Result<IntervalDT> {
470        let usecs = self.usecs() as f64;
471        let result = usecs * number;
472
473        if result.is_infinite() {
474            Err(Error::NumericOverflow)
475        } else if result.is_nan() {
476            Err(Error::InvalidNumber)
477        } else {
478            IntervalDT::try_from_usecs(result as i64)
479        }
480    }
481
482    /// `IntervalDT` divides `f64`
483    #[inline]
484    pub fn div_f64(self, number: f64) -> Result<IntervalDT> {
485        if number == 0.0 {
486            return Err(Error::DivideByZero);
487        }
488        let usecs = self.usecs() as f64;
489        let result = usecs / number;
490
491        if result.is_infinite() {
492            Err(Error::NumericOverflow)
493        } else if result.is_nan() {
494            Err(Error::InvalidNumber)
495        } else {
496            IntervalDT::try_from_usecs(result as i64)
497        }
498    }
499
500    /// `IntervalDT` subtracts `Time`
501    #[inline]
502    pub const fn sub_time(self, time: Time) -> Result<IntervalDT> {
503        IntervalDT::try_from_usecs(self.usecs() - time.usecs())
504    }
505}
506
507impl From<IntervalDT> for NaiveDateTime {
508    #[inline]
509    fn from(interval: IntervalDT) -> Self {
510        let (sign, day, hour, minute, sec, usec) = interval.extract();
511        let negative = sign == Sign::Negative;
512        NaiveDateTime {
513            day,
514            hour,
515            minute,
516            sec,
517            usec,
518            negative,
519            ..NaiveDateTime::new()
520        }
521    }
522}
523
524impl TryFrom<NaiveDateTime> for IntervalDT {
525    type Error = Error;
526
527    #[inline]
528    fn try_from(dt: NaiveDateTime) -> Result<Self> {
529        if dt.negative {
530            Ok(IntervalDT::try_from_dhms(dt.day, dt.hour, dt.minute, dt.sec, dt.usec)?.negate())
531        } else {
532            IntervalDT::try_from_dhms(dt.day, dt.hour, dt.minute, dt.sec, dt.usec)
533        }
534    }
535}
536
537impl From<Time> for IntervalDT {
538    #[inline]
539    fn from(time: Time) -> Self {
540        unsafe { IntervalDT::from_usecs_unchecked(time.usecs()) }
541    }
542}
543
544impl PartialEq<Time> for IntervalDT {
545    #[inline]
546    fn eq(&self, other: &Time) -> bool {
547        self.usecs() == other.usecs()
548    }
549}
550
551impl PartialOrd<Time> for IntervalDT {
552    #[inline]
553    fn partial_cmp(&self, other: &Time) -> Option<Ordering> {
554        Some(self.usecs().cmp(&other.usecs()))
555    }
556}
557
558impl Neg for IntervalDT {
559    type Output = IntervalDT;
560
561    #[inline]
562    fn neg(self) -> Self::Output {
563        self.negate()
564    }
565}
566
567impl DateTime for IntervalDT {
568    #[inline(always)]
569    fn year(&self) -> Option<i32> {
570        None
571    }
572
573    #[inline(always)]
574    fn month(&self) -> Option<i32> {
575        None
576    }
577
578    #[inline(always)]
579    fn day(&self) -> Option<i32> {
580        Some((self.usecs() / USECONDS_PER_DAY) as i32)
581    }
582
583    #[inline(always)]
584    fn hour(&self) -> Option<i32> {
585        let remain_time = self.usecs() % USECONDS_PER_DAY;
586        Some((remain_time / USECONDS_PER_HOUR) as i32)
587    }
588
589    #[inline(always)]
590    fn minute(&self) -> Option<i32> {
591        let remain_time = self.usecs() % USECONDS_PER_HOUR;
592        Some((remain_time / USECONDS_PER_MINUTE) as i32)
593    }
594
595    #[inline]
596    fn second(&self) -> Option<f64> {
597        let remain_time = self.usecs() % USECONDS_PER_MINUTE;
598        Some(remain_time as f64 / USECONDS_PER_SECOND as f64)
599    }
600
601    #[inline(always)]
602    fn date(&self) -> Option<Date> {
603        None
604    }
605}
606
607#[cfg(test)]
608mod tests {
609    use super::*;
610
611    #[test]
612    fn test_interval_ym() {
613        assert_eq!(IntervalYM::ZERO, IntervalYM::try_from_ym(0, 0).unwrap());
614        assert_eq!(
615            IntervalYM::MIN,
616            -IntervalYM::try_from_ym(178000000, 0).unwrap()
617        );
618        assert_eq!(
619            IntervalYM::MAX,
620            IntervalYM::try_from_ym(178000000, 0).unwrap()
621        );
622
623        let interval = IntervalYM::try_from_ym(0, 0).unwrap();
624        assert_eq!(interval.months(), 0);
625        assert_eq!(interval.extract(), (Positive, 0, 0));
626
627        let interval = IntervalYM::try_from_ym(178000000, 0).unwrap();
628        assert_eq!(interval.extract(), (Positive, 178000000, 0));
629        let fmt = format!("{}", interval.format("yyyy-mm").unwrap());
630        assert_eq!(fmt, "+178000000-00");
631        let interval2 = IntervalYM::parse("178000000-00", "yyyy-mm").unwrap();
632        assert_eq!(interval2, interval);
633
634        let interval = -IntervalYM::try_from_ym(1, 0).unwrap();
635        let fmt = format!("{}", interval.format("yy-mm").unwrap());
636        assert_eq!(fmt, "-01-00");
637
638        let interval = IntervalYM::try_from_ym(123, 2).unwrap();
639        let fmt = format!("{}", interval.format("yy-mm").unwrap());
640        assert_eq!(fmt, "+123-02");
641
642        let interval = -IntervalYM::try_from_ym(178000000, 0).unwrap();
643        assert_eq!(interval.extract(), (Negative, 178000000, 0));
644        let interval = IntervalYM::try_from_ym(178000000, 0).unwrap().negate();
645        assert_eq!(interval.extract(), (Negative, 178000000, 0));
646        let fmt = format!("{}", interval.format("yyyy-mm").unwrap());
647        assert_eq!(fmt, "-178000000-00");
648
649        let fmt = format!("{}", interval.format("yy-mm").unwrap());
650        assert_eq!(fmt, "-178000000-00");
651
652        let interval2 = IntervalYM::parse("-178000000-00", "yyyy-mm").unwrap();
653        assert_eq!(interval2, interval);
654
655        let interval2 = IntervalYM::parse("+178000000-00", "yyyy-mm").unwrap();
656        assert_eq!(interval2, -interval);
657
658        let interval = IntervalYM::try_from_ym(177999999, 11).unwrap();
659        assert_eq!(interval.extract(), (Positive, 177999999, 11));
660
661        let interval = -IntervalYM::try_from_ym(177999999, 11).unwrap();
662        assert_eq!(interval.extract(), (Negative, 177999999, 11));
663
664        let interval = IntervalYM::try_from_months(0).unwrap();
665        assert_eq!(interval.extract(), (Positive, 0, 0));
666
667        let interval = IntervalYM::try_from_months(-11).unwrap();
668        assert_eq!(interval.extract(), (Negative, 0, 11));
669        let fmt = format!("{}", interval.format("yyyy-mm").unwrap());
670        assert_eq!(fmt, "-0000-11");
671
672        let interval2 = IntervalYM::parse("-0000-11", "yyyy-mm").unwrap();
673        assert_eq!(interval, interval2);
674        let interval2 = IntervalYM::parse("-0000 - 11", "yyyy - mm").unwrap();
675        assert_eq!(interval, interval2);
676        let interval2 = IntervalYM::parse("       -0000 - 11       ", "yyyy - mm").unwrap();
677        assert_eq!(interval, interval2);
678        let interval2 = IntervalYM::parse("       -0000 - 11       ", "    yyyy - mm    ").unwrap();
679        assert_eq!(interval, interval2);
680        let interval2 = IntervalYM::parse("-0000-11", "yyyy - mm").unwrap();
681        assert_eq!(interval, interval2);
682
683        let interval = IntervalYM::try_from_months(11).unwrap();
684        let interval2 = IntervalYM::parse("0000-11", "yyyy-mm").unwrap();
685        assert_eq!(interval, interval2);
686
687        let interval = IntervalYM::try_from_ym(12345, 1).unwrap();
688        let interval2 = IntervalYM::parse("12345-1", "yyyy-mm").unwrap();
689        assert_eq!(interval, interval2);
690
691        let interval = IntervalYM::try_from_ym(12345, 1).unwrap();
692        let interval2 = IntervalYM::parse("12345-1", "yy-mm").unwrap();
693        assert_eq!(interval, interval2);
694
695        let interval = IntervalYM::try_from_ym(1, 1).unwrap();
696        let interval2 = IntervalYM::parse("1-1", "yy-mm").unwrap();
697        assert_eq!(interval, interval2);
698
699        // Tests when fmt doesn't have month, returns the default month `0` in IntervalYM
700        let res = IntervalYM::parse("2022", "YYYY").unwrap();
701        assert_eq!(res.month().unwrap(), 0);
702
703        // Invalid
704        assert!(IntervalYM::parse("178000000-1", "yyyy-mm").is_err());
705        assert!(IntervalYM::parse("178000001-0", "yyyy-mm").is_err());
706        assert!(IntervalYM::parse("-178000001-0", "yyyy-mm").is_err());
707        assert!(IntervalYM::parse("0-13", "yyyy-mm").is_err());
708        assert!(IntervalYM::parse("-178000000-1", "yyyy-mm").is_err());
709        assert!(IntervalYM::parse("-178000001-0", "yyyy-mm").is_err());
710        assert!(IntervalYM::parse("11", "dd").is_err());
711        assert!(IntervalYM::parse("11", "hh24").is_err());
712        assert!(IntervalYM::parse("11", "mi").is_err());
713        assert!(IntervalYM::parse("11", "ss").is_err());
714
715        assert_eq!(
716            IntervalYM::parse("xxxx", "yy-mm").err().unwrap(),
717            Error::ParseError("the interval is invalid".to_string())
718        )
719        // todo invalid fields
720    }
721
722    #[test]
723    fn test_interval_dt() {
724        assert_eq!(
725            IntervalDT::ZERO,
726            IntervalDT::try_from_dhms(0, 0, 0, 0, 0).unwrap()
727        );
728        assert_eq!(
729            IntervalDT::MIN,
730            -IntervalDT::try_from_dhms(100_000_000, 0, 0, 0, 0).unwrap()
731        );
732        assert_eq!(
733            IntervalDT::MAX,
734            IntervalDT::try_from_dhms(100_000_000, 0, 0, 0, 0).unwrap()
735        );
736
737        let time = Time::try_from_hms(0, 0, 0, 0).unwrap();
738        let interval = IntervalDT::from(time);
739        assert_eq!(IntervalDT::ZERO, interval);
740
741        let time = Time::try_from_hms(1, 2, 3, 4).unwrap();
742        let interval = IntervalDT::from(time);
743        assert_eq!(IntervalDT::try_from_dhms(0, 1, 2, 3, 4).unwrap(), interval);
744
745        let time = Time::try_from_hms(23, 59, 59, 999999).unwrap();
746        let interval = IntervalDT::from(time);
747        assert_eq!(
748            IntervalDT::try_from_dhms(0, 23, 59, 59, 999999).unwrap(),
749            interval
750        );
751
752        let interval = IntervalDT::try_from_dhms(0, 0, 0, 0, 0).unwrap();
753        assert_eq!(interval.usecs(), 0);
754        assert_eq!(interval.extract(), (Positive, 0, 0, 0, 0, 0));
755        let fmt = format!("{}", interval.format("DD HH24:MI:SS").unwrap());
756        assert_eq!(fmt, "+00 00:00:00");
757
758        let interval = IntervalDT::try_from_dhms(100000000, 0, 0, 0, 0).unwrap();
759        assert_eq!(interval.extract(), (Positive, 100000000, 0, 0, 0, 0));
760        let fmt = format!("{}", interval.format("DD HH24:MI:SS").unwrap());
761        assert_eq!(fmt, "+100000000 00:00:00");
762        let interval2 = IntervalDT::parse("100000000 00:00:00", "DD HH24:MI:SS").unwrap();
763        assert_eq!(interval2, interval);
764
765        let interval2 = IntervalDT::parse("+100000000 00:00:00", "DD HH24:MI:SS").unwrap();
766        assert_eq!(interval2, interval);
767
768        let interval = -IntervalDT::try_from_dhms(100000000, 0, 0, 0, 0).unwrap();
769        assert_eq!(interval.extract(), (Negative, 100000000, 0, 0, 0, 0));
770
771        let interval = IntervalDT::try_from_dhms(100000000, 0, 0, 0, 0)
772            .unwrap()
773            .negate();
774        assert_eq!(interval.extract(), (Negative, 100000000, 0, 0, 0, 0));
775
776        let interval = IntervalDT::try_from_dhms(99999999, 23, 59, 59, 999999).unwrap();
777        assert_eq!(interval.extract(), (Positive, 99999999, 23, 59, 59, 999999));
778
779        let interval = -IntervalDT::try_from_dhms(99999999, 23, 59, 59, 999999).unwrap();
780        assert_eq!(interval.extract(), (Negative, 99999999, 23, 59, 59, 999999));
781        let fmt = format!("{}", interval.format("DD HH24:MI:SS.FF6").unwrap());
782        assert_eq!(fmt, "-99999999 23:59:59.999999");
783
784        let interval = IntervalDT::try_from_usecs(-11).unwrap();
785        let interval2 = IntervalDT::parse("-0 00:00:00.000011", "DD HH24:MI:SS.FF6").unwrap();
786        assert_eq!(interval, interval2);
787
788        let interval = IntervalDT::try_from_usecs(11).unwrap();
789        let interval2 = IntervalDT::parse("0 00:00:00.000011", "DD HH24:MI:SS.FF6").unwrap();
790        assert_eq!(interval, interval2);
791
792        let interval = IntervalDT::try_from_usecs(-11).unwrap();
793        let interval2 = IntervalDT::parse("-0 00:00:00.000011", "DD HH24:MI:SS.FF").unwrap();
794        assert_eq!(interval, interval2);
795
796        let interval = IntervalDT::try_from_usecs(-12).unwrap();
797        let interval2 = IntervalDT::parse("-0 00:00:00.000011567", "DD HH24:MI:SS.FF").unwrap();
798        assert_eq!(interval, interval2);
799
800        let interval = IntervalDT::try_from_dhms(12, 4, 5, 6, 0).unwrap().negate();
801        let interval2 = IntervalDT::parse("-12 4:5:6", "DD HH24:MI:SS").unwrap();
802        assert_eq!(interval, interval2);
803
804        // Invalid
805        assert!(IntervalDT::parse("100000000 02:00:00.0", "DD HH24:MI:SS.FF").is_err());
806        assert!(IntervalDT::parse("0 24:00:00:00.0", "DD HH24:MI:SS.FF").is_err());
807        assert!(IntervalDT::parse("100000001 00:00:00.0", "DD HH24:MI:SS.FF").is_err());
808        assert!(IntervalDT::parse("-100000001 00:00:00.0", "DD HH24:MI:SS.FF").is_err());
809        assert!(IntervalDT::parse("-100000000 02:00:00.0", "DD HH24:MI:SS.FF").is_err());
810
811        assert!(IntervalDT::parse("-100000 02:00", "DD HH24:MI:SS.FF").is_err());
812        assert!(IntervalDT::parse("-100000 02", "DD HH24:MI:SS.FF").is_err());
813        assert!(IntervalDT::parse("-100000 ", "DD HH24:MI:SS.FF").is_err());
814
815        assert!(IntervalDT::parse("1919", "yyyy").is_err());
816        assert!(IntervalDT::parse("19", "mm").is_err());
817    }
818
819    #[test]
820    fn test_interval_negate() {
821        assert_eq!(
822            -IntervalDT::try_from_dhms(1, 2, 3, 4, 5).unwrap(),
823            IntervalDT::try_from_usecs(-93784000005).unwrap()
824        );
825        assert_eq!(
826            IntervalDT::try_from_dhms(1, 2, 3, 4, 5).unwrap().negate(),
827            IntervalDT::try_from_usecs(-93784000005).unwrap()
828        );
829        assert_eq!(
830            -IntervalDT::try_from_dhms(0, 0, 0, 0, 0).unwrap(),
831            IntervalDT::try_from_dhms(0, 0, 0, 0, 0).unwrap()
832        );
833        assert_eq!(
834            IntervalDT::try_from_dhms(0, 0, 0, 0, 0).unwrap().negate(),
835            IntervalDT::try_from_dhms(0, 0, 0, 0, 0).unwrap()
836        );
837        assert_eq!(
838            -IntervalDT::try_from_dhms(1, 2, 3, 4, 5).unwrap().negate(),
839            IntervalDT::try_from_usecs(93784000005).unwrap()
840        );
841        assert_eq!(
842            IntervalDT::try_from_dhms(1, 2, 3, 4, 5)
843                .unwrap()
844                .negate()
845                .negate(),
846            IntervalDT::try_from_dhms(1, 2, 3, 4, 5).unwrap()
847        );
848        assert_eq!(
849            -IntervalDT::try_from_dhms(INTERVAL_MAX_DAY as u32, 0, 0, 0, 0).unwrap(),
850            IntervalDT::try_from_usecs(-8640000000000000000).unwrap()
851        );
852        assert_eq!(
853            IntervalDT::try_from_dhms(INTERVAL_MAX_DAY as u32, 0, 0, 0, 0)
854                .unwrap()
855                .negate(),
856            IntervalDT::try_from_usecs(-8640000000000000000).unwrap()
857        );
858        assert_eq!(
859            -IntervalDT::try_from_dhms(INTERVAL_MAX_DAY as u32, 0, 0, 0, 0).unwrap(),
860            IntervalDT::try_from_dhms(INTERVAL_MAX_DAY as u32, 0, 0, 0, 0)
861                .unwrap()
862                .negate()
863        );
864
865        assert_eq!(
866            -IntervalYM::try_from_ym(1, 2).unwrap(),
867            IntervalYM::try_from_months(-14).unwrap()
868        );
869        assert_eq!(
870            IntervalYM::try_from_ym(1, 2).unwrap().negate(),
871            IntervalYM::try_from_months(-14).unwrap()
872        );
873        assert_eq!(
874            -IntervalYM::try_from_ym(0, 0).unwrap(),
875            IntervalYM::try_from_ym(0, 0).unwrap()
876        );
877        assert_eq!(
878            IntervalYM::try_from_ym(0, 0).unwrap().negate(),
879            IntervalYM::try_from_ym(0, 0).unwrap()
880        );
881        assert_eq!(
882            -IntervalYM::try_from_ym(1, 2).unwrap().negate(),
883            IntervalYM::try_from_ym(1, 2).unwrap()
884        );
885        assert_eq!(
886            IntervalYM::try_from_ym(1, 2).unwrap().negate().negate(),
887            IntervalYM::try_from_ym(1, 2).unwrap()
888        );
889        assert_eq!(
890            -IntervalYM::try_from_ym(INTERVAL_MAX_YEAR as u32, 0).unwrap(),
891            IntervalYM::try_from_months(-2136000000).unwrap()
892        );
893        assert_eq!(
894            IntervalYM::try_from_ym(INTERVAL_MAX_YEAR as u32, 0)
895                .unwrap()
896                .negate(),
897            IntervalYM::try_from_months(-2136000000).unwrap()
898        );
899        assert_eq!(
900            -IntervalYM::try_from_ym(INTERVAL_MAX_YEAR as u32, 0)
901                .unwrap()
902                .negate(),
903            IntervalYM::try_from_months(2136000000).unwrap()
904        );
905        assert_eq!(
906            IntervalYM::try_from_ym(INTERVAL_MAX_YEAR as u32, 0)
907                .unwrap()
908                .negate()
909                .negate(),
910            IntervalYM::try_from_months(2136000000).unwrap()
911        );
912    }
913
914    #[test]
915    fn test_interval_ym_add_sub_interval_ym() {
916        assert!(IntervalYM::try_from_ym(178000000, 0)
917            .unwrap()
918            .add_interval_ym(IntervalYM::try_from_ym(0, 1).unwrap())
919            .is_err());
920
921        assert!(IntervalYM::try_from_ym(178000000, 0)
922            .unwrap()
923            .sub_interval_ym(-IntervalYM::try_from_ym(0, 1).unwrap())
924            .is_err());
925
926        assert!(IntervalYM::try_from_ym(178000000, 0)
927            .unwrap()
928            .negate()
929            .sub_interval_ym(IntervalYM::try_from_ym(0, 1).unwrap())
930            .is_err());
931
932        assert!((-IntervalYM::try_from_ym(178000000, 0).unwrap())
933            .add_interval_ym(-IntervalYM::try_from_ym(0, 1).unwrap())
934            .is_err());
935
936        assert_eq!(
937            IntervalYM::try_from_ym(123456, 5)
938                .unwrap()
939                .add_interval_ym(IntervalYM::try_from_ym(123, 7).unwrap())
940                .unwrap(),
941            IntervalYM::try_from_ym(123580, 0).unwrap()
942        );
943
944        assert_eq!(
945            IntervalYM::try_from_ym(123456, 5)
946                .unwrap()
947                .sub_interval_ym(IntervalYM::try_from_ym(123, 7).unwrap())
948                .unwrap(),
949            IntervalYM::try_from_ym(123332, 10).unwrap()
950        );
951    }
952
953    #[test]
954    fn test_interval_dt_add_sub_interval_dt() {
955        assert!(IntervalDT::try_from_dhms(100000000, 0, 0, 0, 0)
956            .unwrap()
957            .add_interval_dt(IntervalDT::try_from_dhms(0, 0, 0, 0, 1).unwrap())
958            .is_err());
959
960        assert!(IntervalDT::try_from_dhms(100000000, 0, 0, 0, 0)
961            .unwrap()
962            .sub_interval_dt(-IntervalDT::try_from_dhms(0, 0, 0, 0, 1).unwrap())
963            .is_err());
964
965        assert!(IntervalDT::try_from_dhms(100000000, 0, 0, 0, 0)
966            .unwrap()
967            .negate()
968            .sub_interval_dt(IntervalDT::try_from_dhms(0, 0, 0, 0, 1).unwrap())
969            .is_err());
970
971        assert!(IntervalDT::try_from_dhms(100000000, 0, 0, 0, 0)
972            .unwrap()
973            .negate()
974            .add_interval_dt(-IntervalDT::try_from_dhms(0, 0, 0, 0, 1).unwrap())
975            .is_err());
976
977        assert_eq!(
978            IntervalDT::try_from_dhms(23456789, 1, 2, 3, 4)
979                .unwrap()
980                .add_interval_dt(IntervalDT::try_from_dhms(123, 1, 2, 3, 4).unwrap())
981                .unwrap(),
982            IntervalDT::try_from_dhms(23456912, 2, 4, 6, 8).unwrap()
983        );
984
985        assert_eq!(
986            IntervalDT::try_from_dhms(23456789, 1, 2, 3, 4)
987                .unwrap()
988                .sub_interval_dt(IntervalDT::try_from_dhms(123, 1, 2, 3, 4).unwrap())
989                .unwrap(),
990            IntervalDT::try_from_dhms(23456666, 0, 0, 0, 0).unwrap()
991        );
992    }
993
994    #[test]
995    fn test_interval_mul_div() {
996        // Normal
997        assert_eq!(
998            IntervalDT::try_from_dhms(1, 2, 3, 4, 5)
999                .unwrap()
1000                .mul_f64(5.0)
1001                .unwrap(),
1002            IntervalDT::try_from_dhms(5, 10, 15, 20, 25).unwrap()
1003        );
1004
1005        assert_eq!(
1006            IntervalDT::try_from_dhms(1, 2, 3, 4, 5)
1007                .unwrap()
1008                .mul_f64(-5.2)
1009                .unwrap(),
1010            -IntervalDT::try_from_dhms(5, 15, 27, 56, 800026).unwrap()
1011        );
1012
1013        assert_eq!(
1014            IntervalDT::try_from_dhms(1, 2, 3, 4, 5)
1015                .unwrap()
1016                .div_f64(-5.2)
1017                .unwrap(),
1018            -IntervalDT::try_from_dhms(0, 5, 0, 35, 384616).unwrap()
1019        );
1020
1021        assert_eq!(
1022            IntervalDT::try_from_dhms(1, 2, 3, 4, 5)
1023                .unwrap()
1024                .div_f64(-5.0)
1025                .unwrap(),
1026            -IntervalDT::try_from_dhms(0, 5, 12, 36, 800001).unwrap()
1027        );
1028
1029        assert_eq!(
1030            IntervalDT::try_from_dhms(99999, 2, 3, 4, 5)
1031                .unwrap()
1032                .div_f64(f64::INFINITY)
1033                .unwrap(),
1034            -IntervalDT::try_from_dhms(0, 0, 0, 0, 0).unwrap()
1035        );
1036
1037        // Round
1038        assert_eq!(
1039            IntervalDT::try_from_dhms(1, 2, 3, 4, 5)
1040                .unwrap()
1041                .div_f64(-5.1)
1042                .unwrap(),
1043            -IntervalDT::try_from_dhms(0, 5, 6, 29, 19608).unwrap()
1044        );
1045
1046        assert_eq!(
1047            IntervalDT::try_from_dhms(1, 2, 3, 4, 5)
1048                .unwrap()
1049                .mul_f64(-5.57)
1050                .unwrap(),
1051            -IntervalDT::try_from_dhms(6, 1, 6, 16, 880027).unwrap()
1052        );
1053
1054        // Out of range
1055        assert!(IntervalDT::try_from_dhms(99999, 2, 3, 4, 5)
1056            .unwrap()
1057            .mul_f64(-1234567890.6)
1058            .is_err());
1059
1060        assert!(IntervalDT::try_from_dhms(99999, 2, 3, 4, 5)
1061            .unwrap()
1062            .div_f64(-0.000000000001)
1063            .is_err());
1064
1065        assert!(IntervalDT::try_from_dhms(99999, 2, 3, 4, 5)
1066            .unwrap()
1067            .mul_f64(f64::NEG_INFINITY)
1068            .is_err());
1069
1070        assert!(IntervalDT::try_from_dhms(99999, 2, 3, 4, 5)
1071            .unwrap()
1072            .div_f64(f64::NAN)
1073            .is_err());
1074
1075        assert!(IntervalDT::try_from_dhms(99999, 2, 3, 4, 5)
1076            .unwrap()
1077            .mul_f64(f64::NAN)
1078            .is_err());
1079
1080        // Divide by zero
1081        assert!(IntervalDT::try_from_dhms(99999, 2, 3, 4, 5)
1082            .unwrap()
1083            .div_f64(0.0)
1084            .is_err());
1085
1086        // Year to month
1087        assert_eq!(
1088            IntervalYM::try_from_ym(1, 2).unwrap().mul_f64(5.0).unwrap(),
1089            IntervalYM::try_from_ym(5, 10).unwrap()
1090        );
1091
1092        assert_eq!(
1093            IntervalYM::try_from_ym(1, 2)
1094                .unwrap()
1095                .mul_f64(-5.3)
1096                .unwrap(),
1097            -IntervalYM::try_from_ym(6, 2).unwrap()
1098        );
1099
1100        assert_eq!(
1101            IntervalYM::try_from_ym(1, 2)
1102                .unwrap()
1103                .mul_f64(-5.2)
1104                .unwrap(),
1105            -IntervalYM::try_from_ym(6, 0).unwrap()
1106        );
1107
1108        assert_eq!(
1109            IntervalYM::try_from_ym(1, 2)
1110                .unwrap()
1111                .div_f64(-5.2)
1112                .unwrap(),
1113            -IntervalYM::try_from_ym(0, 2).unwrap()
1114        );
1115
1116        assert_eq!(
1117            IntervalYM::try_from_ym(1, 2)
1118                .unwrap()
1119                .div_f64(-4.7)
1120                .unwrap(),
1121            -IntervalYM::try_from_ym(0, 2).unwrap()
1122        );
1123
1124        assert_eq!(
1125            IntervalYM::try_from_ym(1, 2)
1126                .unwrap()
1127                .div_f64(f64::INFINITY)
1128                .unwrap(),
1129            -IntervalYM::try_from_ym(0, 0).unwrap()
1130        );
1131
1132        // Out of range
1133        assert!(IntervalYM::try_from_ym(500000, 2)
1134            .unwrap()
1135            .mul_f64(123456789.123)
1136            .is_err());
1137
1138        assert!(IntervalYM::try_from_ym(500000, 2)
1139            .unwrap()
1140            .mul_f64(f64::INFINITY)
1141            .is_err());
1142
1143        assert!(IntervalYM::try_from_ym(500000, 2)
1144            .unwrap()
1145            .mul_f64(f64::NEG_INFINITY)
1146            .is_err());
1147
1148        assert!(IntervalYM::try_from_ym(500000, 2)
1149            .unwrap()
1150            .mul_f64(f64::NAN)
1151            .is_err());
1152
1153        assert!(IntervalYM::try_from_ym(500000, 2)
1154            .unwrap()
1155            .div_f64(f64::NAN)
1156            .is_err());
1157
1158        // Divide by zero
1159        assert!(IntervalYM::try_from_ym(500000, 2)
1160            .unwrap()
1161            .div_f64(0.0)
1162            .is_err());
1163    }
1164
1165    #[test]
1166    fn test_interval_dt_sub_time() {
1167        // Out of range
1168        assert!(
1169            IntervalDT::try_from_dhms(INTERVAL_MAX_DAY as u32, 0, 0, 0, 0)
1170                .unwrap()
1171                .negate()
1172                .sub_time(Time::try_from_hms(1, 2, 3, 4).unwrap())
1173                .is_err()
1174        );
1175
1176        // Normal
1177        assert_eq!(
1178            IntervalDT::try_from_dhms(0, 0, 0, 0, 0)
1179                .unwrap()
1180                .sub_time(Time::try_from_hms(1, 2, 3, 4).unwrap())
1181                .unwrap(),
1182            -IntervalDT::try_from_dhms(0, 1, 2, 3, 4).unwrap()
1183        );
1184    }
1185
1186    fn test_extract_ym(negate: bool, year: u32, month: u32) {
1187        let interval = if negate {
1188            IntervalYM::try_from_ym(year, month).unwrap().negate()
1189        } else {
1190            IntervalYM::try_from_ym(year, month).unwrap()
1191        };
1192
1193        let modifier = if negate { -1 } else { 1 };
1194
1195        assert_eq!(year as i32 * modifier, interval.year().unwrap());
1196        assert_eq!(month as i32 * modifier, interval.month().unwrap());
1197
1198        assert!(interval.hour().is_none());
1199        assert!(interval.day().is_none());
1200        assert!(interval.minute().is_none());
1201        assert!(interval.second().is_none());
1202    }
1203
1204    #[test]
1205    fn test_interval_ym_extract() {
1206        test_extract_ym(false, 0, 0);
1207        test_extract_ym(false, 0, 1);
1208        test_extract_ym(false, 1, 1);
1209        test_extract_ym(false, 1234, 11);
1210        test_extract_ym(false, 178000000, 0);
1211        test_extract_ym(true, 0, 1);
1212        test_extract_ym(true, 1, 1);
1213        test_extract_ym(true, 1234, 11);
1214        test_extract_ym(true, 178000000, 0);
1215    }
1216
1217    #[allow(clippy::float_cmp)]
1218    fn test_extract_dt(negate: bool, day: u32, hour: u32, min: u32, sec: u32, usec: u32) {
1219        let interval = if negate {
1220            IntervalDT::try_from_dhms(day, hour, min, sec, usec)
1221                .unwrap()
1222                .negate()
1223        } else {
1224            IntervalDT::try_from_dhms(day, hour, min, sec, usec).unwrap()
1225        };
1226
1227        let modifier = if negate { -1 } else { 1 };
1228
1229        assert_eq!(day as i32 * modifier, interval.day().unwrap());
1230        assert_eq!(hour as i32 * modifier, interval.hour().unwrap());
1231        assert_eq!(min as i32 * modifier, interval.minute().unwrap());
1232        assert_eq!(
1233            modifier as f64 * (sec as f64 + (usec as f64) / 1_000_000f64),
1234            interval.second().unwrap()
1235        );
1236        assert!(interval.year().is_none());
1237        assert!(interval.month().is_none());
1238    }
1239
1240    #[test]
1241    fn test_interval_dt_extract() {
1242        test_extract_dt(false, 0, 0, 0, 0, 0);
1243        test_extract_dt(false, 0, 0, 0, 0, 1);
1244        test_extract_dt(false, 1, 0, 0, 0, 1);
1245        test_extract_dt(false, 9999, 23, 59, 59, 999999);
1246        test_extract_dt(false, 100000000, 0, 0, 0, 0);
1247        test_extract_dt(true, 0, 0, 0, 0, 1);
1248        test_extract_dt(true, 1, 0, 0, 0, 1);
1249        test_extract_dt(true, 9999, 23, 59, 59, 999999);
1250        test_extract_dt(true, 9999, 23, 59, 59, 375473);
1251        test_extract_dt(true, 100000000, 0, 0, 0, 0);
1252    }
1253}