1use std::fmt::{self, Write};
13use std::sync::LazyLock;
14use std::time::Duration;
15
16use anyhow::{anyhow, bail};
17use mz_persist_types::columnar::FixedSizeCodec;
18use mz_proto::{RustType, TryFromProtoError};
19use num_traits::CheckedMul;
20#[cfg(any(test, feature = "proptest"))]
21use proptest::prelude::{Arbitrary, BoxedStrategy, Strategy, any};
22use serde::{Deserialize, Serialize};
23
24use crate::adt::datetime::DateTimeField;
25use crate::adt::numeric::{DecimalLike, Numeric};
26
27include!(concat!(env!("OUT_DIR"), "/mz_repr.adt.interval.rs"));
28
29#[derive(
33 Debug,
34 Clone,
35 Copy,
36 PartialEq,
37 Eq,
38 PartialOrd,
39 Ord,
40 Serialize,
41 Hash,
42 Deserialize
43)]
44pub struct Interval {
45 pub months: i32,
47 pub days: i32,
51 pub micros: i64,
56}
57
58impl Default for Interval {
59 fn default() -> Self {
60 Self {
61 months: 0,
62 days: 0,
63 micros: 0,
64 }
65 }
66}
67
68impl RustType<ProtoInterval> for Interval {
69 fn into_proto(&self) -> ProtoInterval {
70 ProtoInterval {
71 months: self.months,
72 days: self.days,
73 micros: self.micros,
74 }
75 }
76
77 fn from_proto(proto: ProtoInterval) -> Result<Self, TryFromProtoError> {
78 Ok(Interval {
79 months: proto.months,
80 days: proto.days,
81 micros: proto.micros,
82 })
83 }
84}
85
86impl num_traits::ops::checked::CheckedNeg for Interval {
87 fn checked_neg(&self) -> Option<Self> {
88 if let (Some(months), Some(days), Some(micros)) = (
89 self.months.checked_neg(),
90 self.days.checked_neg(),
91 self.micros.checked_neg(),
92 ) {
93 Some(Self::new(months, days, micros))
94 } else {
95 None
96 }
97 }
98}
99
100impl std::str::FromStr for Interval {
101 type Err = anyhow::Error;
102
103 fn from_str(s: &str) -> Result<Self, Self::Err> {
104 crate::strconv::parse_interval(s).map_err(|e| anyhow!(e))
105 }
106}
107
108static MONTH_OVERFLOW_ERROR: LazyLock<String> = LazyLock::new(|| {
109 format!(
110 "Overflows maximum months; cannot exceed {}/{} microseconds",
111 i32::MAX,
112 i32::MIN,
113 )
114});
115static DAY_OVERFLOW_ERROR: LazyLock<String> = LazyLock::new(|| {
116 format!(
117 "Overflows maximum days; cannot exceed {}/{} microseconds",
118 i32::MAX,
119 i32::MIN,
120 )
121});
122pub static USECS_PER_DAY: LazyLock<i64> = LazyLock::new(|| {
123 Interval::convert_date_time_unit(DateTimeField::Day, DateTimeField::Microseconds, 1i64).unwrap()
124});
125
126#[derive(Debug, Clone)]
127pub enum RoundBehavior {
128 Truncate,
129 Nearest,
130}
131
132impl Interval {
133 pub const CENTURY_PER_MILLENNIUM: u16 = 10;
134 pub const DECADE_PER_CENTURY: u16 = 10;
135 pub const YEAR_PER_DECADE: u16 = 10;
136 pub const MONTH_PER_YEAR: u16 = 12;
137 pub const DAY_PER_MONTH: u16 = 30;
139 pub const HOUR_PER_DAY: u16 = 24;
141 pub const MINUTE_PER_HOUR: u16 = 60;
142 pub const SECOND_PER_MINUTE: u16 = 60;
143 pub const MILLISECOND_PER_SECOND: u16 = 1_000;
144 pub const MICROSECOND_PER_MILLISECOND: u16 = 1_000;
145 pub const NANOSECOND_PER_MICROSECOND: u16 = 1_000;
146 pub const EPOCH_DAYS_PER_YEAR: f64 = 365.25;
152
153 pub const fn new(months: i32, days: i32, micros: i64) -> Interval {
155 Interval {
156 months,
157 days,
158 micros,
159 }
160 }
161
162 pub fn from_duration(duration: &Duration) -> Result<Interval, anyhow::Error> {
167 if duration.subsec_nanos() % 1000 != 0 {
168 return Err(anyhow!(
169 "cannot convert Duration to Interval due to fractional microseconds"
170 ));
171 }
172 Ok(Interval {
173 months: 0,
174 days: 0,
175 micros: duration.as_micros().try_into()?,
176 })
177 }
178
179 pub fn from_chrono_duration(duration: chrono::Duration) -> Result<Self, anyhow::Error> {
182 let Some(micros) = duration.num_microseconds() else {
183 bail!("cannot convert Duration to Interval due to overflowed microseconds");
184 };
185 Ok(Self {
186 months: 0,
187 days: 0,
188 micros,
189 })
190 }
191
192 pub fn checked_add(&self, other: &Self) -> Option<Self> {
193 let months = match self.months.checked_add(other.months) {
194 Some(m) => m,
195 None => return None,
196 };
197 let days = match self.days.checked_add(other.days) {
198 Some(d) => d,
199 None => return None,
200 };
201 let micros = match self.micros.checked_add(other.micros) {
202 Some(us) => us,
203 None => return None,
204 };
205
206 Some(Self::new(months, days, micros))
207 }
208
209 pub fn checked_mul(&self, other: f64) -> Option<Self> {
210 self.checked_op(other, |f1, f2| f1 * f2)
211 }
212
213 pub fn checked_div(&self, other: f64) -> Option<Self> {
214 self.checked_op(other, |f1, f2| f1 / f2)
215 }
216
217 #[allow(clippy::as_conversions)]
220 fn checked_op<F1>(&self, other: f64, op: F1) -> Option<Self>
221 where
222 F1: Fn(f64, f64) -> f64,
223 {
224 let months = op(f64::from(self.months), other);
225 if months.is_nan()
226 || months.is_infinite()
227 || months < i32::MIN.into()
228 || months > i32::MAX.into()
229 {
230 return None;
231 }
232
233 let days =
234 op(f64::from(self.days), other) + months.fract() * f64::from(Self::DAY_PER_MONTH);
235 if days.is_nan() || days.is_infinite() || days < i32::MIN.into() || days > i32::MAX.into() {
236 return None;
237 }
238
239 let micros = op(self.micros as f64, other)
240 + days.fract()
241 * f64::from(Self::HOUR_PER_DAY)
242 * f64::from(Self::MINUTE_PER_HOUR)
243 * f64::from(Self::SECOND_PER_MINUTE)
244 * f64::from(Self::MILLISECOND_PER_SECOND)
245 * f64::from(Self::MICROSECOND_PER_MILLISECOND);
246
247 if micros.is_nan()
248 || micros.is_infinite()
249 || Numeric::from(micros) < Numeric::from(i64::MIN)
250 || Numeric::from(micros) > Numeric::from(i64::MAX)
251 {
252 return None;
253 }
254
255 Some(Self::new(months as i32, days as i32, micros as i64))
256 }
257
258 pub fn millennia(&self) -> i32 {
263 Self::convert_date_time_unit(DateTimeField::Month, DateTimeField::Millennium, self.months)
264 .unwrap()
265 }
266
267 pub fn centuries(&self) -> i32 {
272 Self::convert_date_time_unit(DateTimeField::Month, DateTimeField::Century, self.months)
273 .unwrap()
274 }
275
276 pub fn decades(&self) -> i32 {
281 Self::convert_date_time_unit(DateTimeField::Month, DateTimeField::Decade, self.months)
282 .unwrap()
283 }
284
285 pub fn years(&self) -> i32 {
290 Self::convert_date_time_unit(DateTimeField::Month, DateTimeField::Year, self.months)
291 .unwrap()
292 }
293
294 pub fn quarters(&self) -> i32 {
300 self.months() / 3 + 1
301 }
302
303 pub fn months(&self) -> i32 {
309 self.months % i32::from(Self::MONTH_PER_YEAR)
310 }
311
312 pub fn days(&self) -> i64 {
318 self.days.into()
319 }
320
321 pub fn hours(&self) -> i64 {
327 Self::convert_date_time_unit(
328 DateTimeField::Microseconds,
329 DateTimeField::Hour,
330 self.micros,
331 )
332 .unwrap()
333 % i64::from(Self::HOUR_PER_DAY)
334 }
335
336 pub fn minutes(&self) -> i64 {
342 Self::convert_date_time_unit(
343 DateTimeField::Microseconds,
344 DateTimeField::Minute,
345 self.micros,
346 )
347 .unwrap()
348 % i64::from(Self::MINUTE_PER_HOUR)
349 }
350
351 pub fn seconds<T>(&self) -> T
356 where
357 T: DecimalLike,
358 {
359 T::lossy_from(self.micros % 60_000_000) / T::from(1e6)
360 }
361
362 pub fn milliseconds<T>(&self) -> T
367 where
368 T: DecimalLike,
369 {
370 T::lossy_from(self.micros % 60_000_000) / T::from(1e3)
371 }
372
373 pub fn microseconds<T>(&self) -> T
378 where
379 T: DecimalLike,
380 {
381 T::lossy_from(self.micros % 60_000_000)
382 }
383
384 pub fn nanoseconds(&self) -> i32 {
386 (self.micros % 1_000_000 * 1_000).try_into().unwrap()
387 }
388
389 pub fn as_epoch_seconds<T>(&self) -> T
393 where
394 T: DecimalLike,
395 {
396 let days = T::from(self.years()) * T::from(Self::EPOCH_DAYS_PER_YEAR)
397 + T::from(self.months()) * T::from(Self::DAY_PER_MONTH)
398 + T::from(self.days);
399 let seconds = days
400 * T::from(Self::HOUR_PER_DAY)
401 * T::from(Self::MINUTE_PER_HOUR)
402 * T::from(Self::SECOND_PER_MINUTE);
403
404 seconds
405 + T::lossy_from(self.micros)
406 / (T::from(Self::MICROSECOND_PER_MILLISECOND)
407 * T::from(Self::MILLISECOND_PER_SECOND))
408 }
409
410 pub fn as_microseconds(&self) -> i128 {
412 Self::convert_date_time_unit(
415 DateTimeField::Month,
416 DateTimeField::Microseconds,
417 i128::from(self.months),
418 ).unwrap() +
419 Self::convert_date_time_unit(
422 DateTimeField::Day,
423 DateTimeField::Microseconds,
424 i128::from(self.days),
425 ).unwrap() +
426 i128::from(self.micros)
427 }
428
429 pub fn as_milliseconds(&self) -> i128 {
431 self.as_microseconds() / 1000
432 }
433
434 pub fn duration_as_chrono(&self) -> chrono::Duration {
436 use chrono::Duration;
437 Duration::try_days(self.days.into()).unwrap() + Duration::microseconds(self.micros)
438 }
439
440 pub fn duration(&self) -> Result<Duration, anyhow::Error> {
441 if self.months != 0 {
442 bail!("cannot convert interval with months to duration");
443 }
444 if self.is_negative() {
445 bail!("cannot convert negative interval to duration");
446 }
447 let micros: u64 = u64::try_from(self.as_microseconds())?;
448 Ok(Duration::from_micros(micros))
449 }
450
451 pub fn truncate_low_fields(
460 &mut self,
461 f: DateTimeField,
462 fsec_max_precision: Option<u64>,
463 round_behavior: RoundBehavior,
464 ) -> Result<(), anyhow::Error> {
465 use DateTimeField::*;
466 match f {
467 Millennium => {
468 self.months -= self.months % (12 * 1000);
469 self.days = 0;
470 self.micros = 0;
471 }
472 Century => {
473 self.months -= self.months % (12 * 100);
474 self.days = 0;
475 self.micros = 0;
476 }
477 Decade => {
478 self.months -= self.months % (12 * 10);
479 self.days = 0;
480 self.micros = 0;
481 }
482 Year => {
483 self.months -= self.months % 12;
484 self.days = 0;
485 self.micros = 0;
486 }
487 Month => {
488 self.days = 0;
489 self.micros = 0;
490 }
491 Second => {
493 let default_precision = 6;
494 let precision = match fsec_max_precision {
495 Some(p) => p,
496 None => default_precision,
497 };
498
499 if precision > default_precision {
500 bail!(
501 "SECOND precision must be (0, 6), have SECOND({})",
502 precision
503 )
504 }
505
506 let precision = match u32::try_from(precision) {
507 Ok(p) => p,
508 Err(_) => bail!(
509 "SECOND precision must be (0, 6), have SECOND({})",
510 precision
511 ),
512 };
513 let remainder = self.micros % 10_i64.pow(6 - precision);
515 self.micros -= remainder;
516 if matches!(round_behavior, RoundBehavior::Nearest)
518 && u64::from(precision) != default_precision
519 {
520 let rounding_digit = remainder / 10_i64.pow(5 - precision);
521 let micros = if rounding_digit > 4 {
522 self.micros.checked_add(10_i64.pow(6 - precision))
523 } else if rounding_digit < -4 {
524 self.micros.checked_sub(10_i64.pow(6 - precision))
525 } else {
526 Some(self.micros)
527 };
528 let Some(micros) = micros else {
529 bail!("interval field value out of range: \"{self}\"");
530 };
531 self.micros = micros;
532 }
533 }
534 Day => {
535 self.micros = 0;
536 }
537 Hour | Minute | Milliseconds | Microseconds => {
538 self.micros -= self.micros % f.micros_multiplier();
539 }
540 }
541 Ok(())
542 }
543
544 pub fn as_time_interval(&self) -> Self {
546 Self::new(0, 0, self.micros)
547 }
548
549 pub fn is_negative(&self) -> bool {
551 self.as_microseconds() < 0
552 }
553
554 pub fn convert_date_time_unit<T>(
561 source: DateTimeField,
562 dest: DateTimeField,
563 val: T,
564 ) -> Option<T>
565 where
566 T: From<u16> + CheckedMul + std::ops::DivAssign,
567 {
568 if source < dest {
569 Self::convert_date_time_unit_increasing(source, dest, val)
570 } else if source > dest {
571 Self::convert_date_time_unit_decreasing(source, dest, val)
572 } else {
573 Some(val)
574 }
575 }
576
577 fn convert_date_time_unit_increasing<T>(
578 source: DateTimeField,
579 dest: DateTimeField,
580 val: T,
581 ) -> Option<T>
582 where
583 T: From<u16> + std::ops::DivAssign,
584 {
585 let mut cur_unit = source;
586 let mut res = val;
587 while cur_unit < dest {
588 let divisor: T = match cur_unit {
589 DateTimeField::Millennium => 1.into(),
590 DateTimeField::Century => Self::CENTURY_PER_MILLENNIUM.into(),
591 DateTimeField::Decade => Self::DECADE_PER_CENTURY.into(),
592 DateTimeField::Year => Self::YEAR_PER_DECADE.into(),
593 DateTimeField::Month => Self::MONTH_PER_YEAR.into(),
594 DateTimeField::Day => Self::DAY_PER_MONTH.into(),
595 DateTimeField::Hour => Self::HOUR_PER_DAY.into(),
596 DateTimeField::Minute => Self::MINUTE_PER_HOUR.into(),
597 DateTimeField::Second => Self::SECOND_PER_MINUTE.into(),
598 DateTimeField::Milliseconds => Self::MILLISECOND_PER_SECOND.into(),
599 DateTimeField::Microseconds => Self::MICROSECOND_PER_MILLISECOND.into(),
600 };
601 res /= divisor;
602 cur_unit = cur_unit.next_largest();
603 }
604
605 Some(res)
606 }
607
608 fn convert_date_time_unit_decreasing<T>(
609 source: DateTimeField,
610 dest: DateTimeField,
611 val: T,
612 ) -> Option<T>
613 where
614 T: From<u16> + CheckedMul,
615 {
616 let mut cur_unit = source;
617 let mut res = val;
618 while cur_unit > dest {
619 let multiplier: T = match cur_unit {
620 DateTimeField::Millennium => Self::CENTURY_PER_MILLENNIUM.into(),
621 DateTimeField::Century => Self::DECADE_PER_CENTURY.into(),
622 DateTimeField::Decade => Self::YEAR_PER_DECADE.into(),
623 DateTimeField::Year => Self::MONTH_PER_YEAR.into(),
624 DateTimeField::Month => Self::DAY_PER_MONTH.into(),
625 DateTimeField::Day => Self::HOUR_PER_DAY.into(),
626 DateTimeField::Hour => Self::MINUTE_PER_HOUR.into(),
627 DateTimeField::Minute => Self::SECOND_PER_MINUTE.into(),
628 DateTimeField::Second => Self::MILLISECOND_PER_SECOND.into(),
629 DateTimeField::Milliseconds => Self::MICROSECOND_PER_MILLISECOND.into(),
630 DateTimeField::Microseconds => 1.into(),
631 };
632 res = match res.checked_mul(&multiplier) {
633 Some(r) => r,
634 None => return None,
635 };
636 cur_unit = cur_unit.next_smallest();
637 }
638
639 Some(res)
640 }
641
642 pub fn justify_days(&self) -> Result<Self, anyhow::Error> {
644 let days_per_month = i32::from(Self::DAY_PER_MONTH);
645 let (mut months, mut days) = Self::justify_days_inner(self.months, self.days)?;
646 if months > 0 && days < 0 {
647 days += days_per_month;
648 months -= 1;
649 } else if months < 0 && days > 0 {
650 days -= days_per_month;
651 months += 1;
652 }
653
654 Ok(Self::new(months, days, self.micros))
655 }
656
657 fn justify_days_inner(months: i32, days: i32) -> Result<(i32, i32), anyhow::Error> {
658 let days_per_month = i32::from(Self::DAY_PER_MONTH);
659 let whole_month = days / days_per_month;
660 let days = days - whole_month * days_per_month;
661
662 let months = months
663 .checked_add(whole_month)
664 .ok_or_else(|| anyhow!(&*MONTH_OVERFLOW_ERROR))?;
665
666 Ok((months, days))
667 }
668
669 pub fn justify_hours(&self) -> Result<Self, anyhow::Error> {
671 let (mut days, mut micros) = Self::justify_hours_inner(self.days, self.micros)?;
672 if days > 0 && micros < 0 {
673 micros += &*USECS_PER_DAY;
674 days -= 1;
675 } else if days < 0 && micros > 0 {
676 micros -= &*USECS_PER_DAY;
677 days += 1;
678 }
679
680 Ok(Self::new(self.months, days, micros))
681 }
682
683 fn justify_hours_inner(days: i32, micros: i64) -> Result<(i32, i64), anyhow::Error> {
684 let days = i32::try_from(micros / &*USECS_PER_DAY)
685 .ok()
686 .and_then(|d| days.checked_add(d))
687 .ok_or_else(|| anyhow!(&*DAY_OVERFLOW_ERROR))?;
688 let micros = micros % &*USECS_PER_DAY;
689
690 Ok((days, micros))
691 }
692
693 pub fn justify_interval(&self) -> Result<Self, anyhow::Error> {
697 let days_per_month = i32::from(Self::DAY_PER_MONTH);
698 let mut months = self.months;
699 let mut days = self.days;
700 let micros = self.micros;
701 if (days > 0 && micros > 0) || (days < 0 && micros < 0) {
704 let (m, d) = Self::justify_days_inner(self.months, self.days)?;
705 months = m;
706 days = d;
707 }
708 let (days, mut micros) = Self::justify_hours_inner(days, micros)?;
709 let (mut months, mut days) = Self::justify_days_inner(months, days)?;
710
711 if months > 0 && (days < 0 || (days == 0 && micros < 0)) {
712 days += days_per_month;
713 months -= 1;
714 } else if months < 0 && (days > 0 || (days == 0 && micros > 0)) {
715 days -= days_per_month;
716 months += 1;
717 }
718
719 if days > 0 && micros < 0 {
720 micros += &*USECS_PER_DAY;
721 days -= 1;
722 } else if days < 0 && micros > 0 {
723 micros -= &*USECS_PER_DAY;
724 days += 1;
725 }
726
727 Ok(Self::new(months, days, micros))
728 }
729}
730
731impl fmt::Display for Interval {
739 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
740 let neg_months = self.months < 0;
741 let years = (self.months / 12).abs();
742 let months = (self.months % 12).abs();
743
744 let neg_days = self.days < 0;
745 let days = i64::from(self.days).abs();
746
747 let mut nanos = self.nanoseconds().abs();
748 let mut secs = (self.micros / 1_000_000).abs();
749
750 let sec_per_hr = 60 * 60;
751 let hours = secs / sec_per_hr;
752 secs %= sec_per_hr;
753
754 let sec_per_min = 60;
755 let minutes = secs / sec_per_min;
756 secs %= sec_per_min;
757
758 if years > 0 {
759 if neg_months {
760 f.write_char('-')?;
761 }
762 write!(f, "{} year", years)?;
763 if years > 1 || neg_months {
764 f.write_char('s')?;
765 }
766 }
767
768 if months > 0 {
769 if years != 0 {
770 f.write_char(' ')?;
771 }
772 if neg_months {
773 f.write_char('-')?;
774 }
775 write!(f, "{} mon", months)?;
776 if months > 1 || neg_months {
777 f.write_char('s')?;
778 }
779 }
780
781 if days != 0 {
782 if years > 0 || months > 0 {
783 f.write_char(' ')?;
784 }
785 if neg_months && !neg_days {
786 f.write_char('+')?;
787 }
788 write!(f, "{} day", self.days)?;
789 if self.days != 1 {
790 f.write_char('s')?;
791 }
792 }
793
794 let non_zero_hmsn = hours > 0 || minutes > 0 || secs > 0 || nanos > 0;
795
796 if (years == 0 && months == 0 && days == 0) || non_zero_hmsn {
797 if years > 0 || months > 0 || days > 0 {
798 f.write_char(' ')?;
799 }
800 if self.micros < 0 && non_zero_hmsn {
801 f.write_char('-')?;
802 } else if neg_days || (days == 0 && neg_months) {
803 f.write_char('+')?;
804 }
805 write!(f, "{:02}:{:02}:{:02}", hours, minutes, secs)?;
806 if nanos > 0 {
807 let mut width = 9;
808 while nanos % 10 == 0 {
809 width -= 1;
810 nanos /= 10;
811 }
812 write!(f, ".{:0width$}", nanos, width = width)?;
813 }
814 }
815
816 Ok(())
817 }
818}
819
820#[cfg(any(test, feature = "proptest"))]
821impl Arbitrary for Interval {
822 type Strategy = BoxedStrategy<Self>;
823 type Parameters = ();
824
825 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
826 (
827 any::<i32>(),
828 any::<i32>(),
829 ((((i64::from(i32::MIN) * 60) - 59) * 60) * 1_000_000 - 59_999_999
830 ..(((i64::from(i32::MAX) * 60) + 59) * 60) * 1_000_000 + 59_999_999),
831 )
832 .prop_map(|(months, days, micros)| Interval {
833 months,
834 days,
835 micros,
836 })
837 .boxed()
838 }
839}
840
841#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
845pub struct PackedInterval([u8; Self::SIZE]);
846
847#[allow(clippy::as_conversions)]
851impl FixedSizeCodec<Interval> for PackedInterval {
852 const SIZE: usize = 16;
853
854 fn as_bytes(&self) -> &[u8] {
855 &self.0[..]
856 }
857
858 fn from_bytes(slice: &[u8]) -> Result<Self, String> {
859 let buf: [u8; Self::SIZE] = slice.try_into().map_err(|_| {
860 format!(
861 "size for PackedInterval is {} bytes, got {}",
862 Self::SIZE,
863 slice.len()
864 )
865 })?;
866 Ok(PackedInterval(buf))
867 }
868
869 #[inline]
870 fn from_value(value: Interval) -> Self {
871 let mut buf = [0u8; 16];
872
873 let months = (value.months as u32) ^ (0x8000_0000u32);
876 let days = (value.days as u32) ^ (0x8000_0000u32);
877 let micros = (value.micros as u64) ^ (0x8000_0000_0000_0000u64);
878
879 buf[..4].copy_from_slice(&months.to_be_bytes());
880 buf[4..8].copy_from_slice(&days.to_be_bytes());
881 buf[8..].copy_from_slice(µs.to_be_bytes());
882
883 PackedInterval(buf)
884 }
885
886 #[inline]
887 fn into_value(self) -> Interval {
888 let mut months = [0; 4];
891 months.copy_from_slice(&self.0[..4]);
892 let months = u32::from_be_bytes(months) ^ 0x8000_0000u32;
893
894 let mut days = [0; 4];
895 days.copy_from_slice(&self.0[4..8]);
896 let days = u32::from_be_bytes(days) ^ 0x8000_0000u32;
897
898 let mut micros = [0; 8];
899 micros.copy_from_slice(&self.0[8..]);
900 let micros = u64::from_be_bytes(micros) ^ 0x8000_0000_0000_0000u64;
901
902 Interval {
903 months: months as i32,
904 days: days as i32,
905 micros: micros as i64,
906 }
907 }
908}
909
910#[cfg(test)]
911mod test {
912 use super::*;
913 use proptest::prelude::*;
914
915 #[mz_ore::test]
916 fn interval_fmt() {
917 fn mon(mon: i32) -> String {
918 Interval {
919 months: mon,
920 ..Default::default()
921 }
922 .to_string()
923 }
924
925 assert_eq!(mon(1), "1 mon");
926 assert_eq!(mon(12), "1 year");
927 assert_eq!(mon(13), "1 year 1 mon");
928 assert_eq!(mon(24), "2 years");
929 assert_eq!(mon(25), "2 years 1 mon");
930 assert_eq!(mon(26), "2 years 2 mons");
931
932 fn dur(days: i32, micros: i64) -> String {
933 Interval::new(0, days, micros).to_string()
934 }
935 assert_eq!(&dur(2, 0), "2 days");
936 assert_eq!(&dur(2, 3 * 60 * 60 * 1_000_000), "2 days 03:00:00");
937 assert_eq!(
938 &dur(
939 2,
940 (3 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (6 * 1_000_000)
941 ),
942 "2 days 03:45:06"
943 );
944 assert_eq!(
945 &dur(2, (3 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000)),
946 "2 days 03:45:00"
947 );
948 assert_eq!(&dur(2, 6 * 1_000_000), "2 days 00:00:06");
949 assert_eq!(
950 &dur(2, (45 * 60 * 1_000_000) + (6 * 1_000_000)),
951 "2 days 00:45:06"
952 );
953 assert_eq!(
954 &dur(2, (3 * 60 * 60 * 1_000_000) + (6 * 1_000_000)),
955 "2 days 03:00:06"
956 );
957 assert_eq!(
958 &dur(
959 0,
960 (3 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (6 * 1_000_000)
961 ),
962 "03:45:06"
963 );
964 assert_eq!(
965 &dur(0, (3 * 60 * 60 * 1_000_000) + (6 * 1_000_000)),
966 "03:00:06"
967 );
968 assert_eq!(&dur(0, 3 * 60 * 60 * 1_000_000), "03:00:00");
969 assert_eq!(&dur(0, (45 * 60 * 1_000_000) + (6 * 1_000_000)), "00:45:06");
970 assert_eq!(&dur(0, 45 * 60 * 1_000_000), "00:45:00");
971 assert_eq!(&dur(0, 6 * 1_000_000), "00:00:06");
972
973 assert_eq!(&dur(-2, -6 * 1_000_000), "-2 days -00:00:06");
974 assert_eq!(
975 &dur(-2, (-45 * 60 * 1_000_000) + (-6 * 1_000_000)),
976 "-2 days -00:45:06"
977 );
978 assert_eq!(
979 &dur(-2, (-3 * 60 * 60 * 1_000_000) + (-6 * 1_000_000)),
980 "-2 days -03:00:06"
981 );
982 assert_eq!(
983 &dur(
984 0,
985 (-3 * 60 * 60 * 1_000_000) + (-45 * 60 * 1_000_000) + (-6 * 1_000_000)
986 ),
987 "-03:45:06"
988 );
989 assert_eq!(
990 &dur(0, (-3 * 60 * 60 * 1_000_000) + (-6 * 1_000_000)),
991 "-03:00:06"
992 );
993 assert_eq!(&dur(0, -3 * 60 * 60 * 1_000_000), "-03:00:00");
994 assert_eq!(
995 &dur(0, (-45 * 60 * 1_000_000) + (-6 * 1_000_000)),
996 "-00:45:06"
997 );
998 assert_eq!(&dur(0, -45 * 60 * 1_000_000), "-00:45:00");
999 assert_eq!(&dur(0, -6 * 1_000_000), "-00:00:06");
1000
1001 fn mon_dur(mon: i32, days: i32, micros: i64) -> String {
1002 Interval::new(mon, days, micros).to_string()
1003 }
1004 assert_eq!(&mon_dur(1, 2, 6 * 1_000_000), "1 mon 2 days 00:00:06");
1005 assert_eq!(
1006 &mon_dur(1, 2, (45 * 60 * 1_000_000) + (6 * 1_000_000)),
1007 "1 mon 2 days 00:45:06"
1008 );
1009 assert_eq!(
1010 &mon_dur(1, 2, (3 * 60 * 60 * 1_000_000) + (6 * 1_000_000)),
1011 "1 mon 2 days 03:00:06"
1012 );
1013 assert_eq!(
1014 &mon_dur(
1015 26,
1016 0,
1017 (3 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (6 * 1_000_000)
1018 ),
1019 "2 years 2 mons 03:45:06"
1020 );
1021 assert_eq!(
1022 &mon_dur(26, 0, (3 * 60 * 60 * 1_000_000) + (6 * 1_000_000)),
1023 "2 years 2 mons 03:00:06"
1024 );
1025 assert_eq!(
1026 &mon_dur(26, 0, 3 * 60 * 60 * 1_000_000),
1027 "2 years 2 mons 03:00:00"
1028 );
1029 assert_eq!(
1030 &mon_dur(26, 0, (45 * 60 * 1_000_000) + (6 * 1_000_000)),
1031 "2 years 2 mons 00:45:06"
1032 );
1033 assert_eq!(
1034 &mon_dur(26, 0, 45 * 60 * 1_000_000),
1035 "2 years 2 mons 00:45:00"
1036 );
1037 assert_eq!(&mon_dur(26, 0, 6 * 1_000_000), "2 years 2 mons 00:00:06");
1038
1039 assert_eq!(
1040 &mon_dur(26, -2, -6 * 1_000_000),
1041 "2 years 2 mons -2 days -00:00:06"
1042 );
1043 assert_eq!(
1044 &mon_dur(26, -2, (-45 * 60 * 1_000_000) + (-6 * 1_000_000)),
1045 "2 years 2 mons -2 days -00:45:06"
1046 );
1047 assert_eq!(
1048 &mon_dur(26, -2, (-3 * 60 * 60 * 1_000_000) + (-6 * 1_000_000)),
1049 "2 years 2 mons -2 days -03:00:06"
1050 );
1051 assert_eq!(
1052 &mon_dur(
1053 26,
1054 0,
1055 (-3 * 60 * 60 * 1_000_000) + (-45 * 60 * 1_000_000) + (-6 * 1_000_000)
1056 ),
1057 "2 years 2 mons -03:45:06"
1058 );
1059 assert_eq!(
1060 &mon_dur(26, 0, (-3 * 60 * 60 * 1_000_000) + (-6 * 1_000_000)),
1061 "2 years 2 mons -03:00:06"
1062 );
1063 assert_eq!(
1064 &mon_dur(26, 0, -3 * 60 * 60 * 1_000_000),
1065 "2 years 2 mons -03:00:00"
1066 );
1067 assert_eq!(
1068 &mon_dur(26, 0, (-45 * 60 * 1_000_000) + (-6 * 1_000_000)),
1069 "2 years 2 mons -00:45:06"
1070 );
1071 assert_eq!(
1072 &mon_dur(26, 0, -45 * 60 * 1_000_000),
1073 "2 years 2 mons -00:45:00"
1074 );
1075 assert_eq!(&mon_dur(26, 0, -6 * 1_000_000), "2 years 2 mons -00:00:06");
1076
1077 assert_eq!(&mon_dur(-1, 2, 6 * 1_000_000), "-1 mons +2 days 00:00:06");
1078 assert_eq!(
1079 &mon_dur(-1, 2, (45 * 60 * 1_000_000) + (6 * 1_000_000)),
1080 "-1 mons +2 days 00:45:06"
1081 );
1082 assert_eq!(
1083 &mon_dur(-1, 2, (3 * 60 * 60 * 1_000_000) + (6 * 1_000_000)),
1084 "-1 mons +2 days 03:00:06"
1085 );
1086 assert_eq!(
1087 &mon_dur(
1088 -26,
1089 0,
1090 (3 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (6 * 1_000_000)
1091 ),
1092 "-2 years -2 mons +03:45:06"
1093 );
1094 assert_eq!(
1095 &mon_dur(-26, 0, (3 * 60 * 60 * 1_000_000) + (6 * 1_000_000)),
1096 "-2 years -2 mons +03:00:06"
1097 );
1098 assert_eq!(
1099 &mon_dur(-26, 0, 3 * 60 * 60 * 1_000_000),
1100 "-2 years -2 mons +03:00:00"
1101 );
1102 assert_eq!(
1103 &mon_dur(-26, 0, (45 * 60 * 1_000_000) + (6 * 1_000_000)),
1104 "-2 years -2 mons +00:45:06"
1105 );
1106 assert_eq!(
1107 &mon_dur(-26, 0, 45 * 60 * 1_000_000),
1108 "-2 years -2 mons +00:45:00"
1109 );
1110 assert_eq!(
1111 &mon_dur(-26, 0, 6 * 1_000_000),
1112 "-2 years -2 mons +00:00:06"
1113 );
1114
1115 assert_eq!(
1116 &mon_dur(-26, -2, -6 * 1_000_000),
1117 "-2 years -2 mons -2 days -00:00:06"
1118 );
1119 assert_eq!(
1120 &mon_dur(-26, -2, (-45 * 60 * 1_000_000) + (-6 * 1_000_000)),
1121 "-2 years -2 mons -2 days -00:45:06"
1122 );
1123 assert_eq!(
1124 &mon_dur(-26, -2, (-3 * 60 * 60 * 1_000_000) + (-6 * 1_000_000)),
1125 "-2 years -2 mons -2 days -03:00:06"
1126 );
1127 assert_eq!(
1128 &mon_dur(
1129 -26,
1130 0,
1131 (-3 * 60 * 60 * 1_000_000) + (-45 * 60 * 1_000_000) + (-6 * 1_000_000)
1132 ),
1133 "-2 years -2 mons -03:45:06"
1134 );
1135 assert_eq!(
1136 &mon_dur(-26, 0, (-3 * 60 * 60 * 1_000_000) + (-6 * 1_000_000)),
1137 "-2 years -2 mons -03:00:06"
1138 );
1139 assert_eq!(
1140 &mon_dur(-26, 0, -3 * 60 * 60 * 1_000_000),
1141 "-2 years -2 mons -03:00:00"
1142 );
1143 assert_eq!(
1144 &mon_dur(-26, 0, (-45 * 60 * 1_000_000) + (-6 * 1_000_000)),
1145 "-2 years -2 mons -00:45:06"
1146 );
1147 assert_eq!(
1148 &mon_dur(-26, 0, -45 * 60 * 1_000_000),
1149 "-2 years -2 mons -00:45:00"
1150 );
1151 assert_eq!(
1152 &mon_dur(-26, 0, -6 * 1_000_000),
1153 "-2 years -2 mons -00:00:06"
1154 );
1155 }
1156
1157 #[mz_ore::test]
1158 fn test_interval_value_truncate_low_fields() {
1159 use DateTimeField::*;
1160
1161 let mut test_cases = [
1162 (
1163 Year,
1164 None,
1165 (
1166 321,
1167 7,
1168 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000) + 321_000,
1169 ),
1170 (26 * 12, 0, 0),
1171 ),
1172 (
1173 Month,
1174 None,
1175 (
1176 321,
1177 7,
1178 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000) + 321_000,
1179 ),
1180 (321, 0, 0),
1181 ),
1182 (
1183 Day,
1184 None,
1185 (
1186 321,
1187 7,
1188 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000) + 321_000,
1189 ),
1190 (321, 7, 0),
1191 ),
1192 (
1193 Hour,
1194 None,
1195 (
1196 321,
1197 7,
1198 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000) + 321_000,
1199 ),
1200 (321, 7, 13 * 60 * 60 * 1_000_000),
1201 ),
1202 (
1203 Minute,
1204 None,
1205 (
1206 321,
1207 7,
1208 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000) + 321_000,
1209 ),
1210 (321, 7, (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000)),
1211 ),
1212 (
1213 Second,
1214 None,
1215 (
1216 321,
1217 7,
1218 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000) + 321_000,
1219 ),
1220 (
1221 321,
1222 7,
1223 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000) + 321_000,
1224 ),
1225 ),
1226 (
1227 Second,
1228 Some(1),
1229 (
1230 321,
1231 7,
1232 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000) + 321_000,
1233 ),
1234 (
1235 321,
1236 7,
1237 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000) + 300_000,
1238 ),
1239 ),
1240 (
1241 Second,
1242 Some(0),
1243 (
1244 321,
1245 7,
1246 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000) + 321_000,
1247 ),
1248 (
1249 321,
1250 7,
1251 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000),
1252 ),
1253 ),
1254 ];
1255
1256 for test in test_cases.iter_mut() {
1257 let mut i = Interval::new((test.2).0, (test.2).1, (test.2).2);
1258 let j = Interval::new((test.3).0, (test.3).1, (test.3).2);
1259
1260 i.truncate_low_fields(test.0, test.1, RoundBehavior::Nearest)
1261 .unwrap();
1262
1263 if i != j {
1264 panic!(
1265 "test_interval_value_truncate_low_fields failed on {} \n actual: {:?} \n expected: {:?}",
1266 test.0, i, j
1267 );
1268 }
1269 }
1270 }
1271
1272 #[mz_ore::test]
1273 fn test_convert_date_time_unit() {
1274 assert_eq!(
1275 Some(1_123_200_000_000),
1276 Interval::convert_date_time_unit(
1277 DateTimeField::Day,
1278 DateTimeField::Microseconds,
1279 13i64
1280 )
1281 );
1282
1283 assert_eq!(
1284 Some(3_558_399_705),
1285 Interval::convert_date_time_unit(
1286 DateTimeField::Milliseconds,
1287 DateTimeField::Month,
1288 i64::MAX
1289 )
1290 );
1291
1292 assert_eq!(
1293 None,
1294 Interval::convert_date_time_unit(
1295 DateTimeField::Minute,
1296 DateTimeField::Second,
1297 i32::MAX
1298 )
1299 );
1300
1301 assert_eq!(
1302 Some(1),
1303 Interval::convert_date_time_unit(DateTimeField::Day, DateTimeField::Year, 365)
1304 );
1305
1306 assert_eq!(
1308 Some(360),
1309 Interval::convert_date_time_unit(DateTimeField::Year, DateTimeField::Day, 1)
1310 );
1311 }
1312
1313 #[mz_ore::test]
1314 fn proptest_packed_interval_roundtrips() {
1315 fn roundtrip_interval(og: Interval) {
1316 let packed = PackedInterval::from_value(og);
1317 let rnd = packed.into_value();
1318
1319 assert_eq!(og, rnd);
1320 }
1321
1322 proptest!(|(interval in any::<Interval>())| {
1323 roundtrip_interval(interval);
1324 });
1325 }
1326
1327 #[mz_ore::test]
1328 fn proptest_packed_interval_sorts() {
1329 fn sort_intervals(mut og: Vec<Interval>) {
1330 let mut packed: Vec<_> = og.iter().copied().map(PackedInterval::from_value).collect();
1331
1332 og.sort();
1333 packed.sort();
1334
1335 let rnd: Vec<_> = packed.into_iter().map(PackedInterval::into_value).collect();
1336
1337 assert_eq!(og, rnd);
1338 }
1339
1340 proptest!(|(interval in any::<Vec<Interval>>())| {
1341 sort_intervals(interval);
1342 });
1343 }
1344}