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_high_fields(&mut self, f: DateTimeField) {
453 match f {
454 DateTimeField::Year => {}
455 DateTimeField::Month => self.months %= 12,
456 DateTimeField::Day => self.months = 0,
457 DateTimeField::Hour | DateTimeField::Minute | DateTimeField::Second => {
458 self.months = 0;
459 self.days = 0;
460 self.micros %= f.next_largest().micros_multiplier()
461 }
462 DateTimeField::Millennium
463 | DateTimeField::Century
464 | DateTimeField::Decade
465 | DateTimeField::Milliseconds
466 | DateTimeField::Microseconds => {
467 unreachable!("Cannot truncate interval by {f}");
468 }
469 }
470 }
471
472 pub fn truncate_low_fields(
481 &mut self,
482 f: DateTimeField,
483 fsec_max_precision: Option<u64>,
484 round_behavior: RoundBehavior,
485 ) -> Result<(), anyhow::Error> {
486 use DateTimeField::*;
487 match f {
488 Millennium => {
489 self.months -= self.months % (12 * 1000);
490 self.days = 0;
491 self.micros = 0;
492 }
493 Century => {
494 self.months -= self.months % (12 * 100);
495 self.days = 0;
496 self.micros = 0;
497 }
498 Decade => {
499 self.months -= self.months % (12 * 10);
500 self.days = 0;
501 self.micros = 0;
502 }
503 Year => {
504 self.months -= self.months % 12;
505 self.days = 0;
506 self.micros = 0;
507 }
508 Month => {
509 self.days = 0;
510 self.micros = 0;
511 }
512 Second => {
514 let default_precision = 6;
515 let precision = match fsec_max_precision {
516 Some(p) => p,
517 None => default_precision,
518 };
519
520 if precision > default_precision {
521 bail!(
522 "SECOND precision must be (0, 6), have SECOND({})",
523 precision
524 )
525 }
526
527 let precision = match u32::try_from(precision) {
528 Ok(p) => p,
529 Err(_) => bail!(
530 "SECOND precision must be (0, 6), have SECOND({})",
531 precision
532 ),
533 };
534 let remainder = self.micros % 10_i64.pow(6 - precision);
536 self.micros -= remainder;
537 if matches!(round_behavior, RoundBehavior::Nearest)
539 && u64::from(precision) != default_precision
540 {
541 let rounding_digit = remainder / 10_i64.pow(5 - precision);
542 let micros = if rounding_digit > 4 {
543 self.micros.checked_add(10_i64.pow(6 - precision))
544 } else if rounding_digit < -4 {
545 self.micros.checked_sub(10_i64.pow(6 - precision))
546 } else {
547 Some(self.micros)
548 };
549 let Some(micros) = micros else {
550 bail!("interval field value out of range: \"{self}\"");
551 };
552 self.micros = micros;
553 }
554 }
555 Day => {
556 self.micros = 0;
557 }
558 Hour | Minute | Milliseconds | Microseconds => {
559 self.micros -= self.micros % f.micros_multiplier();
560 }
561 }
562 Ok(())
563 }
564
565 pub fn as_time_interval(&self) -> Self {
567 Self::new(0, 0, self.micros)
568 }
569
570 pub fn is_negative(&self) -> bool {
572 self.as_microseconds() < 0
573 }
574
575 pub fn convert_date_time_unit<T>(
582 source: DateTimeField,
583 dest: DateTimeField,
584 val: T,
585 ) -> Option<T>
586 where
587 T: From<u16> + CheckedMul + std::ops::DivAssign,
588 {
589 if source < dest {
590 Self::convert_date_time_unit_increasing(source, dest, val)
591 } else if source > dest {
592 Self::convert_date_time_unit_decreasing(source, dest, val)
593 } else {
594 Some(val)
595 }
596 }
597
598 fn convert_date_time_unit_increasing<T>(
599 source: DateTimeField,
600 dest: DateTimeField,
601 val: T,
602 ) -> Option<T>
603 where
604 T: From<u16> + std::ops::DivAssign,
605 {
606 let mut cur_unit = source;
607 let mut res = val;
608 while cur_unit < dest {
609 let divisor: T = match cur_unit {
610 DateTimeField::Millennium => 1.into(),
611 DateTimeField::Century => Self::CENTURY_PER_MILLENNIUM.into(),
612 DateTimeField::Decade => Self::DECADE_PER_CENTURY.into(),
613 DateTimeField::Year => Self::YEAR_PER_DECADE.into(),
614 DateTimeField::Month => Self::MONTH_PER_YEAR.into(),
615 DateTimeField::Day => Self::DAY_PER_MONTH.into(),
616 DateTimeField::Hour => Self::HOUR_PER_DAY.into(),
617 DateTimeField::Minute => Self::MINUTE_PER_HOUR.into(),
618 DateTimeField::Second => Self::SECOND_PER_MINUTE.into(),
619 DateTimeField::Milliseconds => Self::MILLISECOND_PER_SECOND.into(),
620 DateTimeField::Microseconds => Self::MICROSECOND_PER_MILLISECOND.into(),
621 };
622 res /= divisor;
623 cur_unit = cur_unit.next_largest();
624 }
625
626 Some(res)
627 }
628
629 fn convert_date_time_unit_decreasing<T>(
630 source: DateTimeField,
631 dest: DateTimeField,
632 val: T,
633 ) -> Option<T>
634 where
635 T: From<u16> + CheckedMul,
636 {
637 let mut cur_unit = source;
638 let mut res = val;
639 while cur_unit > dest {
640 let multiplier: T = match cur_unit {
641 DateTimeField::Millennium => Self::CENTURY_PER_MILLENNIUM.into(),
642 DateTimeField::Century => Self::DECADE_PER_CENTURY.into(),
643 DateTimeField::Decade => Self::YEAR_PER_DECADE.into(),
644 DateTimeField::Year => Self::MONTH_PER_YEAR.into(),
645 DateTimeField::Month => Self::DAY_PER_MONTH.into(),
646 DateTimeField::Day => Self::HOUR_PER_DAY.into(),
647 DateTimeField::Hour => Self::MINUTE_PER_HOUR.into(),
648 DateTimeField::Minute => Self::SECOND_PER_MINUTE.into(),
649 DateTimeField::Second => Self::MILLISECOND_PER_SECOND.into(),
650 DateTimeField::Milliseconds => Self::MICROSECOND_PER_MILLISECOND.into(),
651 DateTimeField::Microseconds => 1.into(),
652 };
653 res = match res.checked_mul(&multiplier) {
654 Some(r) => r,
655 None => return None,
656 };
657 cur_unit = cur_unit.next_smallest();
658 }
659
660 Some(res)
661 }
662
663 pub fn justify_days(&self) -> Result<Self, anyhow::Error> {
665 let days_per_month = i32::from(Self::DAY_PER_MONTH);
666 let (mut months, mut days) = Self::justify_days_inner(self.months, self.days)?;
667 if months > 0 && days < 0 {
668 days += days_per_month;
669 months -= 1;
670 } else if months < 0 && days > 0 {
671 days -= days_per_month;
672 months += 1;
673 }
674
675 Ok(Self::new(months, days, self.micros))
676 }
677
678 fn justify_days_inner(months: i32, days: i32) -> Result<(i32, i32), anyhow::Error> {
679 let days_per_month = i32::from(Self::DAY_PER_MONTH);
680 let whole_month = days / days_per_month;
681 let days = days - whole_month * days_per_month;
682
683 let months = months
684 .checked_add(whole_month)
685 .ok_or_else(|| anyhow!(&*MONTH_OVERFLOW_ERROR))?;
686
687 Ok((months, days))
688 }
689
690 pub fn justify_hours(&self) -> Result<Self, anyhow::Error> {
692 let (mut days, mut micros) = Self::justify_hours_inner(self.days, self.micros)?;
693 if days > 0 && micros < 0 {
694 micros += &*USECS_PER_DAY;
695 days -= 1;
696 } else if days < 0 && micros > 0 {
697 micros -= &*USECS_PER_DAY;
698 days += 1;
699 }
700
701 Ok(Self::new(self.months, days, micros))
702 }
703
704 fn justify_hours_inner(days: i32, micros: i64) -> Result<(i32, i64), anyhow::Error> {
705 let days = i32::try_from(micros / &*USECS_PER_DAY)
706 .ok()
707 .and_then(|d| days.checked_add(d))
708 .ok_or_else(|| anyhow!(&*DAY_OVERFLOW_ERROR))?;
709 let micros = micros % &*USECS_PER_DAY;
710
711 Ok((days, micros))
712 }
713
714 pub fn justify_interval(&self) -> Result<Self, anyhow::Error> {
718 let days_per_month = i32::from(Self::DAY_PER_MONTH);
719 let mut months = self.months;
720 let mut days = self.days;
721 let micros = self.micros;
722 if (days > 0 && micros > 0) || (days < 0 && micros < 0) {
725 let (m, d) = Self::justify_days_inner(self.months, self.days)?;
726 months = m;
727 days = d;
728 }
729 let (days, mut micros) = Self::justify_hours_inner(days, micros)?;
730 let (mut months, mut days) = Self::justify_days_inner(months, days)?;
731
732 if months > 0 && (days < 0 || (days == 0 && micros < 0)) {
733 days += days_per_month;
734 months -= 1;
735 } else if months < 0 && (days > 0 || (days == 0 && micros > 0)) {
736 days -= days_per_month;
737 months += 1;
738 }
739
740 if days > 0 && micros < 0 {
741 micros += &*USECS_PER_DAY;
742 days -= 1;
743 } else if days < 0 && micros > 0 {
744 micros -= &*USECS_PER_DAY;
745 days += 1;
746 }
747
748 Ok(Self::new(months, days, micros))
749 }
750}
751
752impl fmt::Display for Interval {
760 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
761 let neg_months = self.months < 0;
762 let years = (self.months / 12).abs();
763 let months = (self.months % 12).abs();
764
765 let neg_days = self.days < 0;
766 let days = i64::from(self.days).abs();
767
768 let mut nanos = self.nanoseconds().abs();
769 let mut secs = (self.micros / 1_000_000).abs();
770
771 let sec_per_hr = 60 * 60;
772 let hours = secs / sec_per_hr;
773 secs %= sec_per_hr;
774
775 let sec_per_min = 60;
776 let minutes = secs / sec_per_min;
777 secs %= sec_per_min;
778
779 if years > 0 {
780 if neg_months {
781 f.write_char('-')?;
782 }
783 write!(f, "{} year", years)?;
784 if years > 1 || neg_months {
785 f.write_char('s')?;
786 }
787 }
788
789 if months > 0 {
790 if years != 0 {
791 f.write_char(' ')?;
792 }
793 if neg_months {
794 f.write_char('-')?;
795 }
796 write!(f, "{} month", months)?;
797 if months > 1 || neg_months {
798 f.write_char('s')?;
799 }
800 }
801
802 if days != 0 {
803 if years > 0 || months > 0 {
804 f.write_char(' ')?;
805 }
806 if neg_months && !neg_days {
807 f.write_char('+')?;
808 }
809 write!(f, "{} day", self.days)?;
810 if self.days != 1 {
811 f.write_char('s')?;
812 }
813 }
814
815 let non_zero_hmsn = hours > 0 || minutes > 0 || secs > 0 || nanos > 0;
816
817 if (years == 0 && months == 0 && days == 0) || non_zero_hmsn {
818 if years > 0 || months > 0 || days > 0 {
819 f.write_char(' ')?;
820 }
821 if self.micros < 0 && non_zero_hmsn {
822 f.write_char('-')?;
823 } else if neg_days || (days == 0 && neg_months) {
824 f.write_char('+')?;
825 }
826 write!(f, "{:02}:{:02}:{:02}", hours, minutes, secs)?;
827 if nanos > 0 {
828 let mut width = 9;
829 while nanos % 10 == 0 {
830 width -= 1;
831 nanos /= 10;
832 }
833 write!(f, ".{:0width$}", nanos, width = width)?;
834 }
835 }
836
837 Ok(())
838 }
839}
840
841#[cfg(any(test, feature = "proptest"))]
842impl Arbitrary for Interval {
843 type Strategy = BoxedStrategy<Self>;
844 type Parameters = ();
845
846 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
847 (
848 any::<i32>(),
849 any::<i32>(),
850 ((((i64::from(i32::MIN) * 60) - 59) * 60) * 1_000_000 - 59_999_999
851 ..(((i64::from(i32::MAX) * 60) + 59) * 60) * 1_000_000 + 59_999_999),
852 )
853 .prop_map(|(months, days, micros)| Interval {
854 months,
855 days,
856 micros,
857 })
858 .boxed()
859 }
860}
861
862#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
866pub struct PackedInterval([u8; Self::SIZE]);
867
868#[allow(clippy::as_conversions)]
872impl FixedSizeCodec<Interval> for PackedInterval {
873 const SIZE: usize = 16;
874
875 fn as_bytes(&self) -> &[u8] {
876 &self.0[..]
877 }
878
879 fn from_bytes(slice: &[u8]) -> Result<Self, String> {
880 let buf: [u8; Self::SIZE] = slice.try_into().map_err(|_| {
881 format!(
882 "size for PackedInterval is {} bytes, got {}",
883 Self::SIZE,
884 slice.len()
885 )
886 })?;
887 Ok(PackedInterval(buf))
888 }
889
890 #[inline]
891 fn from_value(value: Interval) -> Self {
892 let mut buf = [0u8; 16];
893
894 let months = (value.months as u32) ^ (0x8000_0000u32);
897 let days = (value.days as u32) ^ (0x8000_0000u32);
898 let micros = (value.micros as u64) ^ (0x8000_0000_0000_0000u64);
899
900 buf[..4].copy_from_slice(&months.to_be_bytes());
901 buf[4..8].copy_from_slice(&days.to_be_bytes());
902 buf[8..].copy_from_slice(µs.to_be_bytes());
903
904 PackedInterval(buf)
905 }
906
907 #[inline]
908 fn into_value(self) -> Interval {
909 let mut months = [0; 4];
912 months.copy_from_slice(&self.0[..4]);
913 let months = u32::from_be_bytes(months) ^ 0x8000_0000u32;
914
915 let mut days = [0; 4];
916 days.copy_from_slice(&self.0[4..8]);
917 let days = u32::from_be_bytes(days) ^ 0x8000_0000u32;
918
919 let mut micros = [0; 8];
920 micros.copy_from_slice(&self.0[8..]);
921 let micros = u64::from_be_bytes(micros) ^ 0x8000_0000_0000_0000u64;
922
923 Interval {
924 months: months as i32,
925 days: days as i32,
926 micros: micros as i64,
927 }
928 }
929}
930
931#[cfg(test)]
932mod test {
933 use super::*;
934 use proptest::prelude::*;
935
936 #[mz_ore::test]
937 fn interval_fmt() {
938 fn mon(mon: i32) -> String {
939 Interval {
940 months: mon,
941 ..Default::default()
942 }
943 .to_string()
944 }
945
946 assert_eq!(mon(1), "1 month");
947 assert_eq!(mon(12), "1 year");
948 assert_eq!(mon(13), "1 year 1 month");
949 assert_eq!(mon(24), "2 years");
950 assert_eq!(mon(25), "2 years 1 month");
951 assert_eq!(mon(26), "2 years 2 months");
952
953 fn dur(days: i32, micros: i64) -> String {
954 Interval::new(0, days, micros).to_string()
955 }
956 assert_eq!(&dur(2, 0), "2 days");
957 assert_eq!(&dur(2, 3 * 60 * 60 * 1_000_000), "2 days 03:00:00");
958 assert_eq!(
959 &dur(
960 2,
961 (3 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (6 * 1_000_000)
962 ),
963 "2 days 03:45:06"
964 );
965 assert_eq!(
966 &dur(2, (3 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000)),
967 "2 days 03:45:00"
968 );
969 assert_eq!(&dur(2, 6 * 1_000_000), "2 days 00:00:06");
970 assert_eq!(
971 &dur(2, (45 * 60 * 1_000_000) + (6 * 1_000_000)),
972 "2 days 00:45:06"
973 );
974 assert_eq!(
975 &dur(2, (3 * 60 * 60 * 1_000_000) + (6 * 1_000_000)),
976 "2 days 03:00:06"
977 );
978 assert_eq!(
979 &dur(
980 0,
981 (3 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (6 * 1_000_000)
982 ),
983 "03:45:06"
984 );
985 assert_eq!(
986 &dur(0, (3 * 60 * 60 * 1_000_000) + (6 * 1_000_000)),
987 "03:00:06"
988 );
989 assert_eq!(&dur(0, 3 * 60 * 60 * 1_000_000), "03:00:00");
990 assert_eq!(&dur(0, (45 * 60 * 1_000_000) + (6 * 1_000_000)), "00:45:06");
991 assert_eq!(&dur(0, 45 * 60 * 1_000_000), "00:45:00");
992 assert_eq!(&dur(0, 6 * 1_000_000), "00:00:06");
993
994 assert_eq!(&dur(-2, -6 * 1_000_000), "-2 days -00:00:06");
995 assert_eq!(
996 &dur(-2, (-45 * 60 * 1_000_000) + (-6 * 1_000_000)),
997 "-2 days -00:45:06"
998 );
999 assert_eq!(
1000 &dur(-2, (-3 * 60 * 60 * 1_000_000) + (-6 * 1_000_000)),
1001 "-2 days -03:00:06"
1002 );
1003 assert_eq!(
1004 &dur(
1005 0,
1006 (-3 * 60 * 60 * 1_000_000) + (-45 * 60 * 1_000_000) + (-6 * 1_000_000)
1007 ),
1008 "-03:45:06"
1009 );
1010 assert_eq!(
1011 &dur(0, (-3 * 60 * 60 * 1_000_000) + (-6 * 1_000_000)),
1012 "-03:00:06"
1013 );
1014 assert_eq!(&dur(0, -3 * 60 * 60 * 1_000_000), "-03:00:00");
1015 assert_eq!(
1016 &dur(0, (-45 * 60 * 1_000_000) + (-6 * 1_000_000)),
1017 "-00:45:06"
1018 );
1019 assert_eq!(&dur(0, -45 * 60 * 1_000_000), "-00:45:00");
1020 assert_eq!(&dur(0, -6 * 1_000_000), "-00:00:06");
1021
1022 fn mon_dur(mon: i32, days: i32, micros: i64) -> String {
1023 Interval::new(mon, days, micros).to_string()
1024 }
1025 assert_eq!(&mon_dur(1, 2, 6 * 1_000_000), "1 month 2 days 00:00:06");
1026 assert_eq!(
1027 &mon_dur(1, 2, (45 * 60 * 1_000_000) + (6 * 1_000_000)),
1028 "1 month 2 days 00:45:06"
1029 );
1030 assert_eq!(
1031 &mon_dur(1, 2, (3 * 60 * 60 * 1_000_000) + (6 * 1_000_000)),
1032 "1 month 2 days 03:00:06"
1033 );
1034 assert_eq!(
1035 &mon_dur(
1036 26,
1037 0,
1038 (3 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (6 * 1_000_000)
1039 ),
1040 "2 years 2 months 03:45:06"
1041 );
1042 assert_eq!(
1043 &mon_dur(26, 0, (3 * 60 * 60 * 1_000_000) + (6 * 1_000_000)),
1044 "2 years 2 months 03:00:06"
1045 );
1046 assert_eq!(
1047 &mon_dur(26, 0, 3 * 60 * 60 * 1_000_000),
1048 "2 years 2 months 03:00:00"
1049 );
1050 assert_eq!(
1051 &mon_dur(26, 0, (45 * 60 * 1_000_000) + (6 * 1_000_000)),
1052 "2 years 2 months 00:45:06"
1053 );
1054 assert_eq!(
1055 &mon_dur(26, 0, 45 * 60 * 1_000_000),
1056 "2 years 2 months 00:45:00"
1057 );
1058 assert_eq!(&mon_dur(26, 0, 6 * 1_000_000), "2 years 2 months 00:00:06");
1059
1060 assert_eq!(
1061 &mon_dur(26, -2, -6 * 1_000_000),
1062 "2 years 2 months -2 days -00:00:06"
1063 );
1064 assert_eq!(
1065 &mon_dur(26, -2, (-45 * 60 * 1_000_000) + (-6 * 1_000_000)),
1066 "2 years 2 months -2 days -00:45:06"
1067 );
1068 assert_eq!(
1069 &mon_dur(26, -2, (-3 * 60 * 60 * 1_000_000) + (-6 * 1_000_000)),
1070 "2 years 2 months -2 days -03:00:06"
1071 );
1072 assert_eq!(
1073 &mon_dur(
1074 26,
1075 0,
1076 (-3 * 60 * 60 * 1_000_000) + (-45 * 60 * 1_000_000) + (-6 * 1_000_000)
1077 ),
1078 "2 years 2 months -03:45:06"
1079 );
1080 assert_eq!(
1081 &mon_dur(26, 0, (-3 * 60 * 60 * 1_000_000) + (-6 * 1_000_000)),
1082 "2 years 2 months -03:00:06"
1083 );
1084 assert_eq!(
1085 &mon_dur(26, 0, -3 * 60 * 60 * 1_000_000),
1086 "2 years 2 months -03:00:00"
1087 );
1088 assert_eq!(
1089 &mon_dur(26, 0, (-45 * 60 * 1_000_000) + (-6 * 1_000_000)),
1090 "2 years 2 months -00:45:06"
1091 );
1092 assert_eq!(
1093 &mon_dur(26, 0, -45 * 60 * 1_000_000),
1094 "2 years 2 months -00:45:00"
1095 );
1096 assert_eq!(
1097 &mon_dur(26, 0, -6 * 1_000_000),
1098 "2 years 2 months -00:00:06"
1099 );
1100
1101 assert_eq!(&mon_dur(-1, 2, 6 * 1_000_000), "-1 months +2 days 00:00:06");
1102 assert_eq!(
1103 &mon_dur(-1, 2, (45 * 60 * 1_000_000) + (6 * 1_000_000)),
1104 "-1 months +2 days 00:45:06"
1105 );
1106 assert_eq!(
1107 &mon_dur(-1, 2, (3 * 60 * 60 * 1_000_000) + (6 * 1_000_000)),
1108 "-1 months +2 days 03:00:06"
1109 );
1110 assert_eq!(
1111 &mon_dur(
1112 -26,
1113 0,
1114 (3 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (6 * 1_000_000)
1115 ),
1116 "-2 years -2 months +03:45:06"
1117 );
1118 assert_eq!(
1119 &mon_dur(-26, 0, (3 * 60 * 60 * 1_000_000) + (6 * 1_000_000)),
1120 "-2 years -2 months +03:00:06"
1121 );
1122 assert_eq!(
1123 &mon_dur(-26, 0, 3 * 60 * 60 * 1_000_000),
1124 "-2 years -2 months +03:00:00"
1125 );
1126 assert_eq!(
1127 &mon_dur(-26, 0, (45 * 60 * 1_000_000) + (6 * 1_000_000)),
1128 "-2 years -2 months +00:45:06"
1129 );
1130 assert_eq!(
1131 &mon_dur(-26, 0, 45 * 60 * 1_000_000),
1132 "-2 years -2 months +00:45:00"
1133 );
1134 assert_eq!(
1135 &mon_dur(-26, 0, 6 * 1_000_000),
1136 "-2 years -2 months +00:00:06"
1137 );
1138
1139 assert_eq!(
1140 &mon_dur(-26, -2, -6 * 1_000_000),
1141 "-2 years -2 months -2 days -00:00:06"
1142 );
1143 assert_eq!(
1144 &mon_dur(-26, -2, (-45 * 60 * 1_000_000) + (-6 * 1_000_000)),
1145 "-2 years -2 months -2 days -00:45:06"
1146 );
1147 assert_eq!(
1148 &mon_dur(-26, -2, (-3 * 60 * 60 * 1_000_000) + (-6 * 1_000_000)),
1149 "-2 years -2 months -2 days -03:00:06"
1150 );
1151 assert_eq!(
1152 &mon_dur(
1153 -26,
1154 0,
1155 (-3 * 60 * 60 * 1_000_000) + (-45 * 60 * 1_000_000) + (-6 * 1_000_000)
1156 ),
1157 "-2 years -2 months -03:45:06"
1158 );
1159 assert_eq!(
1160 &mon_dur(-26, 0, (-3 * 60 * 60 * 1_000_000) + (-6 * 1_000_000)),
1161 "-2 years -2 months -03:00:06"
1162 );
1163 assert_eq!(
1164 &mon_dur(-26, 0, -3 * 60 * 60 * 1_000_000),
1165 "-2 years -2 months -03:00:00"
1166 );
1167 assert_eq!(
1168 &mon_dur(-26, 0, (-45 * 60 * 1_000_000) + (-6 * 1_000_000)),
1169 "-2 years -2 months -00:45:06"
1170 );
1171 assert_eq!(
1172 &mon_dur(-26, 0, -45 * 60 * 1_000_000),
1173 "-2 years -2 months -00:45:00"
1174 );
1175 assert_eq!(
1176 &mon_dur(-26, 0, -6 * 1_000_000),
1177 "-2 years -2 months -00:00:06"
1178 );
1179 }
1180
1181 #[mz_ore::test]
1182 fn test_interval_value_truncate_low_fields() {
1183 use DateTimeField::*;
1184
1185 let mut test_cases = [
1186 (
1187 Year,
1188 None,
1189 (
1190 321,
1191 7,
1192 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000) + 321_000,
1193 ),
1194 (26 * 12, 0, 0),
1195 ),
1196 (
1197 Month,
1198 None,
1199 (
1200 321,
1201 7,
1202 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000) + 321_000,
1203 ),
1204 (321, 0, 0),
1205 ),
1206 (
1207 Day,
1208 None,
1209 (
1210 321,
1211 7,
1212 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000) + 321_000,
1213 ),
1214 (321, 7, 0),
1215 ),
1216 (
1217 Hour,
1218 None,
1219 (
1220 321,
1221 7,
1222 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000) + 321_000,
1223 ),
1224 (321, 7, 13 * 60 * 60 * 1_000_000),
1225 ),
1226 (
1227 Minute,
1228 None,
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 (321, 7, (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000)),
1235 ),
1236 (
1237 Second,
1238 None,
1239 (
1240 321,
1241 7,
1242 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000) + 321_000,
1243 ),
1244 (
1245 321,
1246 7,
1247 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000) + 321_000,
1248 ),
1249 ),
1250 (
1251 Second,
1252 Some(1),
1253 (
1254 321,
1255 7,
1256 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000) + 321_000,
1257 ),
1258 (
1259 321,
1260 7,
1261 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000) + 300_000,
1262 ),
1263 ),
1264 (
1265 Second,
1266 Some(0),
1267 (
1268 321,
1269 7,
1270 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000) + 321_000,
1271 ),
1272 (
1273 321,
1274 7,
1275 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000),
1276 ),
1277 ),
1278 ];
1279
1280 for test in test_cases.iter_mut() {
1281 let mut i = Interval::new((test.2).0, (test.2).1, (test.2).2);
1282 let j = Interval::new((test.3).0, (test.3).1, (test.3).2);
1283
1284 i.truncate_low_fields(test.0, test.1, RoundBehavior::Nearest)
1285 .unwrap();
1286
1287 if i != j {
1288 panic!(
1289 "test_interval_value_truncate_low_fields failed on {} \n actual: {:?} \n expected: {:?}",
1290 test.0, i, j
1291 );
1292 }
1293 }
1294 }
1295
1296 #[mz_ore::test]
1297 fn test_interval_value_truncate_high_fields() {
1298 use DateTimeField::*;
1299
1300 let mut test_cases = [
1302 (
1303 Year,
1304 (
1305 321,
1306 7,
1307 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000),
1308 ),
1309 (
1310 321,
1311 7,
1312 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000),
1313 ),
1314 ),
1315 (
1316 Month,
1317 (
1318 321,
1319 7,
1320 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000),
1321 ),
1322 (
1323 9,
1324 7,
1325 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000),
1326 ),
1327 ),
1328 (
1329 Day,
1330 (
1331 321,
1332 7,
1333 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000),
1334 ),
1335 (
1336 0,
1337 7,
1338 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000),
1339 ),
1340 ),
1341 (
1342 Hour,
1343 (
1344 321,
1345 7,
1346 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000),
1347 ),
1348 (
1349 0,
1350 0,
1351 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000),
1352 ),
1353 ),
1354 (
1355 Minute,
1356 (
1357 321,
1358 7,
1359 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000),
1360 ),
1361 (0, 0, (45 * 60 * 1_000_000) + (21 * 1_000_000)),
1362 ),
1363 (
1364 Second,
1365 (
1366 321,
1367 7,
1368 (13 * 60 * 60 * 1_000_000) + (45 * 60 * 1_000_000) + (21 * 1_000_000),
1369 ),
1370 (0, 0, 21 * 1_000_000),
1371 ),
1372 ];
1373
1374 for test in test_cases.iter_mut() {
1375 let mut i = Interval::new((test.1).0, (test.1).1, (test.1).2);
1376 let j = Interval::new((test.2).0, (test.2).1, (test.2).2);
1377
1378 i.truncate_high_fields(test.0);
1379
1380 if i != j {
1381 panic!(
1382 "test_interval_value_truncate_high_fields failed on {} \n actual: {:?} \n expected: {:?}",
1383 test.0, i, j
1384 );
1385 }
1386 }
1387 }
1388
1389 #[mz_ore::test]
1390 fn test_convert_date_time_unit() {
1391 assert_eq!(
1392 Some(1_123_200_000_000),
1393 Interval::convert_date_time_unit(
1394 DateTimeField::Day,
1395 DateTimeField::Microseconds,
1396 13i64
1397 )
1398 );
1399
1400 assert_eq!(
1401 Some(3_558_399_705),
1402 Interval::convert_date_time_unit(
1403 DateTimeField::Milliseconds,
1404 DateTimeField::Month,
1405 i64::MAX
1406 )
1407 );
1408
1409 assert_eq!(
1410 None,
1411 Interval::convert_date_time_unit(
1412 DateTimeField::Minute,
1413 DateTimeField::Second,
1414 i32::MAX
1415 )
1416 );
1417
1418 assert_eq!(
1419 Some(1),
1420 Interval::convert_date_time_unit(DateTimeField::Day, DateTimeField::Year, 365)
1421 );
1422
1423 assert_eq!(
1425 Some(360),
1426 Interval::convert_date_time_unit(DateTimeField::Year, DateTimeField::Day, 1)
1427 );
1428 }
1429
1430 #[mz_ore::test]
1431 fn proptest_packed_interval_roundtrips() {
1432 fn roundtrip_interval(og: Interval) {
1433 let packed = PackedInterval::from_value(og);
1434 let rnd = packed.into_value();
1435
1436 assert_eq!(og, rnd);
1437 }
1438
1439 proptest!(|(interval in any::<Interval>())| {
1440 roundtrip_interval(interval);
1441 });
1442 }
1443
1444 #[mz_ore::test]
1445 fn proptest_packed_interval_sorts() {
1446 fn sort_intervals(mut og: Vec<Interval>) {
1447 let mut packed: Vec<_> = og.iter().copied().map(PackedInterval::from_value).collect();
1448
1449 og.sort();
1450 packed.sort();
1451
1452 let rnd: Vec<_> = packed.into_iter().map(PackedInterval::into_value).collect();
1453
1454 assert_eq!(og, rnd);
1455 }
1456
1457 proptest!(|(interval in any::<Vec<Interval>>())| {
1458 sort_intervals(interval);
1459 });
1460 }
1461}