Skip to main content

mz_repr/adt/
datetime.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! Date and time utilities.
11
12#![allow(missing_docs)]
13
14use std::collections::VecDeque;
15use std::fmt;
16use std::str::FromStr;
17
18use chrono::{NaiveDate, NaiveTime, Timelike};
19use mz_persist_types::columnar::FixedSizeCodec;
20use mz_pgtz::timezone::Timezone;
21#[cfg(any(test, feature = "proptest"))]
22use proptest_derive::Arbitrary;
23use serde::{Deserialize, Serialize};
24
25use crate::adt::interval::Interval;
26
27/// Units of measurements associated with dates and times.
28///
29/// TODO(benesch): with enough thinking, this type could probably be merged with
30/// `DateTimeField`.
31#[derive(
32    Clone,
33    Copy,
34    Debug,
35    PartialOrd,
36    Ord,
37    PartialEq,
38    Eq,
39    Hash,
40    Serialize,
41    Deserialize
42)]
43#[cfg_attr(any(test, feature = "proptest"), derive(Arbitrary))]
44pub enum DateTimeUnits {
45    Epoch,
46    Millennium,
47    Century,
48    Decade,
49    Year,
50    Quarter,
51    Week,
52    Month,
53    Hour,
54    Day,
55    DayOfWeek,
56    DayOfYear,
57    IsoDayOfWeek,
58    IsoDayOfYear,
59    Minute,
60    Second,
61    Milliseconds,
62    Microseconds,
63    Timezone,
64    TimezoneHour,
65    TimezoneMinute,
66}
67
68impl fmt::Display for DateTimeUnits {
69    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
70        match self {
71            Self::Epoch => f.write_str("epoch"),
72            Self::Millennium => f.write_str("millennium"),
73            Self::Century => f.write_str("century"),
74            Self::Decade => f.write_str("decade"),
75            Self::Year => f.write_str("year"),
76            Self::Quarter => f.write_str("quarter"),
77            Self::Week => f.write_str("week"),
78            Self::Month => f.write_str("month"),
79            Self::Hour => f.write_str("hour"),
80            Self::Day => f.write_str("day"),
81            Self::DayOfWeek => f.write_str("dow"),
82            Self::DayOfYear => f.write_str("doy"),
83            Self::IsoDayOfWeek => f.write_str("isodow"),
84            Self::IsoDayOfYear => f.write_str("isodoy"),
85            Self::Minute => f.write_str("minute"),
86            Self::Second => f.write_str("seconds"),
87            Self::Milliseconds => f.write_str("milliseconds"),
88            Self::Microseconds => f.write_str("microseconds"),
89            Self::Timezone => f.write_str("timezone"),
90            Self::TimezoneHour => f.write_str("timezone_hour"),
91            Self::TimezoneMinute => f.write_str("timezone_minute"),
92        }
93    }
94}
95
96impl FromStr for DateTimeUnits {
97    type Err = String;
98
99    fn from_str(s: &str) -> Result<Self, Self::Err> {
100        match s.to_lowercase().as_str() {
101            "epoch" => Ok(Self::Epoch),
102            "mil" | "millennia" | "millennium" | "millenniums" => Ok(Self::Millennium),
103            "c" | "cent" | "century" | "centuries" => Ok(Self::Century),
104            "dec" | "decs" | "decade" | "decades" => Ok(Self::Decade),
105            "y" | "year" | "years" | "yr" | "yrs" => Ok(Self::Year),
106            "qtr" | "quarter" => Ok(Self::Quarter),
107            "w" | "week" | "weeks" => Ok(Self::Week),
108            "d" | "day" | "days" => Ok(Self::Day),
109            "dow" => Ok(Self::DayOfWeek),
110            "doy" => Ok(Self::DayOfYear),
111            "isodow" => Ok(Self::IsoDayOfWeek),
112            "isodoy" => Ok(Self::IsoDayOfYear),
113            "h" | "hour" | "hours" | "hr" | "hrs" => Ok(Self::Hour),
114            "us" | "usec" | "usecs" | "useconds" | "microsecond" | "microseconds" => {
115                Ok(Self::Microseconds)
116            }
117            "m" | "min" | "mins" | "minute" | "minutes" => Ok(Self::Minute),
118            "mon" | "mons" | "month" | "months" => Ok(Self::Month),
119            "ms" | "msec" | "msecs" | "mseconds" | "millisecond" | "milliseconds" => {
120                Ok(Self::Milliseconds)
121            }
122            "s" | "sec" | "second" | "seconds" | "secs" => Ok(Self::Second),
123            "timezone" => Ok(Self::Timezone),
124            "timezone_h" | "timezone_hour" => Ok(Self::TimezoneHour),
125            "timezone_m" | "timezone_minute" => Ok(Self::TimezoneMinute),
126            _ => Err(format!("unknown units {}", s)),
127        }
128    }
129}
130
131/// Order of definition is important for PartialOrd and Ord to be derived correctly
132#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
133pub enum DateTimePart {
134    Microseconds,
135    Milliseconds,
136    Second,
137    Minute,
138    Hour,
139    Day,
140    Week,
141    Month,
142    Quarter,
143    Year,
144    Decade,
145    Century,
146    Millennium,
147}
148
149impl FromStr for DateTimePart {
150    type Err = String;
151
152    fn from_str(s: &str) -> Result<Self, Self::Err> {
153        let units: DateTimeUnits = s.parse()?;
154
155        match units {
156            DateTimeUnits::Microseconds => Ok(DateTimePart::Microseconds),
157            DateTimeUnits::Milliseconds => Ok(DateTimePart::Milliseconds),
158            DateTimeUnits::Second => Ok(DateTimePart::Second),
159            DateTimeUnits::Minute => Ok(DateTimePart::Minute),
160            DateTimeUnits::Hour => Ok(DateTimePart::Hour),
161            DateTimeUnits::Day => Ok(DateTimePart::Day),
162            DateTimeUnits::Week => Ok(DateTimePart::Week),
163            DateTimeUnits::Month => Ok(DateTimePart::Month),
164            DateTimeUnits::Quarter => Ok(DateTimePart::Quarter),
165            DateTimeUnits::Year => Ok(DateTimePart::Year),
166            DateTimeUnits::Decade => Ok(DateTimePart::Decade),
167            DateTimeUnits::Century => Ok(DateTimePart::Century),
168            DateTimeUnits::Millennium => Ok(DateTimePart::Millennium),
169            _ => Err(s.to_string()),
170        }
171    }
172}
173
174// Order of definition is important for PartialOrd and Ord to be derived correctly
175#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
176pub enum DateTimeField {
177    Microseconds,
178    Milliseconds,
179    Second,
180    Minute,
181    Hour,
182    Day,
183    Month,
184    Year,
185    Decade,
186    Century,
187    Millennium,
188}
189
190impl fmt::Display for DateTimeField {
191    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
192        f.write_str(match self {
193            DateTimeField::Millennium => "MILLENNIUM",
194            DateTimeField::Century => "CENTURY",
195            DateTimeField::Decade => "DECADE",
196            DateTimeField::Year => "YEAR",
197            DateTimeField::Month => "MONTH",
198            DateTimeField::Day => "DAY",
199            DateTimeField::Hour => "HOUR",
200            DateTimeField::Minute => "MINUTE",
201            DateTimeField::Second => "SECOND",
202            DateTimeField::Milliseconds => "MILLISECONDS",
203            DateTimeField::Microseconds => "MICROSECONDS",
204        })
205    }
206}
207
208/// Iterate over `DateTimeField`s in descending significance
209impl IntoIterator for DateTimeField {
210    type Item = DateTimeField;
211    type IntoIter = DateTimeFieldIterator;
212    fn into_iter(self) -> DateTimeFieldIterator {
213        DateTimeFieldIterator(Some(self))
214    }
215}
216
217impl FromStr for DateTimeField {
218    type Err = String;
219
220    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
221        match s.to_uppercase().as_ref() {
222            "MILLENNIUM" | "MILLENNIUMS" | "MILLENNIA" | "MIL" | "MILS" => Ok(Self::Millennium),
223            "CENTURY" | "CENTURIES" | "CENT" | "C" => Ok(Self::Century),
224            "DECADE" | "DECADES" | "DEC" | "DECS" => Ok(Self::Decade),
225            "YEAR" | "YEARS" | "YR" | "YRS" | "Y" => Ok(Self::Year),
226            "MONTH" | "MONTHS" | "MON" | "MONS" => Ok(Self::Month),
227            "DAY" | "DAYS" | "D" => Ok(Self::Day),
228            "HOUR" | "HOURS" | "HR" | "HRS" | "H" => Ok(Self::Hour),
229            "MINUTE" | "MINUTES" | "MIN" | "MINS" | "M" => Ok(Self::Minute),
230            "SECOND" | "SECONDS" | "SEC" | "SECS" | "S" => Ok(Self::Second),
231            "MILLISECOND" | "MILLISECONDS" | "MILLISECON" | "MILLISECONS" | "MSECOND"
232            | "MSECONDS" | "MSEC" | "MSECS" | "MS" => Ok(Self::Milliseconds),
233            "MICROSECOND" | "MICROSECONDS" | "MICROSECON" | "MICROSECONS" | "USECOND"
234            | "USECONDS" | "USEC" | "USECS" | "US" => Ok(Self::Microseconds),
235            _ => Err(format!("invalid DateTimeField: {}", s)),
236        }
237    }
238}
239
240impl DateTimeField {
241    /// Iterate the DateTimeField to the next value.
242    /// # Panics
243    /// - When called on Second
244    pub fn next_smallest(self) -> Self {
245        self.into_iter()
246            .next()
247            .unwrap_or_else(|| panic!("Cannot get smaller DateTimeField than {}", self))
248    }
249    /// Iterate the DateTimeField to the prior value.
250    /// # Panics
251    /// - When called on Year
252    pub fn next_largest(self) -> Self {
253        self.into_iter()
254            .next_back()
255            .unwrap_or_else(|| panic!("Cannot get larger DateTimeField than {}", self))
256    }
257
258    /// Returns the number of microseconds in a single unit of `field`.
259    ///
260    /// # Panics
261    ///
262    /// Panics if called on a non-time/day field.
263    pub fn micros_multiplier(self) -> i64 {
264        use DateTimeField::*;
265        match self {
266            Day | Hour | Minute | Second | Milliseconds | Microseconds => {}
267            _other => unreachable!("Do not call with a non-time/day field"),
268        }
269
270        Interval::convert_date_time_unit(self, Self::Microseconds, 1i64).unwrap()
271    }
272
273    /// Returns the number of months in a single unit of `field`.
274    ///
275    /// # Panics
276    ///
277    /// Panics if called on a duration field.
278    pub fn month_multiplier(self) -> i64 {
279        use DateTimeField::*;
280        match self {
281            Millennium | Century | Decade | Year => {}
282            _other => unreachable!("Do not call with a duration field"),
283        }
284
285        Interval::convert_date_time_unit(self, Self::Microseconds, 1i64).unwrap()
286    }
287}
288
289/// An iterator over DateTimeFields
290///
291/// Always starts with the value smaller than the current one.
292///
293/// ```
294/// use mz_repr::adt::datetime::DateTimeField::*;
295/// let mut itr = Hour.into_iter();
296/// assert_eq!(itr.next(), Some(Minute));
297/// assert_eq!(itr.next(), Some(Second));
298/// assert_eq!(itr.next(), Some(Milliseconds));
299/// assert_eq!(itr.next(), Some(Microseconds));
300/// assert_eq!(itr.next(), None);
301/// ```
302#[derive(Debug)]
303pub struct DateTimeFieldIterator(Option<DateTimeField>);
304
305/// Go through fields in descending significance order
306impl Iterator for DateTimeFieldIterator {
307    type Item = DateTimeField;
308    fn next(&mut self) -> Option<Self::Item> {
309        use DateTimeField::*;
310        self.0 = match self.0 {
311            Some(Millennium) => Some(Century),
312            Some(Century) => Some(Decade),
313            Some(Decade) => Some(Year),
314            Some(Year) => Some(Month),
315            Some(Month) => Some(Day),
316            Some(Day) => Some(Hour),
317            Some(Hour) => Some(Minute),
318            Some(Minute) => Some(Second),
319            Some(Second) => Some(Milliseconds),
320            Some(Milliseconds) => Some(Microseconds),
321            Some(Microseconds) => None,
322            None => None,
323        };
324        self.0.clone()
325    }
326}
327
328impl DoubleEndedIterator for DateTimeFieldIterator {
329    fn next_back(&mut self) -> Option<Self::Item> {
330        use DateTimeField::*;
331        self.0 = match self.0 {
332            Some(Millennium) => None,
333            Some(Century) => Some(Millennium),
334            Some(Decade) => Some(Century),
335            Some(Year) => Some(Decade),
336            Some(Month) => Some(Year),
337            Some(Day) => Some(Month),
338            Some(Hour) => Some(Day),
339            Some(Minute) => Some(Hour),
340            Some(Second) => Some(Minute),
341            Some(Milliseconds) => Some(Second),
342            Some(Microseconds) => Some(Milliseconds),
343            None => None,
344        };
345        self.0.clone()
346    }
347}
348
349/// Tracks a unit and a fraction from a parsed time-like string, e.g. INTERVAL
350/// '1.2' DAYS.
351#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
352pub struct DateTimeFieldValue {
353    /// Integer part of the value.
354    pub unit: i64,
355    /// Fractional part of value, padded to billions/has 9 digits of precision,
356    /// e.g. `.5` is represented as `500000000`.
357    pub fraction: i64,
358}
359
360impl Default for DateTimeFieldValue {
361    fn default() -> Self {
362        DateTimeFieldValue {
363            unit: 0,
364            fraction: 0,
365        }
366    }
367}
368
369impl DateTimeFieldValue {
370    /// Construct `DateTimeFieldValue { unit, fraction }`.
371    pub fn new(unit: i64, fraction: i64) -> Self {
372        DateTimeFieldValue { unit, fraction }
373    }
374
375    /// How much padding is added to the fractional portion to achieve a given precision.
376    /// e.g. with the current precision `.5` is represented as `500_000_000`.
377    const FRACTIONAL_DIGIT_PRECISION: i64 = 1_000_000_000;
378}
379
380/// All of the fields that can appear in a literal `DATE`, `TIME`, `TIMESTAMP`
381/// or `INTERVAL` string.
382#[derive(Debug, Clone, PartialEq, Eq, Hash)]
383pub struct ParsedDateTime {
384    pub millennium: Option<DateTimeFieldValue>,
385    pub century: Option<DateTimeFieldValue>,
386    pub decade: Option<DateTimeFieldValue>,
387    pub year: Option<DateTimeFieldValue>,
388    pub month: Option<DateTimeFieldValue>,
389    pub day: Option<DateTimeFieldValue>,
390    pub hour: Option<DateTimeFieldValue>,
391    pub minute: Option<DateTimeFieldValue>,
392    // second.fraction is equivalent to nanoseconds.
393    pub second: Option<DateTimeFieldValue>,
394    pub millisecond: Option<DateTimeFieldValue>,
395    pub microsecond: Option<DateTimeFieldValue>,
396    pub timezone_offset_second: Option<Timezone>,
397}
398
399impl Default for ParsedDateTime {
400    fn default() -> Self {
401        ParsedDateTime {
402            millennium: None,
403            century: None,
404            decade: None,
405            year: None,
406            month: None,
407            day: None,
408            hour: None,
409            minute: None,
410            second: None,
411            millisecond: None,
412            microsecond: None,
413            timezone_offset_second: None,
414        }
415    }
416}
417
418impl ParsedDateTime {
419    /// Compute an Interval from an ParsedDateTime.
420    ///
421    /// # Errors
422    /// - If any component overflows a parameter (i.e. i64).
423    pub fn compute_interval(&self) -> Result<Interval, String> {
424        use DateTimeField::*;
425        let mut months = 0i32;
426        let mut days = 0i32;
427        let mut micros = 0i64;
428
429        // Add all DateTimeFields, from Millennium to Microseconds.
430        self.add_field(Millennium, &mut months, &mut days, &mut micros)?;
431
432        for field in Millennium.into_iter().take_while(|f| *f >= Microseconds) {
433            self.add_field(field, &mut months, &mut days, &mut micros)?;
434        }
435
436        Ok(Interval::new(months, days, micros))
437    }
438    /// Adds the appropriate values from self's ParsedDateTime to `months`,
439    /// `days`, and `micros`. These fields are then appropriate to construct
440    /// std::time::Duration, once accounting for their sign.
441    ///
442    /// # Errors
443    /// - If any component overflows a parameter (i.e. i64).
444    fn add_field(
445        &self,
446        d: DateTimeField,
447        months: &mut i32,
448        days: &mut i32,
449        micros: &mut i64,
450    ) -> Result<(), String> {
451        use DateTimeField::*;
452        /// divide i by d rounding to the closest integer
453        fn div_and_round(i: i128, d: i64) -> Option<i64> {
454            let mut res = i / i128::from(d);
455            let round_digit = (i / (i128::from(d) / 10)) % 10;
456            if round_digit > 4 {
457                res += 1;
458            } else if round_digit < -4 {
459                res -= 1;
460            }
461            i64::try_from(res).ok()
462        }
463        match d {
464            Millennium | Century | Decade | Year => {
465                let (y, y_f) = match self.units_of(d) {
466                    Some(y) => (y.unit, y.fraction),
467                    None => return Ok(()),
468                };
469                // months += y.to_month()
470                *months = Interval::convert_date_time_unit(d, DateTimeField::Month, y)
471                    .and_then(|y_m| i32::try_from(y_m).ok())
472                    .and_then(|y_m| months.checked_add(y_m))
473                    .ok_or_else(|| {
474                        format!(
475                            "Overflows maximum months; cannot exceed {}/{} months",
476                            i32::MAX,
477                            i32::MIN,
478                        )
479                    })?;
480
481                // months += y_f.to_month() / DateTimeFieldValue::FRACTION_MULTIPLIER
482                *months = Interval::convert_date_time_unit(d, DateTimeField::Month, y_f)
483                    .and_then(|y_f_m| {
484                        y_f_m.checked_div(DateTimeFieldValue::FRACTIONAL_DIGIT_PRECISION)
485                    })
486                    .and_then(|y_f_m| i32::try_from(y_f_m).ok())
487                    .and_then(|y_f_m| months.checked_add(y_f_m))
488                    .ok_or_else(|| {
489                        format!(
490                            "Overflows maximum months; cannot exceed {}/{} months",
491                            i32::MAX,
492                            i32::MIN,
493                        )
494                    })?;
495                Ok(())
496            }
497            Month => {
498                let (m, m_f) = match self.units_of(Month) {
499                    Some(m) => (m.unit, m.fraction),
500                    None => return Ok(()),
501                };
502
503                // months += m
504                *months = i32::try_from(m)
505                    .ok()
506                    .and_then(|m_month| months.checked_add(m_month))
507                    .ok_or_else(|| {
508                        format!(
509                            "Overflows maximum months; cannot exceed {}/{} months",
510                            i32::MAX,
511                            i32::MIN,
512                        )
513                    })?;
514
515                let m_f_days = Interval::convert_date_time_unit(d, DateTimeField::Day, m_f)
516                    .ok_or_else(|| "Intermediate overflow in MONTH fraction".to_owned())?;
517                // days += m_f.to_day() / DateTimeFieldValue::FRACTION_MULTIPLIER
518                *days = m_f_days
519                    .checked_div(DateTimeFieldValue::FRACTIONAL_DIGIT_PRECISION)
520                    .and_then(|m_f_days| i32::try_from(m_f_days).ok())
521                    .and_then(|m_f_days| days.checked_add(m_f_days))
522                    .ok_or_else(|| {
523                        format!(
524                            "Overflows maximum seconds; cannot exceed {}/{} days",
525                            i32::MAX,
526                            i32::MIN,
527                        )
528                    })?;
529
530                // micros += (m_f.to_day() % DateTimeFieldValue::FRACTION_MULTIPLIER).to_micros() / DateTimeFieldValue::FRACTION_MULTIPLIER
531                *micros = i128::from(m_f_days)
532                    .checked_rem(DateTimeFieldValue::FRACTIONAL_DIGIT_PRECISION.into())
533                    .and_then(|m_f_us| {
534                        Interval::convert_date_time_unit(
535                            DateTimeField::Day,
536                            DateTimeField::Microseconds,
537                            m_f_us,
538                        )
539                    })
540                    .and_then(|m_f_us| {
541                        div_and_round(m_f_us, DateTimeFieldValue::FRACTIONAL_DIGIT_PRECISION)
542                    })
543                    .and_then(|m_f_us| micros.checked_add(m_f_us))
544                    .ok_or_else(|| {
545                        format!(
546                            "Overflows maximum microseconds; cannot exceed {}/{} microseconds",
547                            i64::MAX,
548                            i64::MIN
549                        )
550                    })?;
551
552                Ok(())
553            }
554            Day => {
555                let (t, t_f) = match self.units_of(d) {
556                    Some(t) => (t.unit, t.fraction),
557                    None => return Ok(()),
558                };
559
560                // days += t
561                *days = i32::try_from(t)
562                    .ok()
563                    .and_then(|t_day| days.checked_add(t_day))
564                    .ok_or_else(|| {
565                        format!(
566                            "Overflows maximum days; cannot exceed {}/{} days",
567                            i32::MAX,
568                            i32::MIN,
569                        )
570                    })?;
571
572                // micros += t_f.to_micros() / DateTimeFieldValue::FRACTION_MULTIPLIER
573                *micros = Interval::convert_date_time_unit(
574                    d,
575                    DateTimeField::Microseconds,
576                    i128::from(t_f),
577                )
578                .and_then(|t_f_us| {
579                    div_and_round(t_f_us, DateTimeFieldValue::FRACTIONAL_DIGIT_PRECISION)
580                })
581                .and_then(|t_f_us| micros.checked_add(t_f_us))
582                .ok_or_else(|| {
583                    format!(
584                        "Overflows maximum microseconds; cannot exceed {}/{} microseconds",
585                        i64::MAX,
586                        i64::MIN
587                    )
588                })?;
589
590                Ok(())
591            }
592            Hour | Minute | Second | Milliseconds | Microseconds => {
593                let (t, t_f) = match self.units_of(d) {
594                    Some(t) => (t.unit, t.fraction),
595                    None => return Ok(()),
596                };
597
598                // micros += t.to_micros()
599                *micros = Interval::convert_date_time_unit(d, DateTimeField::Microseconds, t)
600                    .and_then(|t_s| micros.checked_add(t_s))
601                    .ok_or_else(|| {
602                        format!(
603                            "Overflows maximum microseconds; cannot exceed {}/{} microseconds",
604                            i64::MAX,
605                            i64::MIN,
606                        )
607                    })?;
608
609                // micros += t_f.to_micros() / DateTimeFieldValue::FRACTION_MULTIPLIER
610                *micros = Interval::convert_date_time_unit(d, DateTimeField::Microseconds, t_f)
611                    .and_then(|t_f_ns| {
612                        div_and_round(
613                            t_f_ns.into(),
614                            DateTimeFieldValue::FRACTIONAL_DIGIT_PRECISION,
615                        )
616                    })
617                    .and_then(|t_f_ns| micros.checked_add(t_f_ns))
618                    .ok_or_else(|| {
619                        format!(
620                            "Overflows maximum microseconds; cannot exceed {}/{} microseconds",
621                            i64::MAX,
622                            i64::MIN,
623                        )
624                    })?;
625                Ok(())
626            }
627        }
628    }
629
630    /// Compute a chrono::NaiveDate from an ParsedDateTime.
631    ///
632    /// # Errors
633    /// - If year, month, or day overflows their respective parameter in
634    ///   [chrono::naive::date::NaiveDate::from_ymd_opt](https://docs.rs/chrono/0.4/chrono/naive/struct.NaiveDate.html#method.from_ymd_opt).
635    ///
636    /// Note: Postgres does not recognize Year 0, but in order to make
637    /// arithmetic work as expected, the Year 1 BC in a ParsedDateTime
638    /// is mapped to the Year 0 in a NaiveDate, and vice-versa.
639    pub fn compute_date(&self) -> Result<chrono::NaiveDate, String> {
640        match (self.year, self.month, self.day) {
641            (Some(year), Some(month), Some(day)) => {
642                // Adjust for BC years
643                let year = if year.unit < 0 {
644                    year.unit + 1
645                } else {
646                    year.unit
647                };
648                let p_err = |e, field| format!("{} in date is invalid: {}", field, e);
649                let year = year.try_into().map_err(|e| p_err(e, "Year"))?;
650                let month = month.unit.try_into().map_err(|e| p_err(e, "Month"))?;
651                let day = day.unit.try_into().map_err(|e| p_err(e, "Day"))?;
652                NaiveDate::from_ymd_opt(year, month, day)
653                    .ok_or_else(|| "invalid or out-of-range date".into())
654            }
655            (_, _, _) => Err("YEAR, MONTH, DAY are all required".into()),
656        }
657    }
658
659    /// Compute a chrono::NaiveDate from an ParsedDateTime.
660    ///
661    /// # Errors
662    /// - If hour, minute, or second (both `unit` and `fraction`) overflow `u32`.
663    pub fn compute_time(&self) -> Result<chrono::NaiveTime, String> {
664        let p_err = |e, field| format!("invalid {}: {}", field, e);
665        let hour = match self.hour {
666            Some(hour) => hour.unit.try_into().map_err(|e| p_err(e, "HOUR"))?,
667            None => 0,
668        };
669        let minute = match self.minute {
670            Some(minute) => minute.unit.try_into().map_err(|e| p_err(e, "MINUTE"))?,
671            None => 0,
672        };
673        let (second, nano) = match self.second {
674            Some(second) => {
675                let nano: u32 = second
676                    .fraction
677                    .try_into()
678                    .map_err(|e| p_err(e, "NANOSECOND"))?;
679                let second: u32 = second.unit.try_into().map_err(|e| p_err(e, "MINUTE"))?;
680                (second, nano)
681            }
682            None => (0, 0),
683        };
684        Ok(NaiveTime::from_hms_nano_opt(hour, minute, second, nano).unwrap())
685    }
686
687    /// Builds a ParsedDateTime from an interval string (`value`).
688    ///
689    /// # Arguments
690    ///
691    /// * `value` is a PostgreSQL-compatible interval string, e.g `INTERVAL 'value'`.
692    /// * `leading_time_precision` optionally identifies the leading time component
693    ///   HOUR | MINUTE to disambiguate {}:{} formatted intervals
694    /// * `ambiguous_resolver` identifies the DateTimeField of the final part
695    ///   if it's ambiguous, e.g. in `INTERVAL '1' MONTH` '1' is ambiguous as its
696    ///   DateTimeField, but MONTH resolves the ambiguity.
697    pub fn build_parsed_datetime_interval(
698        value: &str,
699        leading_time_precision: Option<DateTimeField>,
700        ambiguous_resolver: DateTimeField,
701    ) -> Result<ParsedDateTime, String> {
702        use DateTimeField::*;
703
704        let mut pdt = ParsedDateTime::default();
705        let mut value_parts = VecDeque::new();
706        let mut value_tokens = tokenize_time_str(value.trim())?;
707        let mut token_buffer = VecDeque::new();
708
709        while let Some(t) = value_tokens.pop_front() {
710            match t {
711                TimeStrToken::Delim => {
712                    if !token_buffer.is_empty() {
713                        value_parts.push_back(token_buffer.clone());
714                        token_buffer.clear();
715                    }
716                }
717                // Equivalent to trimming Colons from each leading element.
718                TimeStrToken::Colon if token_buffer.is_empty() => {}
719                _ => token_buffer.push_back(t),
720            }
721        }
722
723        if !token_buffer.is_empty() {
724            value_parts.push_back(token_buffer)
725        }
726
727        let mut annotated_parts = Vec::new();
728
729        while let Some(part) = value_parts.pop_front() {
730            let mut fmt = determine_format_w_datetimefield(part.clone(), leading_time_precision)?;
731            // If you cannot determine the format of this part, try to infer its
732            // format.
733            if fmt.is_none() {
734                fmt = match value_parts.pop_front() {
735                    Some(next_part) => {
736                        match determine_format_w_datetimefield(
737                            next_part.clone(),
738                            leading_time_precision,
739                        )? {
740                            Some(TimePartFormat::SqlStandard(f)) => {
741                                match f {
742                                    // Do not capture this token because expression
743                                    // is going to fail.
744                                    Year | Month | Day => None,
745                                    // If following part is H:M:S, infer that this
746                                    // part is Day. Because this part can use a
747                                    // fraction, it should be parsed as PostgreSQL.
748                                    _ => {
749                                        // We can capture these annotated tokens
750                                        // because expressions are commutative.
751                                        annotated_parts.push(AnnotatedIntervalPart {
752                                            fmt: TimePartFormat::SqlStandard(f),
753                                            tokens: next_part.clone(),
754                                        });
755                                        Some(TimePartFormat::PostgreSql(Day))
756                                    }
757                                }
758                            }
759                            // None | Some(TimePartFormat::PostgreSQL(f))
760                            // If next_fmt is TimePartFormat::PostgreSQL, that
761                            // indicates that the following string was a TimeUnit,
762                            // e.g. `day`, and this is where those tokens get
763                            // consumed and propagated to their preceding numerical
764                            // value.
765                            next_fmt => next_fmt,
766                        }
767                    }
768                    // Allow resolution of final part using ambiguous_resolver.
769                    None => Some(TimePartFormat::PostgreSql(ambiguous_resolver)),
770                }
771            }
772            match fmt {
773                Some(fmt) => annotated_parts.push(AnnotatedIntervalPart {
774                    fmt,
775                    tokens: part.clone(),
776                }),
777                None => {
778                    return Err("Cannot determine format of all parts. Add \
779                        explicit time components, e.g. INTERVAL '1 day' or INTERVAL '1' DAY"
780                        .into());
781                }
782            }
783        }
784
785        for mut ap in annotated_parts {
786            match ap.fmt {
787                TimePartFormat::SqlStandard(f) => {
788                    fill_pdt_interval_sql(&mut ap.tokens, f, &mut pdt)?;
789                    pdt.check_interval_bounds(f)?;
790                }
791                TimePartFormat::PostgreSql(f) => fill_pdt_interval_pg(&mut ap.tokens, f, &mut pdt)?,
792            }
793
794            // Consume unused TimeUnits for PG compat.
795            while let Some(TimeStrToken::TimeUnit(_)) = ap.tokens.front() {
796                ap.tokens.pop_front();
797            }
798
799            if !ap.tokens.is_empty() {
800                return Err(format!(
801                    "have unprocessed tokens {}",
802                    itertools::join(ap.tokens, "").trim_end(),
803                ));
804            }
805        }
806
807        Ok(pdt)
808    }
809    /// Builds a ParsedDateTime from a TIMESTAMP string (`value`).
810    ///
811    /// # Arguments
812    ///
813    /// * `value` is a SQL-formatted TIMESTAMP string.
814    pub fn build_parsed_datetime_timestamp(
815        value: &str,
816        era: CalendarEra,
817        order: DateOrder,
818    ) -> Result<ParsedDateTime, String> {
819        let mut pdt = ParsedDateTime::default();
820
821        let mut ts_actual = tokenize_time_str(value)?;
822
823        fill_pdt_date(&mut pdt, &mut ts_actual, order, era)?;
824
825        if let CalendarEra::BC = era {
826            pdt.year = pdt.year.map(|mut y| {
827                y.unit = -y.unit;
828                y
829            });
830        }
831
832        if let Some(TimeStrToken::DateTimeDelimiter) = ts_actual.front() {
833            ts_actual.pop_front();
834            ltrim_delim_or_colon(&mut ts_actual);
835        }
836
837        fill_pdt_time(&mut pdt, &mut ts_actual)?;
838        pdt.check_datelike_bounds()?;
839
840        if ts_actual.is_empty() {
841            Ok(pdt)
842        } else {
843            Err(format!(
844                "have unprocessed tokens {}",
845                itertools::join(ts_actual, "").trim_end(),
846            ))
847        }
848    }
849    /// Builds a ParsedDateTime from a TIME string (`value`).
850    ///
851    /// # Arguments
852    ///
853    /// * `value` is a SQL-formatted TIME string.
854    pub fn build_parsed_datetime_time(value: &str) -> Result<ParsedDateTime, String> {
855        let mut pdt = ParsedDateTime::default();
856
857        let mut time_actual = tokenize_time_str(value)?;
858        fill_pdt_time(&mut pdt, &mut time_actual)?;
859        pdt.check_datelike_bounds()?;
860
861        if time_actual.is_empty() {
862            Ok(pdt)
863        } else {
864            Err(format!(
865                "have unprocessed tokens {}",
866                itertools::join(time_actual, "").trim_end(),
867            ))
868        }
869    }
870
871    /// Write to the specified field of a ParsedDateTime iff it is currently set
872    /// to None; otherwise generate an error to propagate to the user.
873    pub fn write_field_iff_none(
874        &mut self,
875        f: DateTimeField,
876        u: Option<DateTimeFieldValue>,
877    ) -> Result<(), String> {
878        use DateTimeField::*;
879
880        if let Some(unwrapped_u) = &u {
881            match f {
882                Millennium if self.millennium.is_none() => {
883                    self.millennium = u;
884                }
885                Century if self.century.is_none() => {
886                    self.century = u;
887                }
888                Decade if self.decade.is_none() => {
889                    self.decade = u;
890                }
891                Year if self.year.is_none() => {
892                    self.year = u;
893                }
894                Month if self.month.is_none() => {
895                    self.month = u;
896                }
897                Day if self.day.is_none() => {
898                    self.day = u;
899                }
900                Hour if self.hour.is_none() => {
901                    self.hour = u;
902                }
903                Minute if self.minute.is_none() => {
904                    self.minute = u;
905                }
906                Second if self.second.is_none() => {
907                    if unwrapped_u.fraction != 0
908                        && (self.millisecond.is_some() || self.microsecond.is_some())
909                    {
910                        return Err(format!(
911                            "Cannot set {} or {} field if {} field has a fraction component",
912                            Milliseconds, Microseconds, f
913                        ));
914                    }
915                    self.second = u;
916                }
917                Milliseconds if self.millisecond.is_none() => {
918                    if self.seconds_has_fraction() {
919                        return Err(format!(
920                            "Cannot set {} or {} field if {} field has a fraction component",
921                            f, Microseconds, Second
922                        ));
923                    }
924                    self.millisecond = u;
925                }
926                Microseconds if self.microsecond.is_none() => {
927                    if self.seconds_has_fraction() {
928                        return Err(format!(
929                            "Cannot set {} or {} field if {} field has a fraction component",
930                            Milliseconds, f, Second
931                        ));
932                    }
933                    self.microsecond = u;
934                }
935                _ => return Err(format!("{} field set twice", f)),
936            }
937        }
938        Ok(())
939    }
940
941    fn seconds_has_fraction(&self) -> bool {
942        self.second.is_some() && self.second.as_ref().unwrap().fraction != 0
943    }
944
945    pub fn check_datelike_bounds(&mut self) -> Result<(), String> {
946        if let Some(year) = self.year {
947            // 1BC is not represented as year 0 at the parser level, only internally
948            if year.unit == 0 {
949                return Err("YEAR cannot be zero".to_string());
950            }
951        }
952        if let Some(month) = self.month {
953            if month.unit < 1 || month.unit > 12 {
954                return Err(format!("MONTH must be [1, 12], got {}", month.unit));
955            };
956        }
957        if let Some(day) = self.day {
958            if day.unit < 1 || day.unit > 31 {
959                return Err(format!("DAY must be [1, 31], got {}", day.unit));
960            };
961        }
962        if let Some(hour) = self.hour {
963            if hour.unit < 0 || hour.unit > 23 {
964                return Err(format!("HOUR must be [0, 23], got {}", hour.unit));
965            };
966        }
967        if let Some(minute) = self.minute {
968            if minute.unit < 0 || minute.unit > 59 {
969                return Err(format!("MINUTE must be [0, 59], got {}", minute.unit));
970            };
971        }
972
973        if let Some(second) = &mut self.second {
974            // Chrono supports leap seconds by moving them into nanos:
975            // https://docs.rs/chrono/0.4.19/chrono/naive/struct.NaiveTime.html#leap-second-handling
976            // See also Tom Lane saying that leap seconds are not well handled:
977            // https://www.postgresql.org/message-id/23016.1327976502@sss.pgh.pa.us
978            if second.unit == 60 {
979                second.unit = 59;
980                second.fraction = second.fraction.saturating_add(1_000_000_000);
981            }
982            if second.unit < 0 || second.unit > 60 {
983                return Err(format!("SECOND must be [0, 60], got {}", second.unit));
984            };
985            if second.fraction < 0 || second.fraction > 1_000_000_000 {
986                return Err(format!(
987                    "NANOSECOND must be [0, 1_000_000_000], got {}",
988                    second.fraction
989                ));
990            };
991        }
992
993        Ok(())
994    }
995    pub fn check_interval_bounds(&self, d: DateTimeField) -> Result<(), String> {
996        use DateTimeField::*;
997
998        match d {
999            Millennium | Century | Decade | Year | Month => {
1000                if let Some(month) = self.month {
1001                    if month.unit < -12 || month.unit > 12 {
1002                        return Err(format!("MONTH must be [-12, 12], got {}", month.unit));
1003                    };
1004                }
1005            }
1006            Hour | Minute | Second | Milliseconds | Microseconds => {
1007                if let Some(minute) = self.minute {
1008                    if minute.unit < -59 || minute.unit > 59 {
1009                        return Err(format!("MINUTE must be [-59, 59], got {}", minute.unit));
1010                    };
1011                }
1012
1013                let mut seconds = 0;
1014                let mut nanoseconds = 0;
1015
1016                if let Some(second) = self.second {
1017                    seconds += second.unit;
1018                    nanoseconds += second.fraction;
1019                }
1020
1021                if let Some(millisecond) = self.millisecond {
1022                    seconds += millisecond.unit / 1_000;
1023                    nanoseconds += (millisecond.unit % 1_000) * 1_000_000;
1024                    nanoseconds += (millisecond.fraction / 1_000) % 1_000_000_000;
1025                }
1026
1027                if let Some(microsecond) = self.microsecond {
1028                    seconds += microsecond.unit / 1_000_000;
1029                    nanoseconds += (microsecond.unit % 1_000_000) * 1_000;
1030                    nanoseconds += (microsecond.fraction / 1_000_000) % 1_000_000_000;
1031                }
1032
1033                if seconds < -60 || seconds > 60 {
1034                    return Err(format!("SECOND must be [-60, 60], got {}", seconds));
1035                };
1036                if nanoseconds < -1_000_000_000 || nanoseconds > 1_000_000_000 {
1037                    return Err(format!(
1038                        "NANOSECOND must be [-1_000_000_000, 1_000_000_000], got {}",
1039                        nanoseconds
1040                    ));
1041                };
1042            }
1043            Day => {}
1044        }
1045
1046        Ok(())
1047    }
1048
1049    pub fn clear_date(&mut self) {
1050        self.year = None;
1051        self.month = None;
1052        self.day = None;
1053    }
1054
1055    /// Retrieve any value that we parsed out of the literal string for the
1056    /// `field`.
1057    fn units_of(&self, field: DateTimeField) -> Option<DateTimeFieldValue> {
1058        match field {
1059            DateTimeField::Millennium => self.millennium,
1060            DateTimeField::Century => self.century,
1061            DateTimeField::Decade => self.decade,
1062            DateTimeField::Year => self.year,
1063            DateTimeField::Month => self.month,
1064            DateTimeField::Day => self.day,
1065            DateTimeField::Hour => self.hour,
1066            DateTimeField::Minute => self.minute,
1067            DateTimeField::Second => self.second,
1068            DateTimeField::Milliseconds => self.millisecond,
1069            DateTimeField::Microseconds => self.microsecond,
1070        }
1071    }
1072}
1073
1074/// Fills the year, month, and day fields of `pdt` using the `TimeStrToken`s in
1075/// `actual`.
1076///
1077/// # Args
1078///
1079/// - `pdt`: The ParsedDateTime to fill.
1080/// - `actual`: The queue of tokens representing the string you want use to fill
1081///   `pdt`'s fields.
1082fn fill_pdt_date(
1083    pdt: &mut ParsedDateTime,
1084    actual: &mut VecDeque<TimeStrToken>,
1085    order: DateOrder,
1086    era: CalendarEra,
1087) -> Result<(), String> {
1088    use TimeStrToken::*;
1089
1090    // Check for one number that represents YYYYMMDDD. A following `Dash` means
1091    // the date uses explicit `Y-M-D` separators, so the leading number is a
1092    // full year (which can be 6 digits, e.g. `118852-02-05`), not a compact
1093    // date. Only treat it as compact when it is a standalone number.
1094    match actual.front() {
1095        Some(&Num(mut val, ref digits))
1096            if 6 <= *digits && *digits <= 8 && !matches!(actual.get(1), Some(Dash)) =>
1097        {
1098            let unit = i64::try_from(val % 100)
1099                .expect("modulo between u64 and constant 100 should fit signed 64-bit integer");
1100            pdt.day = Some(DateTimeFieldValue::new(unit, 0));
1101            val /= 100;
1102
1103            let unit = i64::try_from(val % 100)
1104                .expect("modulo between u64 and constant 100 should fit signed 64-bit integer");
1105            pdt.month = Some(DateTimeFieldValue::new(unit, 0));
1106            val /= 100;
1107            // Handle 2 digit year case
1108            if *digits == 6 {
1109                if val < 70 {
1110                    val += 2000;
1111                } else {
1112                    val += 1900;
1113                }
1114            }
1115
1116            let unit = i64::try_from(val)
1117                .map_err(|_| "number should fit in signed 64-bit integer".to_string())?;
1118            pdt.year = Some(DateTimeFieldValue::new(unit, 0));
1119            actual.pop_front();
1120            // Trim remaining optional tokens, but never an immediately
1121            // following colon
1122            if let Some(Delim) = actual.front() {
1123                actual.pop_front();
1124                ltrim_delim_or_colon(actual);
1125            }
1126            return Ok(());
1127        }
1128        _ => (),
1129    }
1130
1131    // PostgreSQL under the pinned `DateStyle = ISO, MDY` reads a separated
1132    // date whose leading field has one or two digits as month-day-year, and
1133    // only a leading field of three or more digits as year-month-day. The
1134    // formats below fill fields positionally as year-month-day, so record the
1135    // digit counts up front and reorder the fields after a successful fill.
1136    let mdy = match (order, actual.front()) {
1137        (DateOrder::Mdy, Some(&Num(_, digits))) => digits <= 2,
1138        _ => false,
1139    };
1140    // Digit count of the third numeric field, the year in a month-day-year
1141    // date, which decides two-digit-year windowing.
1142    let year_digits = actual
1143        .iter()
1144        .take_while(|t| matches!(t, Num(..) | Dash | Delim))
1145        .filter_map(|t| match t {
1146            Num(_, digits) => Some(*digits),
1147            _ => None,
1148        })
1149        .nth(2);
1150
1151    let valid_formats = [
1152        [
1153            Num(0, 1), // year
1154            Dash,
1155            Num(0, 1), // month
1156            Dash,
1157            Num(0, 1), // day
1158        ],
1159        [
1160            Num(0, 1), // year
1161            Delim,
1162            Num(0, 1), // month
1163            Dash,
1164            Num(0, 1), // day
1165        ],
1166        [
1167            Num(0, 1), // year
1168            Delim,
1169            Num(0, 1), // month
1170            Delim,
1171            Num(0, 1), // day
1172        ],
1173    ];
1174
1175    let original_actual = actual.clone();
1176
1177    for expected in valid_formats {
1178        match fill_pdt_from_tokens(pdt, actual, &expected, DateTimeField::Year, 1) {
1179            Ok(()) => {
1180                // In a month-day-year date the positional fill above put the
1181                // month in `year`, the day in `month`, and the year in `day`.
1182                if mdy {
1183                    if let (Some(month), Some(day), Some(mut year)) = (pdt.year, pdt.month, pdt.day)
1184                    {
1185                        // Window two-digit years into 1970..=2069, as
1186                        // PostgreSQL does. Explicit BC years are never
1187                        // windowed.
1188                        if era == CalendarEra::AD && year_digits.is_some_and(|d| d <= 2) {
1189                            year.unit += if year.unit < 70 { 2000 } else { 1900 };
1190                        }
1191                        pdt.year = Some(year);
1192                        pdt.month = Some(month);
1193                        pdt.day = Some(day);
1194                    }
1195                }
1196                return Ok(());
1197            }
1198            Err(_) => {
1199                actual.clone_from(&original_actual);
1200                pdt.clear_date();
1201            }
1202        }
1203    }
1204
1205    Err("does not match any format for date component".into())
1206}
1207
1208/// Fills the hour, minute, and second fields of `pdt` using the `TimeStrToken`s in
1209/// `actual`.
1210///
1211/// # Args
1212///
1213/// - `pdt`: The ParsedDateTime to fill.
1214/// - `actual`: The queue of tokens representing the string you want use to fill
1215///   `pdt`'s fields.
1216fn fill_pdt_time(
1217    pdt: &mut ParsedDateTime,
1218    actual: &mut VecDeque<TimeStrToken>,
1219) -> Result<(), String> {
1220    match determine_format_w_datetimefield(actual.clone(), None)? {
1221        Some(TimePartFormat::SqlStandard(leading_field)) => {
1222            let expected = expected_dur_like_tokens(leading_field)?;
1223
1224            fill_pdt_from_tokens(pdt, actual, expected, leading_field, 1)
1225        }
1226        _ => Ok(()),
1227    }
1228}
1229
1230/// Fills a ParsedDateTime's fields when encountering SQL standard-style interval
1231/// parts, e.g. `1-2` for Y-M `4:5:6.7` for H:M:S.NS.
1232///
1233/// Note that:
1234/// - SQL-standard style groups ({Y-M}{D}{H:M:S.NS}) require that no fields in
1235///   the group have been modified, and do not allow any fields to be modified
1236///   afterward.
1237/// - Single digits, e.g. `3` in `3 4:5:6.7` could be parsed as SQL standard
1238///   tokens, but end up being parsed as PostgreSQL-style tokens because of their
1239///   greater expressivity, in that they allow fractions, and otherwise-equivalence.
1240fn fill_pdt_interval_sql(
1241    actual: &mut VecDeque<TimeStrToken>,
1242    leading_field: DateTimeField,
1243    pdt: &mut ParsedDateTime,
1244) -> Result<(), String> {
1245    use DateTimeField::*;
1246
1247    // Ensure that no fields have been previously modified.
1248    match leading_field {
1249        Year | Month => {
1250            if pdt.year.is_some() || pdt.month.is_some() {
1251                return Err("YEAR or MONTH field set twice".into());
1252            }
1253        }
1254        Day => {
1255            if pdt.day.is_some() {
1256                return Err("DAY field set twice".into());
1257            }
1258        }
1259        Hour | Minute | Second => {
1260            if pdt.hour.is_some() || pdt.minute.is_some() || pdt.second.is_some() {
1261                return Err("HOUR, MINUTE, SECOND field set twice".into());
1262            }
1263        }
1264        Millennium | Century | Decade | Milliseconds | Microseconds => {
1265            return Err(format!(
1266                "Cannot specify {} field for SQL standard-style interval parts",
1267                leading_field
1268            ));
1269        }
1270    }
1271
1272    let expected = expected_sql_standard_interval_tokens(leading_field);
1273
1274    let sign = trim_and_return_sign(actual);
1275
1276    fill_pdt_from_tokens(pdt, actual, expected, leading_field, sign)?;
1277
1278    // Write default values to any unwritten member of the `leading_field`'s group.
1279    // This will ensure that those fields cannot be written to at a later time, which
1280    // is part of the SQL standard behavior for timelike components.
1281    match leading_field {
1282        Year | Month => {
1283            if pdt.year.is_none() {
1284                pdt.year = Some(DateTimeFieldValue::default());
1285            }
1286            if pdt.month.is_none() {
1287                pdt.month = Some(DateTimeFieldValue::default());
1288            }
1289        }
1290        Day => {
1291            if pdt.day.is_none() {
1292                pdt.day = Some(DateTimeFieldValue::default());
1293            }
1294        }
1295        Hour | Minute | Second => {
1296            if pdt.hour.is_none() {
1297                pdt.hour = Some(DateTimeFieldValue::default());
1298            }
1299            if pdt.minute.is_none() {
1300                pdt.minute = Some(DateTimeFieldValue::default());
1301            }
1302            if pdt.second.is_none() {
1303                pdt.second = Some(DateTimeFieldValue::default());
1304            }
1305        }
1306        Millennium | Century | Decade | Milliseconds | Microseconds => {
1307            return Err(format!(
1308                "Cannot specify {} field for SQL standard-style interval parts",
1309                leading_field
1310            ));
1311        }
1312    }
1313
1314    Ok(())
1315}
1316
1317/// Fills a ParsedDateTime's fields for a single PostgreSQL-style interval
1318/// parts, e.g. `1 month`. Invoke this function once for each PostgreSQL-style
1319/// interval part.
1320///
1321/// Note that:
1322/// - This function only meaningfully parses the numerical component of the
1323///   string, and relies on determining the DateTimeField component from
1324///   AnnotatedIntervalPart, passed in as `time_unit`.
1325/// - Only PostgreSQL-style parts can use fractional components in positions
1326///   other than seconds, e.g. `1.5 months`.
1327fn fill_pdt_interval_pg(
1328    actual: &mut VecDeque<TimeStrToken>,
1329    time_unit: DateTimeField,
1330    pdt: &mut ParsedDateTime,
1331) -> Result<(), String> {
1332    use TimeStrToken::*;
1333
1334    // We remove all spaces during tokenization, so TimeUnit only shows up if
1335    // there is no space between the number and the TimeUnit, e.g. `1y 2d 3h`, which
1336    // PostgreSQL allows.
1337    let expected = [Num(0, 1), Dot, Nanos(0), TimeUnit(DateTimeField::Year)];
1338
1339    let sign = trim_and_return_sign(actual);
1340
1341    fill_pdt_from_tokens(pdt, actual, &expected, time_unit, sign)?;
1342
1343    Ok(())
1344}
1345
1346/// Fills a ParsedDateTime's fields using the `actual` tokens, starting at `leading_field`
1347/// and descending to less significant DateTimeFields.
1348///
1349/// # Errors
1350/// - If `actual` doesn't match `expected`, modulo skippable TimeStrTokens.
1351/// - Setting a field in ParsedDateTime that has already been set.
1352///
1353/// # Panics
1354/// - Trying to advance to the next smallest DateTimeField if you're currently
1355///     at DateTimeField::Second.
1356fn fill_pdt_from_tokens<'a, E: IntoIterator<Item = &'a TimeStrToken>>(
1357    pdt: &mut ParsedDateTime,
1358    actual: &mut VecDeque<TimeStrToken>,
1359    expected: E,
1360    leading_field: DateTimeField,
1361    sign: i64,
1362) -> Result<(), String> {
1363    use TimeStrToken::*;
1364    let mut current_field = leading_field;
1365
1366    let mut i = 0u8;
1367
1368    let mut unit_buf: Option<DateTimeFieldValue> = None;
1369
1370    let mut expected = expected.into_iter().peekable();
1371
1372    while let (Some(atok), Some(etok)) = (actual.front(), expected.peek()) {
1373        match (atok, etok) {
1374            // The following forms of punctuation signal the end of a field and can
1375            // trigger a write.
1376            (Dash, Dash) | (Colon, Colon) => {
1377                pdt.write_field_iff_none(current_field, unit_buf)?;
1378                unit_buf = None;
1379                current_field = current_field.next_smallest();
1380            }
1381            (Delim, Delim) => {
1382                pdt.write_field_iff_none(current_field, unit_buf)?;
1383                unit_buf = None;
1384                current_field = current_field.next_smallest();
1385
1386                // Spaces require special processing to allow users to enter an
1387                // arbitrary number of delimiters wherever they're allowed. Note
1388                // that this does not include colons.
1389                actual.pop_front();
1390                expected.next();
1391                i += 1;
1392
1393                while let Some(Delim) = actual.front() {
1394                    actual.pop_front();
1395                }
1396
1397                continue;
1398            }
1399            (TimeUnit(f), TimeUnit(_)) => {
1400                if unit_buf.is_some() && *f != current_field {
1401                    return Err(format!(
1402                        "Invalid syntax at offset {}: provided TimeUnit({}) but expected TimeUnit({})",
1403                        i, f, current_field
1404                    ));
1405                }
1406            }
1407            // If we got a DateTimeUnits, attempt to convert it to a TimeUnit.
1408            (DateTimeUnit(u), TimeUnit(_)) => {
1409                let f = match u {
1410                    DateTimeUnits::Hour => DateTimeField::Hour,
1411                    DateTimeUnits::Minute => DateTimeField::Minute,
1412                    DateTimeUnits::Second => DateTimeField::Second,
1413                    DateTimeUnits::Milliseconds => DateTimeField::Milliseconds,
1414                    DateTimeUnits::Microseconds => DateTimeField::Microseconds,
1415                    _ => return Err(format!("unsupported unit {}", u)),
1416                };
1417                if unit_buf.is_some() && f != current_field {
1418                    return Err(format!(
1419                        "Invalid syntax at offset {}: provided DateTimeUnit({}) but expected TimeUnit({})",
1420                        u, f, current_field
1421                    ));
1422                }
1423            }
1424            (Dot, Dot) => {}
1425            (Num(val, _), Num(_, _)) => match unit_buf {
1426                Some(_) => {
1427                    return Err(
1428                        "Invalid syntax; parts must be separated by '-', ':', or ' '".to_string(),
1429                    );
1430                }
1431                None => {
1432                    // create signed copy of *val
1433                    let unit = i64::try_from(i128::from(*val) * i128::from(sign))
1434                        .map_err(|_| format!("Unable to parse value {val} as a number: number too large to fit in target type"))?;
1435                    unit_buf = Some(DateTimeFieldValue { unit, fraction: 0 });
1436                }
1437            },
1438            (Nanos(val), Nanos(_)) => match unit_buf {
1439                Some(ref mut u) => {
1440                    u.fraction = *val * sign;
1441                }
1442                None => {
1443                    unit_buf = Some(DateTimeFieldValue {
1444                        unit: 0,
1445                        fraction: *val * sign,
1446                    });
1447                }
1448            },
1449            (Num(n, _), Nanos(_)) => {
1450                // Create disposable copy of n.
1451                let mut nc = *n;
1452
1453                let mut width = 0;
1454                // Destructively count the number of digits in n.
1455                while nc != 0 {
1456                    nc /= 10;
1457                    width += 1;
1458                }
1459
1460                let mut n = *n;
1461
1462                // Nanoseconds have 9 digits of precision.
1463                let precision = 9;
1464
1465                if width > precision {
1466                    // Trim n to its 9 most significant digits.
1467                    n /= 10_u64.pow(width - precision);
1468                } else {
1469                    // Right-pad n with 0s.
1470                    n *= 10_u64.pow(precision - width);
1471                }
1472
1473                // create signed copy of n
1474                let sn  = i64::try_from(i128::from(n) * i128::from(sign))
1475                    .map_err(|_| format!("Unable to parse value {n} as a number: number too large to fit in target type"))?;
1476
1477                match unit_buf {
1478                    Some(ref mut u) => {
1479                        u.fraction = sn;
1480                    }
1481                    None => {
1482                        unit_buf = Some(DateTimeFieldValue {
1483                            unit: 0,
1484                            fraction: sn,
1485                        });
1486                    }
1487                }
1488            }
1489            // Allow skipping expected spaces (Delim), numbers, dots, and nanoseconds.
1490            (_, Num(_, _)) | (_, Dot) | (_, Nanos(_)) | (_, Delim) => {
1491                expected.next();
1492                continue;
1493            }
1494            (provided, expected) => {
1495                return Err(format!(
1496                    "Invalid syntax at offset {i}: provided {provided:?} but expected {expected:?}",
1497                ));
1498            }
1499        }
1500        i += 1;
1501        actual.pop_front();
1502        expected.next();
1503    }
1504
1505    ltrim_delim_or_colon(actual);
1506
1507    pdt.write_field_iff_none(current_field, unit_buf)?;
1508
1509    Ok(())
1510}
1511
1512/// Interval strings can be presented in one of two formats:
1513/// - SQL Standard, e.g. `1-2 3 4:5:6.7`
1514/// - PostgreSQL, e.g. `1 year 2 months 3 days`
1515/// TimePartFormat indicates which type of parsing to use and encodes a
1516/// DateTimeField, which indicates "where" you should begin parsing the
1517/// associated tokens w/r/t their respective syntax.
1518#[derive(Debug, Eq, PartialEq, Clone)]
1519enum TimePartFormat {
1520    SqlStandard(DateTimeField),
1521    PostgreSql(DateTimeField),
1522}
1523
1524/// AnnotatedIntervalPart contains the tokens to be parsed, as well as the format
1525/// to parse them.
1526#[derive(Debug, Eq, PartialEq, Clone)]
1527struct AnnotatedIntervalPart {
1528    pub tokens: VecDeque<TimeStrToken>,
1529    pub fmt: TimePartFormat,
1530}
1531
1532/// Determines the format of the interval part (uses None to identify an
1533/// indeterminant/ambiguous format). This is necessary because the interval
1534/// string format is not LL(1); we instead parse as few tokens as possible to
1535/// generate the string's semantics.
1536///
1537/// Note that `toks` should _not_ contain space
1538fn determine_format_w_datetimefield(
1539    mut toks: VecDeque<TimeStrToken>,
1540    leading_time_precision: Option<DateTimeField>,
1541) -> Result<Option<TimePartFormat>, String> {
1542    use DateTimeField::*;
1543    use TimePartFormat::*;
1544    use TimeStrToken::*;
1545
1546    trim_and_return_sign(&mut toks);
1547
1548    if let Some(Num(_, _)) = toks.front() {
1549        toks.pop_front();
1550    }
1551
1552    match toks.pop_front() {
1553        // Implies {?}{?}{?}, ambiguous case.
1554        None | Some(Delim) => Ok(None),
1555        Some(Dot) => {
1556            match toks.front() {
1557                Some(Num(_, _)) | Some(Nanos(_)) => {
1558                    toks.pop_front();
1559                }
1560                _ => {}
1561            }
1562            match toks.pop_front() {
1563                // Implies {Num.NumTimeUnit}
1564                Some(TimeUnit(f)) => Ok(Some(PostgreSql(f))),
1565                // Implies {?}{?}{?}, ambiguous case.
1566                _ => Ok(None),
1567            }
1568        }
1569        // Implies {Y-...}{}{}
1570        Some(Dash) => Ok(Some(SqlStandard(Year))),
1571        // Implies {}{}{?:...}
1572        Some(Colon) => {
1573            if let Some(Num(_, _)) = toks.front() {
1574                toks.pop_front();
1575            }
1576
1577            match toks.pop_front() {
1578                // Implies {H:M:?...}
1579                Some(Colon) | Some(Delim) => Ok(Some(SqlStandard(Hour))),
1580                // Implies {M:S.NS}
1581                Some(Dot) => Ok(Some(SqlStandard(Minute))),
1582                // Implies {a:b}. We default to {H:M}, and the leading
1583                // precision can be specified explicitly
1584                None => Ok(leading_time_precision
1585                    .map(SqlStandard)
1586                    .or(Some(SqlStandard(Hour)))),
1587                _ => Err("Cannot determine format of all parts".into()),
1588            }
1589        }
1590        // Implies {Num}?{TimeUnit}
1591        Some(TimeUnit(f)) => Ok(Some(PostgreSql(f))),
1592        Some(DateTimeUnit(DateTimeUnits::Hour)) => Ok(Some(PostgreSql(Hour))),
1593        Some(DateTimeUnit(DateTimeUnits::Minute)) => Ok(Some(PostgreSql(Minute))),
1594        Some(DateTimeUnit(DateTimeUnits::Second)) => Ok(Some(PostgreSql(Second))),
1595        Some(DateTimeUnit(DateTimeUnits::Milliseconds)) => Ok(Some(PostgreSql(Milliseconds))),
1596        Some(DateTimeUnit(DateTimeUnits::Microseconds)) => Ok(Some(PostgreSql(Microseconds))),
1597        Some(DateTimeUnit(_)) => Ok(None),
1598        _ => Err("Cannot determine format of all parts".into()),
1599    }
1600}
1601
1602/// Get the expected TimeStrTokens to parse SQL Standard time-like
1603/// DateTimeFields, i.e. HOUR, MINUTE, SECOND. This is used for INTERVAL,
1604/// TIMESTAMP, and TIME.
1605///
1606/// # Errors
1607/// - If `from` is YEAR, MONTH, or DAY.
1608fn expected_dur_like_tokens(from: DateTimeField) -> Result<&'static [TimeStrToken], String> {
1609    use DateTimeField::*;
1610    use TimeStrToken::*;
1611
1612    const ALL_TOKS: [TimeStrToken; 7] = [
1613        Num(0, 1), // hour
1614        Colon,
1615        Num(0, 1), // minute
1616        Colon,
1617        Num(0, 1), // second
1618        Dot,
1619        Nanos(0), // Nanos
1620    ];
1621    let start = match from {
1622        Hour => 0,
1623        Minute => 2,
1624        Second => 4,
1625        _ => {
1626            return Err(format!(
1627                "expected_dur_like_tokens can only be called with HOUR, MINUTE, SECOND; got {}",
1628                from
1629            ));
1630        }
1631    };
1632
1633    Ok(&ALL_TOKS[start..ALL_TOKS.len()])
1634}
1635
1636/// Get the expected TimeStrTokens to parse TimePartFormat::SqlStandard parts,
1637/// starting from some `DateTimeField`. Delim tokens are never actually included
1638/// in the output, but are illustrative of what the expected input of SQL
1639/// Standard interval values looks like.
1640fn expected_sql_standard_interval_tokens(from: DateTimeField) -> &'static [TimeStrToken] {
1641    use DateTimeField::*;
1642    use TimeStrToken::*;
1643
1644    const ALL_TOKS: [TimeStrToken; 6] = [
1645        Num(0, 1), // year
1646        Dash,
1647        Num(0, 1), // month
1648        Delim,
1649        Num(0, 1), // day
1650        Delim,
1651    ];
1652
1653    let (start, end) = match from {
1654        Year => (0, 4),
1655        Month => (2, 4),
1656        Day => (4, 6),
1657        _ => {
1658            return expected_dur_like_tokens(from)
1659                .expect("input to expected_dur_like_tokens shown to be valid");
1660        }
1661    };
1662
1663    &ALL_TOKS[start..end]
1664}
1665
1666fn trim_and_return_sign(z: &mut VecDeque<TimeStrToken>) -> i64 {
1667    use TimeStrToken::*;
1668
1669    match z.front() {
1670        Some(Dash) => {
1671            z.pop_front();
1672            -1
1673        }
1674        Some(Plus) => {
1675            z.pop_front();
1676            1
1677        }
1678        _ => 1,
1679    }
1680}
1681
1682/// PostgreSQL treats out-of-place colons as errant punctuation marks, and
1683/// trims them.
1684fn ltrim_delim_or_colon(z: &mut VecDeque<TimeStrToken>) {
1685    while Some(&TimeStrToken::Colon) == z.front() || Some(&TimeStrToken::Delim) == z.front() {
1686        z.pop_front();
1687    }
1688}
1689
1690/// TimeStrToken represents valid tokens in time-like strings,
1691/// i.e those used in INTERVAL, TIMESTAMP/TZ, DATE, and TIME.
1692#[derive(Debug, Clone, PartialEq, Eq)]
1693pub(crate) enum TimeStrToken {
1694    Dash,
1695    Colon,
1696    Dot,
1697    Plus,
1698    // Holds the parsed number and the number of digits in the original string
1699    Num(u64, usize),
1700    Nanos(i64),
1701    // Tokenized version of a DateTimeField string e.g. 'YEAR'
1702    TimeUnit(DateTimeField),
1703    // Fallback if TimeUnit isn't parseable.
1704    DateTimeUnit(DateTimeUnits),
1705    // Used to support ISO-formatted timestamps.
1706    DateTimeDelimiter,
1707    // Space arbitrary non-enum punctuation (e.g. !), or leading/trailing
1708    // colons.
1709    Delim,
1710}
1711
1712impl std::fmt::Display for TimeStrToken {
1713    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1714        use TimeStrToken::*;
1715        match self {
1716            Dash => write!(f, "-"),
1717            Colon => write!(f, ":"),
1718            Dot => write!(f, "."),
1719            Plus => write!(f, "+"),
1720            Num(i, digits) => write!(f, "{:01$}", i, digits - 1),
1721            Nanos(i) => write!(f, "{}", i),
1722            TimeUnit(d) => write!(f, "{:?}", d),
1723            DateTimeUnit(u) => write!(f, "{}", u),
1724            DateTimeDelimiter => write!(f, "T"),
1725            Delim => write!(f, " "),
1726        }
1727    }
1728}
1729
1730/// Convert a string from a time-like datatype (INTERVAL, TIMESTAMP/TZ, DATE, and TIME)
1731/// into `Vec<TimeStrToken>`.
1732///
1733/// # Warning
1734/// - Any sequence of numeric characters following a decimal that exceeds 9 characters
1735///   gets truncated to 9 characters, e.g. `0.1234567899` is truncated to `0.123456789`.
1736///
1737/// # Errors
1738/// - Any sequence of alphabetic characters cannot be cast into a DateTimeField.
1739/// - Any sequence of numeric characters cannot be cast into an i64.
1740/// - Any non-alpha numeric character cannot be cast into a TimeStrToken, e.g. `%`.
1741pub(crate) fn tokenize_time_str(value: &str) -> Result<VecDeque<TimeStrToken>, String> {
1742    let mut toks = VecDeque::new();
1743    let mut num_buf = String::with_capacity(4);
1744    let mut char_buf = String::with_capacity(7);
1745    fn parse_num(n: &str, idx: usize) -> Result<TimeStrToken, String> {
1746        Ok(TimeStrToken::Num(
1747            n.parse().map_err(|e| {
1748                format!("Unable to parse value as a number at index {}: {}", idx, e)
1749            })?,
1750            n.len(),
1751        ))
1752    }
1753    fn maybe_tokenize_num_buf(
1754        n: &mut String,
1755        i: usize,
1756        is_frac: &mut bool,
1757        t: &mut VecDeque<TimeStrToken>,
1758    ) -> Result<(), String> {
1759        if !n.is_empty() {
1760            if *is_frac {
1761                // Fractions only support 9 places of precision.
1762                n.truncate(9);
1763                let len = u32::try_from(n.len()).expect("length known to fit in a u32");
1764                let raw: i64 = n
1765                    .parse()
1766                    .map_err(|e| format!("couldn't parse fraction {}: {}", n, e))?;
1767                let multiplicand = 1_000_000_000 / 10_i64.pow(len);
1768                t.push_back(TimeStrToken::Nanos(raw * multiplicand));
1769                n.clear();
1770            } else {
1771                t.push_back(parse_num(n, i)?);
1772                n.clear();
1773            }
1774        }
1775        *is_frac = false;
1776        Ok(())
1777    }
1778    fn maybe_tokenize_char_buf(
1779        c: &mut String,
1780        t: &mut VecDeque<TimeStrToken>,
1781    ) -> Result<(), String> {
1782        if !c.is_empty() {
1783            // Supports ISO-formatted datetime strings.
1784            if c == "T" || c == "t" {
1785                t.push_back(TimeStrToken::DateTimeDelimiter);
1786            } else {
1787                match c.to_uppercase().parse() {
1788                    Ok(u) => t.push_back(TimeStrToken::TimeUnit(u)),
1789                    Err(_) => t.push_back(TimeStrToken::DateTimeUnit(c.parse()?)),
1790                }
1791            }
1792            c.clear();
1793        }
1794        Ok(())
1795    }
1796    let mut next_num_is_frac = false;
1797    for (i, chr) in value.chars().enumerate() {
1798        if !num_buf.is_empty() && !char_buf.is_empty() {
1799            return Err("Could not tokenize".into());
1800        }
1801        match chr {
1802            '+' => {
1803                maybe_tokenize_num_buf(&mut num_buf, i, &mut next_num_is_frac, &mut toks)?;
1804                maybe_tokenize_char_buf(&mut char_buf, &mut toks)?;
1805                toks.push_back(TimeStrToken::Plus);
1806            }
1807            '-' => {
1808                maybe_tokenize_num_buf(&mut num_buf, i, &mut next_num_is_frac, &mut toks)?;
1809                maybe_tokenize_char_buf(&mut char_buf, &mut toks)?;
1810                toks.push_back(TimeStrToken::Dash);
1811            }
1812            ':' => {
1813                maybe_tokenize_num_buf(&mut num_buf, i, &mut next_num_is_frac, &mut toks)?;
1814                maybe_tokenize_char_buf(&mut char_buf, &mut toks)?;
1815                toks.push_back(TimeStrToken::Colon);
1816            }
1817            '.' => {
1818                maybe_tokenize_num_buf(&mut num_buf, i, &mut next_num_is_frac, &mut toks)?;
1819                maybe_tokenize_char_buf(&mut char_buf, &mut toks)?;
1820                toks.push_back(TimeStrToken::Dot);
1821                next_num_is_frac = true;
1822            }
1823            chr if chr.is_digit(10) => {
1824                maybe_tokenize_char_buf(&mut char_buf, &mut toks)?;
1825                num_buf.push(chr);
1826            }
1827            chr if chr.is_ascii_alphabetic() => {
1828                maybe_tokenize_num_buf(&mut num_buf, i, &mut next_num_is_frac, &mut toks)?;
1829                char_buf.push(chr);
1830            }
1831            chr if chr.is_ascii_whitespace() || chr.is_ascii_punctuation() => {
1832                maybe_tokenize_num_buf(&mut num_buf, i, &mut next_num_is_frac, &mut toks)?;
1833                maybe_tokenize_char_buf(&mut char_buf, &mut toks)?;
1834                toks.push_back(TimeStrToken::Delim);
1835            }
1836            _ => {
1837                return Err(format!(
1838                    "Invalid character at offset {} in {}: {:?}",
1839                    i, value, chr
1840                ));
1841            }
1842        }
1843    }
1844
1845    maybe_tokenize_num_buf(&mut num_buf, value.len(), &mut next_num_is_frac, &mut toks)?;
1846    maybe_tokenize_char_buf(&mut char_buf, &mut toks)?;
1847
1848    ltrim_delim_or_colon(&mut toks);
1849
1850    Ok(toks)
1851}
1852
1853#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1854pub enum CalendarEra {
1855    BC,
1856    AD,
1857}
1858
1859/// Determines how ambiguous all-numeric dates like `01/02/03` are interpreted.
1860#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1861pub enum DateOrder {
1862    /// PostgreSQL semantics under the pinned `DateStyle = ISO, MDY`: a date
1863    /// whose leading field has one or two digits is month-day-year, and a
1864    /// two-digit year is windowed into 1970..=2069.
1865    Mdy,
1866    /// Always year-month-day, with no year windowing. Frozen behavior for
1867    /// storage source casts, which must not change across releases (see the
1868    /// stability contract in `mz_storage_types::sources::casts`).
1869    LegacyYmd,
1870}
1871
1872/// Takes a 'date timezone' 'date time timezone' string and splits it into 'date
1873/// {time}' and 'timezone' components then checks for a 'CalenderEra' defaulting
1874/// to 'AD'.
1875pub(crate) fn split_timestamp_string(value: &str) -> (&str, &str, CalendarEra) {
1876    // First we need to see if the string contains " +" or " -" because
1877    // timestamps can come in a format YYYY-MM-DD {+|-}<tz> (where the timezone
1878    // string can have colons)
1879    let cut = value.find(" +").or_else(|| value.find(" -"));
1880
1881    if let Some(cut) = cut {
1882        let (datetime, timezone) = value.split_at(cut);
1883
1884        let (timezone, era) = strip_era_from_timezone(timezone);
1885
1886        return (datetime.trim(), timezone.trim(), era);
1887    }
1888
1889    // If we have a hh:mm:dd component, we need to go past that to see if we can
1890    // find a tz
1891    let colon = value.find(':');
1892
1893    if let Some(colon) = colon {
1894        let substring = value.get(colon..);
1895        if let Some(substring) = substring {
1896            let tz = substring
1897                .find(|c: char| (c == '-') || (c == '+') || (c == ' ') || c.is_ascii_alphabetic());
1898
1899            if let Some(tz) = tz {
1900                let (datetime, timezone) = value.split_at(colon + tz);
1901
1902                let (timezone, era) = strip_era_from_timezone(timezone);
1903
1904                return (datetime.trim(), timezone.trim(), era);
1905            }
1906        }
1907
1908        (value.trim(), "", CalendarEra::AD)
1909    } else {
1910        // We don't have a time, so the only formats available are YYY-mm-dd<tz>
1911        // or YYYY-MM-dd <tz> Numeric offset timezones need to be separated from
1912        // the ymd by a space.
1913        let cut = value.find(|c: char| c.is_ascii_alphabetic());
1914
1915        if let Some(cut) = cut {
1916            let (datetime, timezone) = value.split_at(cut);
1917
1918            let (timezone, era) = strip_era_from_timezone(timezone);
1919
1920            return (datetime.trim(), timezone.trim(), era);
1921        }
1922
1923        (value.trim(), "", CalendarEra::AD)
1924    }
1925}
1926
1927// We support three era formats:
1928//  1. "<timestamp><separator><timezone> <era>"
1929//  2. "<timestamp> <era>"
1930//  3. "<timestamp><era>"
1931//
1932// NB(ptravers): pg supports more formats than noted above.
1933fn strip_era_from_timezone(timezone: &str) -> (&str, CalendarEra) {
1934    use CalendarEra::{AD, BC};
1935    let timezone = timezone.trim();
1936    let timezone_upper = timezone.to_uppercase();
1937
1938    // Covers cases 1 and 2.
1939    if timezone.len() < 3 {
1940        return match (
1941            timezone_upper.strip_suffix("BC"),
1942            timezone_upper.strip_suffix("AD"),
1943        ) {
1944            (Some(_), None) => ("", BC),
1945            (None, Some(_)) => ("", AD),
1946            // NB(ptravers): we expect this to fail when we go to check
1947            // the timezone is valid in the next step.
1948            _ => (timezone, AD),
1949        };
1950    }
1951
1952    // Covers case 3.
1953    //
1954    // Safety: upper case and lower case chars for a, b, c, d, and \s
1955    // are all 1 byte so suffix is always of length 3 bytes.
1956    match (
1957        timezone_upper.strip_suffix(" BC"),
1958        timezone_upper.strip_suffix(" AD"),
1959    ) {
1960        (Some(_), None) => (&timezone[..timezone.len() - 3], BC),
1961        (None, Some(_)) => (&timezone[..timezone.len() - 3], AD),
1962        _ => (timezone, AD),
1963    }
1964}
1965
1966/// An encoded packed variant of [`NaiveTime`].
1967///
1968/// We uphold the invariant that [`PackedNaiveTime`] sorts the same as
1969/// [`NaiveTime`].
1970#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
1971pub struct PackedNaiveTime([u8; Self::SIZE]);
1972
1973impl FixedSizeCodec<NaiveTime> for PackedNaiveTime {
1974    const SIZE: usize = 8;
1975
1976    fn as_bytes(&self) -> &[u8] {
1977        &self.0
1978    }
1979
1980    fn from_bytes(slice: &[u8]) -> Result<Self, String> {
1981        let buf: [u8; Self::SIZE] = slice.try_into().map_err(|_| {
1982            format!(
1983                "size for PackedNaiveTime is {} bytes, got {}",
1984                Self::SIZE,
1985                slice.len()
1986            )
1987        })?;
1988        Ok(PackedNaiveTime(buf))
1989    }
1990
1991    #[inline]
1992    fn from_value(value: NaiveTime) -> Self {
1993        let secs = value.num_seconds_from_midnight();
1994        let nano = value.nanosecond();
1995
1996        let mut buf = [0u8; Self::SIZE];
1997
1998        (buf[..4]).copy_from_slice(&secs.to_be_bytes());
1999        (buf[4..]).copy_from_slice(&nano.to_be_bytes());
2000
2001        PackedNaiveTime(buf)
2002    }
2003
2004    #[inline]
2005    fn into_value(self) -> NaiveTime {
2006        let mut secs = [0u8; 4];
2007        secs.copy_from_slice(&self.0[..4]);
2008        let secs = u32::from_be_bytes(secs);
2009
2010        let mut nano = [0u8; 4];
2011        nano.copy_from_slice(&self.0[4..]);
2012        let nano = u32::from_be_bytes(nano);
2013
2014        NaiveTime::from_num_seconds_from_midnight_opt(secs, nano)
2015            .expect("NaiveTime roundtrips with PackedNaiveTime")
2016    }
2017}
2018
2019#[cfg(test)]
2020mod tests {
2021    use itertools::Itertools;
2022    use proptest::{prop_assert_eq, proptest};
2023
2024    use crate::scalar::add_arb_duration;
2025
2026    use super::*;
2027
2028    #[mz_ore::test]
2029    fn iterate_datetimefield() {
2030        use DateTimeField::*;
2031        assert_eq!(
2032            Millennium.into_iter().take(10).collect::<Vec<_>>(),
2033            vec![
2034                Century,
2035                Decade,
2036                Year,
2037                Month,
2038                Day,
2039                Hour,
2040                Minute,
2041                Second,
2042                Milliseconds,
2043                Microseconds
2044            ]
2045        )
2046    }
2047
2048    #[mz_ore::test]
2049    fn test_expected_dur_like_tokens() {
2050        use DateTimeField::*;
2051        use TimeStrToken::*;
2052        assert_eq!(
2053            expected_sql_standard_interval_tokens(Hour),
2054            vec![Num(0, 1), Colon, Num(0, 1), Colon, Num(0, 1), Dot, Nanos(0)]
2055        );
2056        assert_eq!(
2057            expected_sql_standard_interval_tokens(Minute),
2058            vec![Num(0, 1), Colon, Num(0, 1), Dot, Nanos(0)]
2059        );
2060        assert_eq!(
2061            expected_sql_standard_interval_tokens(Second),
2062            vec![Num(0, 1), Dot, Nanos(0)]
2063        );
2064    }
2065
2066    #[mz_ore::test]
2067    fn test_expected_sql_standard_interval_tokens() {
2068        use DateTimeField::*;
2069        use TimeStrToken::*;
2070        assert_eq!(
2071            expected_sql_standard_interval_tokens(Year),
2072            vec![Num(0, 1), Dash, Num(0, 1), Delim]
2073        );
2074
2075        assert_eq!(
2076            expected_sql_standard_interval_tokens(Day),
2077            vec![Num(0, 1), Delim]
2078        );
2079        assert_eq!(
2080            expected_sql_standard_interval_tokens(Hour),
2081            vec![Num(0, 1), Colon, Num(0, 1), Colon, Num(0, 1), Dot, Nanos(0)]
2082        );
2083    }
2084    #[mz_ore::test]
2085    fn test_trim_and_return_sign() {
2086        let test_cases = [
2087            ("-2", -1, "2"),
2088            ("3", 1, "3"),
2089            ("+5", 1, "5"),
2090            ("-", -1, ""),
2091            ("-YEAR", -1, "YEAR"),
2092            ("YEAR", 1, "YEAR"),
2093        ];
2094
2095        for test in test_cases.iter() {
2096            let mut s = tokenize_time_str(test.0).unwrap();
2097
2098            assert_eq!(trim_and_return_sign(&mut s), test.1);
2099            assert_eq!(s.front(), tokenize_time_str(test.2).unwrap().front());
2100        }
2101    }
2102    #[mz_ore::test]
2103    fn test_determine_format_w_datetimefield() {
2104        use DateTimeField::*;
2105        use TimePartFormat::*;
2106
2107        let test_cases = [
2108            ("1-2 3", Some(SqlStandard(Year))),
2109            ("4:5", Some(SqlStandard(Hour))),
2110            ("4:5.6", Some(SqlStandard(Minute))),
2111            ("-4:5.6", Some(SqlStandard(Minute))),
2112            ("+4:5.6", Some(SqlStandard(Minute))),
2113            ("year", Some(PostgreSql(Year))),
2114            ("4year", Some(PostgreSql(Year))),
2115            ("-4year", Some(PostgreSql(Year))),
2116            ("5", None),
2117            ("5.6", None),
2118            ("3 4:5:6.7", None),
2119        ];
2120
2121        for test in test_cases.iter() {
2122            let s = tokenize_time_str(test.0).unwrap();
2123
2124            match (
2125                determine_format_w_datetimefield(s, None).unwrap(),
2126                test.1.as_ref(),
2127            ) {
2128                (Some(a), Some(b)) => {
2129                    if a != *b {
2130                        panic!(
2131                            "determine_format_w_datetimefield_and_time returned {:?}, expected {:?}",
2132                            a, b,
2133                        )
2134                    }
2135                }
2136                (None, None) => {}
2137                (x, y) => panic!(
2138                    "determine_format_w_datetimefield_and_time returned {:?}, expected {:?}",
2139                    x, y,
2140                ),
2141            }
2142        }
2143    }
2144    #[mz_ore::test]
2145    fn test_determine_format_w_datetimefield_and_leading_time() {
2146        use DateTimeField::*;
2147        use TimePartFormat::*;
2148
2149        assert_eq!(
2150            determine_format_w_datetimefield(tokenize_time_str("4:5").unwrap(), None,).unwrap(),
2151            Some(SqlStandard(Hour))
2152        );
2153        assert_eq!(
2154            determine_format_w_datetimefield(
2155                tokenize_time_str("4:5").unwrap(),
2156                Some(DateTimeField::Minute),
2157            )
2158            .unwrap(),
2159            Some(SqlStandard(Minute))
2160        );
2161        assert_eq!(
2162            determine_format_w_datetimefield(
2163                tokenize_time_str("4:5").unwrap(),
2164                Some(DateTimeField::Hour),
2165            )
2166            .unwrap(),
2167            Some(SqlStandard(Hour))
2168        );
2169    }
2170    #[mz_ore::test]
2171    fn test_determine_format_w_datetimefield_error() {
2172        let test_cases = [
2173            ("1+2", "Cannot determine format of all parts"),
2174            ("1:2+3", "Cannot determine format of all parts"),
2175            ("1:1YEAR2", "Cannot determine format of all parts"),
2176        ];
2177
2178        for test in test_cases.iter() {
2179            let s = tokenize_time_str(test.0).unwrap();
2180            match determine_format_w_datetimefield(s, None) {
2181                Err(e) => assert_eq!(e.to_string(), test.1),
2182                Ok(f) => panic!(
2183                    "Test passed when expected to fail: {}, generated {:?}",
2184                    test.0, f
2185                ),
2186            };
2187        }
2188    }
2189
2190    #[mz_ore::test]
2191    fn test_fill_pdt_from_tokens() {
2192        use DateTimeField::*;
2193        let test_cases = [
2194            (
2195                ParsedDateTime {
2196                    year: Some(DateTimeFieldValue::new(1, 0)),
2197                    month: Some(DateTimeFieldValue::new(2, 0)),
2198                    day: Some(DateTimeFieldValue::new(3, 0)),
2199                    hour: Some(DateTimeFieldValue::new(4, 0)),
2200                    minute: Some(DateTimeFieldValue::new(5, 0)),
2201                    second: Some(DateTimeFieldValue::new(6, 0)),
2202                    ..Default::default()
2203                },
2204                "1 2 3 4 5 6",
2205                "0 0 0 0 0 0",
2206                Year,
2207                1,
2208            ),
2209            (
2210                ParsedDateTime {
2211                    day: Some(DateTimeFieldValue::new(4, 0)),
2212                    hour: Some(DateTimeFieldValue::new(5, 0)),
2213                    minute: Some(DateTimeFieldValue::new(6, 0)),
2214                    ..Default::default()
2215                },
2216                "4 5 6",
2217                "0 0 0",
2218                Day,
2219                1,
2220            ),
2221            (
2222                ParsedDateTime {
2223                    day: Some(DateTimeFieldValue::new(-4, 0)),
2224                    hour: Some(DateTimeFieldValue::new(-5, 0)),
2225                    minute: Some(DateTimeFieldValue::new(-6, 0)),
2226                    ..Default::default()
2227                },
2228                "4 5 6",
2229                "0 0 0",
2230                Day,
2231                -1,
2232            ),
2233            // Mixed delimiter parsing
2234            (
2235                ParsedDateTime {
2236                    year: Some(DateTimeFieldValue::new(1, 0)),
2237                    month: Some(DateTimeFieldValue::new(2, 0)),
2238                    day: Some(DateTimeFieldValue::new(3, 0)),
2239                    hour: Some(DateTimeFieldValue::new(4, 0)),
2240                    minute: Some(DateTimeFieldValue::new(5, 0)),
2241                    second: Some(DateTimeFieldValue::new(6, 0)),
2242                    ..Default::default()
2243                },
2244                "1-2:3-4 5 6",
2245                "0-0:0-0 0 0",
2246                Year,
2247                1,
2248            ),
2249            // Skip an element at the end
2250            (
2251                ParsedDateTime {
2252                    year: Some(DateTimeFieldValue::new(1, 0)),
2253                    month: Some(DateTimeFieldValue::new(2, 0)),
2254                    day: Some(DateTimeFieldValue::new(3, 0)),
2255                    hour: Some(DateTimeFieldValue::new(5, 0)),
2256                    minute: Some(DateTimeFieldValue::new(6, 0)),
2257                    ..Default::default()
2258                },
2259                "1 2 3 5 6",
2260                "0 0 0 0 0 0",
2261                Year,
2262                1,
2263            ),
2264            // Skip an element w/ non-space parsing
2265            (
2266                ParsedDateTime {
2267                    year: Some(DateTimeFieldValue::new(1, 0)),
2268                    month: Some(DateTimeFieldValue::new(2, 0)),
2269                    day: Some(DateTimeFieldValue::new(3, 0)),
2270                    minute: Some(DateTimeFieldValue::new(5, 0)),
2271                    second: Some(DateTimeFieldValue::new(6, 0)),
2272                    ..Default::default()
2273                },
2274                "1-2:3- 5 6",
2275                "0-0:0-0 0 0",
2276                Year,
2277                1,
2278            ),
2279            // Get Nanos from tokenizer
2280            (
2281                ParsedDateTime {
2282                    year: Some(DateTimeFieldValue::new(1, 0)),
2283                    month: Some(DateTimeFieldValue::new(2, 0)),
2284                    day: Some(DateTimeFieldValue::new(3, 0)),
2285                    hour: Some(DateTimeFieldValue::new(4, 0)),
2286                    minute: Some(DateTimeFieldValue::new(5, 0)),
2287                    second: Some(DateTimeFieldValue::new(6, 700_000_000)),
2288                    ..Default::default()
2289                },
2290                "1-2:3-4 5 6.7",
2291                "0-0:0-0 0 0.0",
2292                Year,
2293                1,
2294            ),
2295            // Proper fraction/nano conversion anywhere
2296            (
2297                ParsedDateTime {
2298                    year: Some(DateTimeFieldValue::new(1, 200_000_000)),
2299                    ..Default::default()
2300                },
2301                "1.2",
2302                "0.0",
2303                Year,
2304                1,
2305            ),
2306            (
2307                ParsedDateTime {
2308                    minute: Some(DateTimeFieldValue::new(1, 200_000_000)),
2309                    ..Default::default()
2310                },
2311                "1.2",
2312                "0.0",
2313                Minute,
2314                1,
2315            ),
2316            // Parse TimeUnit
2317            (
2318                ParsedDateTime {
2319                    month: Some(DateTimeFieldValue::new(3, 0)),
2320                    ..Default::default()
2321                },
2322                "3MONTHS",
2323                "0YEAR",
2324                Month,
2325                1,
2326            ),
2327            (
2328                ParsedDateTime {
2329                    month: Some(DateTimeFieldValue::new(1, 0)),
2330                    day: Some(DateTimeFieldValue::new(2, 0)),
2331                    hour: Some(DateTimeFieldValue::new(3, 0)),
2332                    ..Default::default()
2333                },
2334                "1MONTHS 2DAYS 3HOURS",
2335                "0YEAR 0YEAR 0YEAR",
2336                Month,
2337                1,
2338            ),
2339            (
2340                ParsedDateTime {
2341                    month: Some(DateTimeFieldValue::new(1, 0)),
2342                    day: Some(DateTimeFieldValue::new(2, 0)),
2343                    ..Default::default()
2344                },
2345                "1MONTHS-2",
2346                "0YEAR-0",
2347                Month,
2348                1,
2349            ),
2350        ];
2351        for test in test_cases.iter() {
2352            let mut pdt = ParsedDateTime::default();
2353            let mut actual = tokenize_time_str(test.1).unwrap();
2354            let expected = tokenize_time_str(test.2).unwrap();
2355
2356            fill_pdt_from_tokens(&mut pdt, &mut actual, &expected, test.3, test.4).unwrap();
2357
2358            assert_eq!(pdt, test.0);
2359        }
2360    }
2361
2362    #[mz_ore::test]
2363    fn test_fill_pdt_from_tokens_errors() {
2364        use DateTimeField::*;
2365        let test_cases = [
2366            // Mismatched syntax
2367            (
2368                "1 2 3",
2369                "0-0 0",
2370                Year,
2371                1,
2372                "Invalid syntax at offset 1: provided Delim but expected Dash",
2373            ),
2374        ];
2375        for test in test_cases.iter() {
2376            let mut pdt = ParsedDateTime::default();
2377            let mut actual = tokenize_time_str(test.0).unwrap();
2378            let expected = tokenize_time_str(test.1).unwrap();
2379
2380            match fill_pdt_from_tokens(&mut pdt, &mut actual, &expected, test.2, test.3) {
2381                Err(e) => assert_eq!(e.to_string(), test.4),
2382                Ok(_) => panic!("Test passed when expected to fail, generated {:?}", pdt),
2383            };
2384        }
2385    }
2386    #[mz_ore::test]
2387    #[should_panic(expected = "Cannot get smaller DateTimeField than MICROSECONDS")]
2388    fn test_fill_pdt_from_tokens_panic() {
2389        use DateTimeField::*;
2390        let test_cases = [
2391            // Mismatched syntax
2392            ("1 2", "0 0", Microseconds, 1),
2393        ];
2394        for test in test_cases.iter() {
2395            let mut pdt = ParsedDateTime::default();
2396            let mut actual = tokenize_time_str(test.0).unwrap();
2397            let expected = tokenize_time_str(test.1).unwrap();
2398
2399            if fill_pdt_from_tokens(&mut pdt, &mut actual, &expected, test.2, test.3).is_ok() {
2400                panic!(
2401                    "test_fill_pdt_from_tokens_panic should have panicked. input {}\nformat {}\
2402                     \nDateTimeField {}\nGenerated ParsedDateTime {:?}",
2403                    test.0, test.1, test.2, pdt
2404                );
2405            };
2406        }
2407    }
2408
2409    #[mz_ore::test]
2410    fn test_fill_pdt_interval_pg() {
2411        use DateTimeField::*;
2412        let test_cases = [
2413            (
2414                ParsedDateTime {
2415                    year: Some(DateTimeFieldValue::new(2, 0)),
2416                    ..Default::default()
2417                },
2418                "2",
2419                Year,
2420            ),
2421            (
2422                ParsedDateTime {
2423                    month: Some(DateTimeFieldValue::new(2, 300_000_000)),
2424                    ..Default::default()
2425                },
2426                "2.3",
2427                Month,
2428            ),
2429            (
2430                ParsedDateTime {
2431                    day: Some(DateTimeFieldValue::new(-2, -300_000_000)),
2432                    ..Default::default()
2433                },
2434                "-2.3",
2435                Day,
2436            ),
2437            (
2438                ParsedDateTime {
2439                    hour: Some(DateTimeFieldValue::new(2, 0)),
2440                    ..Default::default()
2441                },
2442                "2",
2443                Hour,
2444            ),
2445            (
2446                ParsedDateTime {
2447                    minute: Some(DateTimeFieldValue::new(2, 300_000_000)),
2448                    ..Default::default()
2449                },
2450                "2.3",
2451                Minute,
2452            ),
2453            (
2454                ParsedDateTime {
2455                    second: Some(DateTimeFieldValue::new(-2, -300_000_000)),
2456                    ..Default::default()
2457                },
2458                "-2.3",
2459                Second,
2460            ),
2461            (
2462                ParsedDateTime {
2463                    year: Some(DateTimeFieldValue::new(2, 0)),
2464                    ..Default::default()
2465                },
2466                "2year",
2467                Year,
2468            ),
2469            (
2470                ParsedDateTime {
2471                    month: Some(DateTimeFieldValue::new(2, 300_000_000)),
2472                    ..Default::default()
2473                },
2474                "2.3month",
2475                Month,
2476            ),
2477            (
2478                ParsedDateTime {
2479                    day: Some(DateTimeFieldValue::new(-2, -300_000_000)),
2480                    ..Default::default()
2481                },
2482                "-2.3day",
2483                Day,
2484            ),
2485            (
2486                ParsedDateTime {
2487                    hour: Some(DateTimeFieldValue::new(2, 0)),
2488                    ..Default::default()
2489                },
2490                "2hour",
2491                Hour,
2492            ),
2493            (
2494                ParsedDateTime {
2495                    minute: Some(DateTimeFieldValue::new(2, 300_000_000)),
2496                    ..Default::default()
2497                },
2498                "2.3minute",
2499                Minute,
2500            ),
2501            (
2502                ParsedDateTime {
2503                    second: Some(DateTimeFieldValue::new(-2, -300_000_000)),
2504                    ..Default::default()
2505                },
2506                "-2.3second",
2507                Second,
2508            ),
2509            (
2510                ParsedDateTime {
2511                    second: Some(DateTimeFieldValue::new(-2, -300_000_000)),
2512                    ..Default::default()
2513                },
2514                ":::::::::-2.3second",
2515                Second,
2516            ),
2517            (
2518                ParsedDateTime {
2519                    second: Some(DateTimeFieldValue::new(2, 300_000_000)),
2520                    ..Default::default()
2521                },
2522                ":::::::::+2.3second",
2523                Second,
2524            ),
2525            (
2526                ParsedDateTime {
2527                    millisecond: Some(DateTimeFieldValue::new(1, 200_000_000)),
2528                    ..Default::default()
2529                },
2530                "1.2milliseconds",
2531                Milliseconds,
2532            ),
2533            (
2534                ParsedDateTime {
2535                    microsecond: Some(DateTimeFieldValue::new(2, 300_000_000)),
2536                    ..Default::default()
2537                },
2538                "2.3microseconds",
2539                Microseconds,
2540            ),
2541            (
2542                ParsedDateTime {
2543                    millennium: Some(DateTimeFieldValue::new(4, 500_000_000)),
2544                    ..Default::default()
2545                },
2546                "4.5millennium",
2547                Millennium,
2548            ),
2549            (
2550                ParsedDateTime {
2551                    century: Some(DateTimeFieldValue::new(6, 700_000_000)),
2552                    ..Default::default()
2553                },
2554                "6.7century",
2555                Century,
2556            ),
2557            (
2558                ParsedDateTime {
2559                    decade: Some(DateTimeFieldValue::new(8, 900_000_000)),
2560                    ..Default::default()
2561                },
2562                "8.9decade",
2563                Decade,
2564            ),
2565        ];
2566        for test in test_cases.iter() {
2567            let mut pdt = ParsedDateTime::default();
2568            let mut actual = tokenize_time_str(test.1).unwrap();
2569            fill_pdt_interval_pg(&mut actual, test.2, &mut pdt).unwrap();
2570
2571            assert_eq!(pdt, test.0);
2572        }
2573    }
2574
2575    #[mz_ore::test]
2576    fn fill_pdt_interval_pg_errors() {
2577        use DateTimeField::*;
2578        let test_cases = [
2579            // Invalid syntax
2580            (
2581                "1.2.",
2582                Month,
2583                "Invalid syntax at offset 3: provided Dot but expected TimeUnit(Year)",
2584            ),
2585            // Running into this error means that determine_format_w_datetimefield
2586            // failed.
2587            (
2588                "1YEAR",
2589                Month,
2590                "Invalid syntax at offset 1: provided TimeUnit(YEAR) but expected TimeUnit(MONTH)",
2591            ),
2592        ];
2593        for test in test_cases.iter() {
2594            let mut pdt = ParsedDateTime::default();
2595            let mut actual = tokenize_time_str(test.0).unwrap();
2596            match fill_pdt_interval_pg(&mut actual, test.1, &mut pdt) {
2597                Err(e) => assert_eq!(e.to_string(), test.2),
2598                Ok(_) => panic!(
2599                    "Test passed when expected to fail, generated {:?}, expected error {}",
2600                    pdt, test.2,
2601                ),
2602            };
2603        }
2604    }
2605
2606    #[mz_ore::test]
2607    fn test_fill_pdt_interval_sql() {
2608        use DateTimeField::*;
2609        let test_cases = [
2610            (
2611                ParsedDateTime {
2612                    year: Some(DateTimeFieldValue::new(1, 0)),
2613                    month: Some(DateTimeFieldValue::new(2, 0)),
2614                    ..Default::default()
2615                },
2616                "1-2",
2617                Year,
2618            ),
2619            (
2620                ParsedDateTime {
2621                    hour: Some(DateTimeFieldValue::new(1, 0)),
2622                    minute: Some(DateTimeFieldValue::new(2, 0)),
2623                    second: Some(DateTimeFieldValue::new(3, 400_000_000)),
2624                    ..Default::default()
2625                },
2626                "1:2:3.4",
2627                Hour,
2628            ),
2629            (
2630                ParsedDateTime {
2631                    hour: Some(DateTimeFieldValue::new(1, 0)),
2632                    minute: Some(DateTimeFieldValue::new(0, 0)),
2633                    second: Some(DateTimeFieldValue::new(3, 400_000_000)),
2634                    ..Default::default()
2635                },
2636                "1::3.4",
2637                Hour,
2638            ),
2639            (
2640                ParsedDateTime {
2641                    hour: Some(DateTimeFieldValue::new(1, 0)),
2642                    minute: Some(DateTimeFieldValue::new(0, 0)),
2643                    second: Some(DateTimeFieldValue::new(0, 400_000_000)),
2644                    ..Default::default()
2645                },
2646                "1::.4",
2647                Hour,
2648            ),
2649            (
2650                ParsedDateTime {
2651                    hour: Some(DateTimeFieldValue::new(0, 0)),
2652                    minute: Some(DateTimeFieldValue::new(0, 0)),
2653                    second: Some(DateTimeFieldValue::new(0, 400_000_000)),
2654                    ..Default::default()
2655                },
2656                ".4",
2657                Second,
2658            ),
2659            (
2660                ParsedDateTime {
2661                    hour: Some(DateTimeFieldValue::new(0, 0)),
2662                    minute: Some(DateTimeFieldValue::new(1, 0)),
2663                    second: Some(DateTimeFieldValue::new(2, 300_000_000)),
2664                    ..Default::default()
2665                },
2666                "1:2.3",
2667                Minute,
2668            ),
2669            (
2670                ParsedDateTime {
2671                    year: Some(DateTimeFieldValue::new(-1, 0)),
2672                    month: Some(DateTimeFieldValue::new(-2, 0)),
2673                    ..Default::default()
2674                },
2675                "-1-2",
2676                Year,
2677            ),
2678            (
2679                ParsedDateTime {
2680                    hour: Some(DateTimeFieldValue::new(-1, 0)),
2681                    minute: Some(DateTimeFieldValue::new(-2, 0)),
2682                    second: Some(DateTimeFieldValue::new(-3, -400_000_000)),
2683                    ..Default::default()
2684                },
2685                "-1:2:3.4",
2686                Hour,
2687            ),
2688            (
2689                ParsedDateTime {
2690                    year: Some(DateTimeFieldValue::new(1, 0)),
2691                    month: Some(DateTimeFieldValue::new(2, 0)),
2692                    ..Default::default()
2693                },
2694                "+1-2",
2695                Year,
2696            ),
2697            (
2698                ParsedDateTime {
2699                    hour: Some(DateTimeFieldValue::new(1, 0)),
2700                    minute: Some(DateTimeFieldValue::new(2, 0)),
2701                    second: Some(DateTimeFieldValue::new(3, 400_000_000)),
2702                    ..Default::default()
2703                },
2704                "+1:2:3.4",
2705                Hour,
2706            ),
2707            (
2708                ParsedDateTime {
2709                    year: Some(DateTimeFieldValue::new(-1, 0)),
2710                    month: Some(DateTimeFieldValue::new(-2, 0)),
2711                    ..Default::default()
2712                },
2713                "::::::-1-2",
2714                Year,
2715            ),
2716            (
2717                ParsedDateTime {
2718                    hour: Some(DateTimeFieldValue::new(-1, 0)),
2719                    minute: Some(DateTimeFieldValue::new(-2, 0)),
2720                    second: Some(DateTimeFieldValue::new(-3, -400_000_000)),
2721                    ..Default::default()
2722                },
2723                ":::::::-1:2:3.4",
2724                Hour,
2725            ),
2726        ];
2727        for test in test_cases.iter() {
2728            let mut pdt = ParsedDateTime::default();
2729            let mut actual = tokenize_time_str(test.1).unwrap();
2730            fill_pdt_interval_sql(&mut actual, test.2, &mut pdt).unwrap();
2731
2732            assert_eq!(pdt, test.0);
2733        }
2734    }
2735
2736    #[mz_ore::test]
2737    fn test_fill_pdt_interval_sql_errors() {
2738        use DateTimeField::*;
2739        let test_cases = [
2740            // Invalid syntax
2741            (
2742                "1.2",
2743                Year,
2744                "Invalid syntax at offset 1: provided Dot but expected Dash",
2745            ),
2746            (
2747                "1-2:3.4",
2748                Minute,
2749                "Invalid syntax at offset 1: provided Dash but expected Colon",
2750            ),
2751            (
2752                "1YEAR",
2753                Year,
2754                "Invalid syntax at offset 1: provided TimeUnit(Year) but expected Dash",
2755            ),
2756        ];
2757        for test in test_cases.iter() {
2758            let mut pdt = ParsedDateTime::default();
2759            let mut actual = tokenize_time_str(test.0).unwrap();
2760            match fill_pdt_interval_sql(&mut actual, test.1, &mut pdt) {
2761                Err(e) => assert_eq!(e.to_string(), test.2),
2762                Ok(_) => panic!("Test passed when expected to fail, generated {:?}", pdt),
2763            };
2764        }
2765    }
2766
2767    #[mz_ore::test]
2768    fn test_build_parsed_datetime_time() {
2769        run_test_build_parsed_datetime_time(
2770            "3:4:5.6",
2771            ParsedDateTime {
2772                hour: Some(DateTimeFieldValue::new(3, 0)),
2773                minute: Some(DateTimeFieldValue::new(4, 0)),
2774                second: Some(DateTimeFieldValue::new(5, 600_000_000)),
2775                ..Default::default()
2776            },
2777        );
2778        run_test_build_parsed_datetime_time(
2779            "3:4",
2780            ParsedDateTime {
2781                hour: Some(DateTimeFieldValue::new(3, 0)),
2782                minute: Some(DateTimeFieldValue::new(4, 0)),
2783                ..Default::default()
2784            },
2785        );
2786        run_test_build_parsed_datetime_time(
2787            "3:4.5",
2788            ParsedDateTime {
2789                minute: Some(DateTimeFieldValue::new(3, 0)),
2790                second: Some(DateTimeFieldValue::new(4, 500_000_000)),
2791                ..Default::default()
2792            },
2793        );
2794        run_test_build_parsed_datetime_time(
2795            "0::4.5",
2796            ParsedDateTime {
2797                hour: Some(DateTimeFieldValue::new(0, 0)),
2798                second: Some(DateTimeFieldValue::new(4, 500_000_000)),
2799                ..Default::default()
2800            },
2801        );
2802        run_test_build_parsed_datetime_time(
2803            "0::.5",
2804            ParsedDateTime {
2805                hour: Some(DateTimeFieldValue::new(0, 0)),
2806                second: Some(DateTimeFieldValue::new(0, 500_000_000)),
2807                ..Default::default()
2808            },
2809        );
2810
2811        fn run_test_build_parsed_datetime_time(test: &str, res: ParsedDateTime) {
2812            assert_eq!(
2813                ParsedDateTime::build_parsed_datetime_time(test).unwrap(),
2814                res
2815            );
2816        }
2817    }
2818
2819    #[mz_ore::test]
2820    fn test_build_parsed_datetime_timestamp() {
2821        run_test_build_parsed_datetime_timestamp(
2822            "2000-01-02",
2823            ParsedDateTime {
2824                year: Some(DateTimeFieldValue::new(2000, 0)),
2825                month: Some(DateTimeFieldValue::new(1, 0)),
2826                day: Some(DateTimeFieldValue::new(2, 0)),
2827                ..Default::default()
2828            },
2829        );
2830        run_test_build_parsed_datetime_timestamp(
2831            "2000",
2832            ParsedDateTime {
2833                year: Some(DateTimeFieldValue::new(2000, 0)),
2834                ..Default::default()
2835            },
2836        );
2837        run_test_build_parsed_datetime_timestamp(
2838            "2000-1-",
2839            ParsedDateTime {
2840                year: Some(DateTimeFieldValue::new(2000, 0)),
2841                month: Some(DateTimeFieldValue::new(1, 0)),
2842                ..Default::default()
2843            },
2844        );
2845        run_test_build_parsed_datetime_timestamp(
2846            "2000-01-02 3:4:5.6",
2847            ParsedDateTime {
2848                year: Some(DateTimeFieldValue::new(2000, 0)),
2849                month: Some(DateTimeFieldValue::new(1, 0)),
2850                day: Some(DateTimeFieldValue::new(2, 0)),
2851                hour: Some(DateTimeFieldValue::new(3, 0)),
2852                minute: Some(DateTimeFieldValue::new(4, 0)),
2853                second: Some(DateTimeFieldValue::new(5, 600_000_000)),
2854                ..Default::default()
2855            },
2856        );
2857        run_test_build_parsed_datetime_timestamp(
2858            "2000-01-02T3:4:5.6",
2859            ParsedDateTime {
2860                year: Some(DateTimeFieldValue::new(2000, 0)),
2861                month: Some(DateTimeFieldValue::new(1, 0)),
2862                day: Some(DateTimeFieldValue::new(2, 0)),
2863                hour: Some(DateTimeFieldValue::new(3, 0)),
2864                minute: Some(DateTimeFieldValue::new(4, 0)),
2865                second: Some(DateTimeFieldValue::new(5, 600_000_000)),
2866                ..Default::default()
2867            },
2868        );
2869        // A 6-digit year with explicit `-` separators must be read as a full
2870        // year, not a compact `YYMMDD` date. This value is producible by our own
2871        // timestamp formatting, so parsing it back must round-trip.
2872        run_test_build_parsed_datetime_timestamp(
2873            "118852-02-05 00:00:00",
2874            ParsedDateTime {
2875                year: Some(DateTimeFieldValue::new(118852, 0)),
2876                month: Some(DateTimeFieldValue::new(2, 0)),
2877                day: Some(DateTimeFieldValue::new(5, 0)),
2878                hour: Some(DateTimeFieldValue::new(0, 0)),
2879                minute: Some(DateTimeFieldValue::new(0, 0)),
2880                second: Some(DateTimeFieldValue::new(0, 0)),
2881                ..Default::default()
2882            },
2883        );
2884        // A standalone number with no separators is still parsed as a compact
2885        // date (`YYYYMMDD`).
2886        run_test_build_parsed_datetime_timestamp(
2887            "20000102",
2888            ParsedDateTime {
2889                year: Some(DateTimeFieldValue::new(2000, 0)),
2890                month: Some(DateTimeFieldValue::new(1, 0)),
2891                day: Some(DateTimeFieldValue::new(2, 0)),
2892                ..Default::default()
2893            },
2894        );
2895        // A one- or two-digit leading field starts a month-day-year date, and
2896        // a two-digit year is windowed into 1970..=2069.
2897        run_test_build_parsed_datetime_timestamp(
2898            "01/02/03",
2899            ParsedDateTime {
2900                year: Some(DateTimeFieldValue::new(2003, 0)),
2901                month: Some(DateTimeFieldValue::new(1, 0)),
2902                day: Some(DateTimeFieldValue::new(2, 0)),
2903                ..Default::default()
2904            },
2905        );
2906        run_test_build_parsed_datetime_timestamp(
2907            "01-02-70 3:4:5.6",
2908            ParsedDateTime {
2909                year: Some(DateTimeFieldValue::new(1970, 0)),
2910                month: Some(DateTimeFieldValue::new(1, 0)),
2911                day: Some(DateTimeFieldValue::new(2, 0)),
2912                hour: Some(DateTimeFieldValue::new(3, 0)),
2913                minute: Some(DateTimeFieldValue::new(4, 0)),
2914                second: Some(DateTimeFieldValue::new(5, 600_000_000)),
2915                ..Default::default()
2916            },
2917        );
2918        // A four-digit year in month-day-year position is not windowed.
2919        run_test_build_parsed_datetime_timestamp(
2920            "1/2/1999",
2921            ParsedDateTime {
2922                year: Some(DateTimeFieldValue::new(1999, 0)),
2923                month: Some(DateTimeFieldValue::new(1, 0)),
2924                day: Some(DateTimeFieldValue::new(2, 0)),
2925                ..Default::default()
2926            },
2927        );
2928        // BC years are never windowed.
2929        assert_eq!(
2930            ParsedDateTime::build_parsed_datetime_timestamp(
2931                "01/02/03",
2932                CalendarEra::BC,
2933                DateOrder::Mdy,
2934            )
2935            .unwrap(),
2936            ParsedDateTime {
2937                year: Some(DateTimeFieldValue::new(-3, 0)),
2938                month: Some(DateTimeFieldValue::new(1, 0)),
2939                day: Some(DateTimeFieldValue::new(2, 0)),
2940                ..Default::default()
2941            },
2942        );
2943        // The legacy order keeps the frozen year-month-day interpretation
2944        // with no windowing.
2945        assert_eq!(
2946            ParsedDateTime::build_parsed_datetime_timestamp(
2947                "01/02/03",
2948                CalendarEra::AD,
2949                DateOrder::LegacyYmd,
2950            )
2951            .unwrap(),
2952            ParsedDateTime {
2953                year: Some(DateTimeFieldValue::new(1, 0)),
2954                month: Some(DateTimeFieldValue::new(2, 0)),
2955                day: Some(DateTimeFieldValue::new(3, 0)),
2956                ..Default::default()
2957            },
2958        );
2959
2960        fn run_test_build_parsed_datetime_timestamp(test: &str, res: ParsedDateTime) {
2961            assert_eq!(
2962                ParsedDateTime::build_parsed_datetime_timestamp(
2963                    test,
2964                    CalendarEra::AD,
2965                    DateOrder::Mdy
2966                )
2967                .unwrap(),
2968                res
2969            );
2970        }
2971    }
2972
2973    #[mz_ore::test]
2974    fn test_build_parsed_datetime_interval() {
2975        use DateTimeField::*;
2976        let test_cases = [
2977            (
2978                ParsedDateTime {
2979                    year: Some(DateTimeFieldValue::new(1, 0)),
2980                    month: Some(DateTimeFieldValue::new(2, 0)),
2981                    day: Some(DateTimeFieldValue::new(3, 0)),
2982                    hour: Some(DateTimeFieldValue::new(4, 0)),
2983                    minute: Some(DateTimeFieldValue::new(5, 0)),
2984                    second: Some(DateTimeFieldValue::new(6, 700_000_000)),
2985                    ..Default::default()
2986                },
2987                "1-2 3 4:5:6.7",
2988                Second,
2989            ),
2990            (
2991                ParsedDateTime {
2992                    year: Some(DateTimeFieldValue::new(1, 0)),
2993                    month: Some(DateTimeFieldValue::new(2, 0)),
2994                    day: Some(DateTimeFieldValue::new(3, 0)),
2995                    hour: Some(DateTimeFieldValue::new(4, 0)),
2996                    minute: Some(DateTimeFieldValue::new(5, 0)),
2997                    second: Some(DateTimeFieldValue::new(6, 700_000_000)),
2998                    ..Default::default()
2999                },
3000                "1 year 2 months 3 days 4 hours 5 minutes 6.7 seconds",
3001                Second,
3002            ),
3003            (
3004                ParsedDateTime {
3005                    second: Some(DateTimeFieldValue::new(1, 0)),
3006                    ..Default::default()
3007                },
3008                "1",
3009                Second,
3010            ),
3011            (
3012                ParsedDateTime {
3013                    day: Some(DateTimeFieldValue::new(1, 0)),
3014                    ..Default::default()
3015                },
3016                "1",
3017                Day,
3018            ),
3019            (
3020                ParsedDateTime {
3021                    month: Some(DateTimeFieldValue::new(1, 0)),
3022                    ..Default::default()
3023                },
3024                "1",
3025                Month,
3026            ),
3027            (
3028                ParsedDateTime {
3029                    hour: Some(DateTimeFieldValue::new(1, 0)),
3030                    minute: Some(DateTimeFieldValue::new(0, 0)),
3031                    second: Some(DateTimeFieldValue::new(0, 0)),
3032                    ..Default::default()
3033                },
3034                "1:",
3035                Second,
3036            ),
3037            (
3038                ParsedDateTime {
3039                    year: Some(DateTimeFieldValue::new(1, 0)),
3040                    month: Some(DateTimeFieldValue::new(0, 0)),
3041                    ..Default::default()
3042                },
3043                "1-",
3044                Second,
3045            ),
3046            (
3047                ParsedDateTime {
3048                    day: Some(DateTimeFieldValue::new(1, 0)),
3049                    hour: Some(DateTimeFieldValue::new(2, 0)),
3050                    minute: Some(DateTimeFieldValue::new(0, 0)),
3051                    second: Some(DateTimeFieldValue::new(0, 0)),
3052                    ..Default::default()
3053                },
3054                "1 2:",
3055                Second,
3056            ),
3057            (
3058                ParsedDateTime {
3059                    year: Some(DateTimeFieldValue::new(1, 0)),
3060                    month: Some(DateTimeFieldValue::new(2, 0)),
3061                    hour: Some(DateTimeFieldValue::new(3, 0)),
3062                    minute: Some(DateTimeFieldValue::new(4, 0)),
3063                    second: Some(DateTimeFieldValue::new(0, 0)),
3064                    ..Default::default()
3065                },
3066                "1-2 3:4",
3067                Second,
3068            ),
3069            (
3070                ParsedDateTime {
3071                    year: Some(DateTimeFieldValue::new(1, 0)),
3072                    month: Some(DateTimeFieldValue::new(0, 0)),
3073                    day: Some(DateTimeFieldValue::new(2, 0)),
3074                    hour: Some(DateTimeFieldValue::new(3, 0)),
3075                    minute: Some(DateTimeFieldValue::new(0, 0)),
3076                    second: Some(DateTimeFieldValue::new(0, 0)),
3077                    ..Default::default()
3078                },
3079                "1- 2 3:",
3080                Second,
3081            ),
3082            (
3083                ParsedDateTime {
3084                    year: Some(DateTimeFieldValue::new(5, 0)),
3085                    month: Some(DateTimeFieldValue::new(6, 0)),
3086                    hour: Some(DateTimeFieldValue::new(1, 0)),
3087                    minute: Some(DateTimeFieldValue::new(2, 0)),
3088                    second: Some(DateTimeFieldValue::new(3, 400_000_000)),
3089                    ..Default::default()
3090                },
3091                "1:2:3.4 5-6",
3092                Second,
3093            ),
3094            (
3095                ParsedDateTime {
3096                    year: Some(DateTimeFieldValue::new(1, 0)),
3097                    month: Some(DateTimeFieldValue::new(2, 0)),
3098                    hour: Some(DateTimeFieldValue::new(3, 0)),
3099                    ..Default::default()
3100                },
3101                "1-2 3",
3102                Hour,
3103            ),
3104            (
3105                ParsedDateTime {
3106                    year: Some(DateTimeFieldValue::new(-1, 0)),
3107                    month: Some(DateTimeFieldValue::new(-2, 0)),
3108                    day: Some(DateTimeFieldValue::new(-3, 0)),
3109                    hour: Some(DateTimeFieldValue::new(-4, 0)),
3110                    minute: Some(DateTimeFieldValue::new(-5, 0)),
3111                    second: Some(DateTimeFieldValue::new(-6, -700_000_000)),
3112                    ..Default::default()
3113                },
3114                "-1-2 -3 -4:5:6.7",
3115                Second,
3116            ),
3117            (
3118                ParsedDateTime {
3119                    year: Some(DateTimeFieldValue::new(-1, 0)),
3120                    month: Some(DateTimeFieldValue::new(-2, 0)),
3121                    day: Some(DateTimeFieldValue::new(3, 0)),
3122                    hour: Some(DateTimeFieldValue::new(-4, 0)),
3123                    minute: Some(DateTimeFieldValue::new(-5, 0)),
3124                    second: Some(DateTimeFieldValue::new(-6, -700_000_000)),
3125                    ..Default::default()
3126                },
3127                "-1-2 3 -4:5:6.7",
3128                Second,
3129            ),
3130            (
3131                ParsedDateTime {
3132                    year: Some(DateTimeFieldValue::new(-1, 0)),
3133                    month: Some(DateTimeFieldValue::new(-2, 0)),
3134                    day: Some(DateTimeFieldValue::new(-3, 0)),
3135                    hour: Some(DateTimeFieldValue::new(4, 0)),
3136                    minute: Some(DateTimeFieldValue::new(0, 0)),
3137                    second: Some(DateTimeFieldValue::new(0, 500_000_000)),
3138                    ..Default::default()
3139                },
3140                "-1-2 -3 4::.5",
3141                Second,
3142            ),
3143            (
3144                ParsedDateTime {
3145                    hour: Some(DateTimeFieldValue::new(0, 0)),
3146                    minute: Some(DateTimeFieldValue::new(0, 0)),
3147                    second: Some(DateTimeFieldValue::new(-1, -270_000_000)),
3148                    ..Default::default()
3149                },
3150                "-::1.27",
3151                Second,
3152            ),
3153            (
3154                ParsedDateTime {
3155                    second: Some(DateTimeFieldValue::new(1, 270_000_000)),
3156                    ..Default::default()
3157                },
3158                ":::::1.27",
3159                Second,
3160            ),
3161            (
3162                ParsedDateTime {
3163                    year: Some(DateTimeFieldValue::new(1, 0)),
3164                    month: Some(DateTimeFieldValue::new(0, 0)),
3165                    day: Some(DateTimeFieldValue::new(2, 0)),
3166                    hour: Some(DateTimeFieldValue::new(3, 0)),
3167                    minute: Some(DateTimeFieldValue::new(0, 0)),
3168                    second: Some(DateTimeFieldValue::new(0, 0)),
3169                    ..Default::default()
3170                },
3171                ":::1- ::2 ::::3:",
3172                Second,
3173            ),
3174            (
3175                ParsedDateTime {
3176                    year: Some(DateTimeFieldValue::new(1, 0)),
3177                    month: Some(DateTimeFieldValue::new(2, 0)),
3178                    day: Some(DateTimeFieldValue::new(3, 0)),
3179                    hour: Some(DateTimeFieldValue::new(4, 0)),
3180                    minute: Some(DateTimeFieldValue::new(5, 0)),
3181                    second: Some(DateTimeFieldValue::new(6, 700_000_000)),
3182                    ..Default::default()
3183                },
3184                "1 years 2 months 3 days 4 hours 5 minutes 6.7 seconds",
3185                Second,
3186            ),
3187            (
3188                ParsedDateTime {
3189                    year: Some(DateTimeFieldValue::new(1, 0)),
3190                    month: Some(DateTimeFieldValue::new(2, 0)),
3191                    day: Some(DateTimeFieldValue::new(3, 0)),
3192                    hour: Some(DateTimeFieldValue::new(4, 0)),
3193                    minute: Some(DateTimeFieldValue::new(5, 0)),
3194                    second: Some(DateTimeFieldValue::new(6, 700_000_000)),
3195                    ..Default::default()
3196                },
3197                "1y 2mon 3d 4h 5m 6.7s",
3198                Second,
3199            ),
3200            (
3201                ParsedDateTime {
3202                    year: Some(DateTimeFieldValue::new(1, 0)),
3203                    month: Some(DateTimeFieldValue::new(2, 0)),
3204                    day: Some(DateTimeFieldValue::new(3, 0)),
3205                    hour: Some(DateTimeFieldValue::new(4, 0)),
3206                    minute: Some(DateTimeFieldValue::new(5, 0)),
3207                    second: Some(DateTimeFieldValue::new(6, 700_000_000)),
3208                    ..Default::default()
3209                },
3210                "6.7 seconds 5 minutes 3 days 4 hours 1 year 2 month",
3211                Second,
3212            ),
3213            (
3214                ParsedDateTime {
3215                    year: Some(DateTimeFieldValue::new(-1, 0)),
3216                    month: Some(DateTimeFieldValue::new(2, 0)),
3217                    day: Some(DateTimeFieldValue::new(-3, 0)),
3218                    hour: Some(DateTimeFieldValue::new(4, 0)),
3219                    minute: Some(DateTimeFieldValue::new(5, 0)),
3220                    second: Some(DateTimeFieldValue::new(-6, -700_000_000)),
3221                    ..Default::default()
3222                },
3223                "-6.7 seconds 5 minutes -3 days 4 hours -1 year 2 month",
3224                Second,
3225            ),
3226            (
3227                ParsedDateTime {
3228                    year: Some(DateTimeFieldValue::new(1, 0)),
3229                    month: Some(DateTimeFieldValue::new(2, 300_000_000)),
3230                    day: Some(DateTimeFieldValue::new(4, 500_000_000)),
3231                    ..Default::default()
3232                },
3233                "1y 2.3mon 4.5d",
3234                Second,
3235            ),
3236            (
3237                ParsedDateTime {
3238                    year: Some(DateTimeFieldValue::new(-1, -200_000_000)),
3239                    month: Some(DateTimeFieldValue::new(2, 300_000_000)),
3240                    day: Some(DateTimeFieldValue::new(-3, -400_000_000)),
3241                    hour: Some(DateTimeFieldValue::new(4, 500_000_000)),
3242                    minute: Some(DateTimeFieldValue::new(5, 600_000_000)),
3243                    second: Some(DateTimeFieldValue::new(-6, -700_000_000)),
3244                    ..Default::default()
3245                },
3246                "-6.7 seconds 5.6 minutes -3.4 days 4.5 hours -1.2 year 2.3 month",
3247                Second,
3248            ),
3249            (
3250                ParsedDateTime {
3251                    day: Some(DateTimeFieldValue::new(1, 0)),
3252                    second: Some(DateTimeFieldValue::new(0, -270_000_000)),
3253                    ..Default::default()
3254                },
3255                "1 day -0.27 seconds",
3256                Second,
3257            ),
3258            (
3259                ParsedDateTime {
3260                    day: Some(DateTimeFieldValue::new(-1, 0)),
3261                    second: Some(DateTimeFieldValue::new(0, 270_000_000)),
3262                    ..Default::default()
3263                },
3264                "-1 day 0.27 seconds",
3265                Second,
3266            ),
3267            (
3268                ParsedDateTime {
3269                    year: Some(DateTimeFieldValue::new(10, 333_000_000)),
3270                    ..Default::default()
3271                },
3272                "10.333 years",
3273                Second,
3274            ),
3275            (
3276                ParsedDateTime {
3277                    year: Some(DateTimeFieldValue::new(10, 333_000_000)),
3278                    ..Default::default()
3279                },
3280                "10.333",
3281                Year,
3282            ),
3283            (
3284                ParsedDateTime {
3285                    year: Some(DateTimeFieldValue::new(1, 0)),
3286                    month: Some(DateTimeFieldValue::new(2, 0)),
3287                    day: Some(DateTimeFieldValue::new(5, 0)),
3288                    hour: Some(DateTimeFieldValue::new(3, 0)),
3289                    minute: Some(DateTimeFieldValue::new(4, 0)),
3290                    second: Some(DateTimeFieldValue::new(0, 0)),
3291                    ..Default::default()
3292                },
3293                "1-2 3:4 5 day",
3294                Second,
3295            ),
3296            (
3297                ParsedDateTime {
3298                    year: Some(DateTimeFieldValue::new(1, 0)),
3299                    month: Some(DateTimeFieldValue::new(2, 0)),
3300                    day: Some(DateTimeFieldValue::new(5, 0)),
3301                    hour: Some(DateTimeFieldValue::new(3, 0)),
3302                    minute: Some(DateTimeFieldValue::new(4, 0)),
3303                    second: Some(DateTimeFieldValue::new(0, 0)),
3304                    ..Default::default()
3305                },
3306                "5 day 3:4 1-2",
3307                Second,
3308            ),
3309            (
3310                ParsedDateTime {
3311                    year: Some(DateTimeFieldValue::new(1, 0)),
3312                    month: Some(DateTimeFieldValue::new(2, 0)),
3313                    day: Some(DateTimeFieldValue::new(5, 0)),
3314                    hour: Some(DateTimeFieldValue::new(3, 0)),
3315                    minute: Some(DateTimeFieldValue::new(4, 0)),
3316                    second: Some(DateTimeFieldValue::new(0, 0)),
3317                    ..Default::default()
3318                },
3319                "1-2 5 day 3:4",
3320                Second,
3321            ),
3322            (
3323                ParsedDateTime {
3324                    year: Some(DateTimeFieldValue::new(1, 0)),
3325                    day: Some(DateTimeFieldValue::new(2, 0)),
3326                    hour: Some(DateTimeFieldValue::new(3, 0)),
3327                    minute: Some(DateTimeFieldValue::new(4, 0)),
3328                    second: Some(DateTimeFieldValue::new(5, 600_000_000)),
3329                    ..Default::default()
3330                },
3331                "+1 year +2 days +3:4:5.6",
3332                Second,
3333            ),
3334            (
3335                ParsedDateTime {
3336                    year: Some(DateTimeFieldValue::new(1, 0)),
3337                    month: Some(DateTimeFieldValue::new(2, 0)),
3338                    ..Default::default()
3339                },
3340                "1-2",
3341                Month,
3342            ),
3343            (
3344                ParsedDateTime {
3345                    year: Some(DateTimeFieldValue::new(1, 0)),
3346                    month: Some(DateTimeFieldValue::new(2, 0)),
3347                    ..Default::default()
3348                },
3349                "1-2",
3350                Minute,
3351            ),
3352            (
3353                ParsedDateTime {
3354                    day: Some(DateTimeFieldValue::new(1, 999_999_999)),
3355                    ..Default::default()
3356                },
3357                "1.999999999999999999 days",
3358                Second,
3359            ),
3360            (
3361                ParsedDateTime {
3362                    millisecond: Some(DateTimeFieldValue::new(1, 200_000_000)),
3363                    ..Default::default()
3364                },
3365                "1.2ms",
3366                Second,
3367            ),
3368            (
3369                ParsedDateTime {
3370                    millisecond: Some(DateTimeFieldValue::new(1, 0)),
3371                    ..Default::default()
3372                },
3373                "1ms",
3374                Second,
3375            ),
3376            (
3377                ParsedDateTime {
3378                    millisecond: Some(DateTimeFieldValue::new(2100, 0)),
3379                    ..Default::default()
3380                },
3381                "2100ms",
3382                Second,
3383            ),
3384            (
3385                ParsedDateTime {
3386                    hour: Some(DateTimeFieldValue::new(1, 0)),
3387                    millisecond: Some(DateTimeFieldValue::new(2, 0)),
3388                    ..Default::default()
3389                },
3390                "1h 2ms",
3391                Second,
3392            ),
3393            (
3394                ParsedDateTime {
3395                    millisecond: Some(DateTimeFieldValue::new(42, 900_000_000)),
3396                    ..Default::default()
3397                },
3398                "42.9 milliseconds",
3399                Second,
3400            ),
3401            (
3402                ParsedDateTime {
3403                    second: Some(DateTimeFieldValue::new(5, 0)),
3404                    millisecond: Some(DateTimeFieldValue::new(37, 660_000_000)),
3405                    ..Default::default()
3406                },
3407                "5.0 seconds 37.66 milliseconds",
3408                Second,
3409            ),
3410            (
3411                ParsedDateTime {
3412                    day: Some(DateTimeFieldValue::new(14, 0)),
3413                    millisecond: Some(DateTimeFieldValue::new(60, 0)),
3414                    ..Default::default()
3415                },
3416                "14 days 60 ms",
3417                Second,
3418            ),
3419            (
3420                ParsedDateTime {
3421                    microsecond: Some(DateTimeFieldValue::new(42, 900_000_000)),
3422                    ..Default::default()
3423                },
3424                "42.9 microseconds",
3425                Second,
3426            ),
3427            (
3428                ParsedDateTime {
3429                    second: Some(DateTimeFieldValue::new(5, 0)),
3430                    microsecond: Some(DateTimeFieldValue::new(37, 660_000_000)),
3431                    ..Default::default()
3432                },
3433                "5.0 seconds 37.66 microseconds",
3434                Second,
3435            ),
3436            (
3437                ParsedDateTime {
3438                    millennium: Some(DateTimeFieldValue::new(9, 800_000_000)),
3439                    ..Default::default()
3440                },
3441                "9.8 millenniums",
3442                Second,
3443            ),
3444            (
3445                ParsedDateTime {
3446                    century: Some(DateTimeFieldValue::new(7, 600_000_000)),
3447                    ..Default::default()
3448                },
3449                "7.6 centuries",
3450                Second,
3451            ),
3452            (
3453                ParsedDateTime {
3454                    decade: Some(DateTimeFieldValue::new(5, 400_000_000)),
3455                    ..Default::default()
3456                },
3457                "5.4 decades",
3458                Second,
3459            ),
3460            (
3461                ParsedDateTime {
3462                    year: Some(DateTimeFieldValue::new(1, 200_000_000)),
3463                    decade: Some(DateTimeFieldValue::new(4, 300_000_000)),
3464                    century: Some(DateTimeFieldValue::new(5, 600_000_000)),
3465                    millennium: Some(DateTimeFieldValue::new(8, 700_000_000)),
3466                    ..Default::default()
3467                },
3468                "8.7 mils 5.6 cent 4.3 decs 1.2 y",
3469                Second,
3470            ),
3471        ];
3472
3473        for test in test_cases.iter() {
3474            let actual =
3475                ParsedDateTime::build_parsed_datetime_interval(test.1, None, test.2).unwrap();
3476            if actual != test.0 {
3477                panic!(
3478                    "In test INTERVAL '{}' {}\n actual: {:?} \n expected: {:?}",
3479                    test.1, test.2, actual, test.0
3480                );
3481            }
3482        }
3483    }
3484
3485    #[mz_ore::test]
3486    fn test_build_parsed_datetime_interval_errors() {
3487        use DateTimeField::*;
3488        let test_cases = [
3489            ("1 year 2 years", Second, "YEAR field set twice"),
3490            ("1-2 3-4", Second, "YEAR or MONTH field set twice"),
3491            ("1-2 3 year", Second, "YEAR field set twice"),
3492            ("1-2 3", Month, "MONTH field set twice"),
3493            ("1-2 3:4 5", Second, "SECOND field set twice"),
3494            ("1:2:3.4 5-6 7", Year, "YEAR field set twice"),
3495            ("-:::::1.27", Second, "have unprocessed tokens 1.270000000"),
3496            (
3497                "-1 ::.27",
3498                Second,
3499                "Cannot determine format of all parts. Add explicit time components, e.g. \
3500                INTERVAL '1 day' or INTERVAL '1' DAY",
3501            ),
3502            ("1:2:3.4.5", Second, "have unprocessed tokens .500000000"),
3503            ("1+2:3.4", Second, "Cannot determine format of all parts"),
3504            ("1x2:3.4", Second, "unknown units x"),
3505            ("0 foo", Second, "unknown units foo"),
3506            ("1-2 3:4 5 second", Second, "SECOND field set twice"),
3507            (
3508                "1-2 5 second 3:4",
3509                Second,
3510                "HOUR, MINUTE, SECOND field set twice",
3511            ),
3512            (
3513                "1 2-3 4:5",
3514                Day,
3515                "Cannot determine format of all parts. Add explicit time components, e.g. \
3516                INTERVAL '1 day' or INTERVAL '1' DAY",
3517            ),
3518            (
3519                "9223372036854775808 months",
3520                Day,
3521                "Unable to parse value 9223372036854775808 as a number: number too large to fit in target type",
3522            ),
3523            (
3524                "-9223372036854775809 months",
3525                Day,
3526                "Unable to parse value 9223372036854775809 as a number: number too large to fit in target type",
3527            ),
3528            (
3529                "9223372036854775808 seconds",
3530                Day,
3531                "Unable to parse value 9223372036854775808 as a number: number too large to fit in target type",
3532            ),
3533            (
3534                "-9223372036854775809 seconds",
3535                Day,
3536                "Unable to parse value 9223372036854775809 as a number: number too large to fit in target type",
3537            ),
3538            (
3539                "1.234 second 5 ms",
3540                Second,
3541                "Cannot set MILLISECONDS or MICROSECONDS field if SECOND field has a fraction component",
3542            ),
3543            (
3544                "1.234 second 5 us",
3545                Second,
3546                "Cannot set MILLISECONDS or MICROSECONDS field if SECOND field has a fraction component",
3547            ),
3548            (
3549                "7 ms 4.321 second",
3550                Second,
3551                "Cannot set MILLISECONDS or MICROSECONDS field if SECOND field has a fraction component",
3552            ),
3553            (
3554                "7 us 4.321 second",
3555                Second,
3556                "Cannot set MILLISECONDS or MICROSECONDS field if SECOND field has a fraction component",
3557            ),
3558        ];
3559        for test in test_cases.iter() {
3560            match ParsedDateTime::build_parsed_datetime_interval(test.0, None, test.1) {
3561                Err(e) => assert_eq!(e.to_string(), test.2),
3562                Ok(pdt) => panic!(
3563                    "Test INTERVAL '{}' {} passed when expected to fail with {}, generated ParsedDateTime {:?}",
3564                    test.0, test.1, test.2, pdt,
3565                ),
3566            }
3567        }
3568    }
3569
3570    #[mz_ore::test]
3571    fn test_split_timestamp_string() {
3572        let test_cases = [
3573            (
3574                "1969-06-01 10:10:10.410 UTC",
3575                "1969-06-01 10:10:10.410",
3576                "UTC",
3577            ),
3578            (
3579                "1969-06-01 10:10:10.410+4:00",
3580                "1969-06-01 10:10:10.410",
3581                "+4:00",
3582            ),
3583            (
3584                "1969-06-01 10:10:10.410-4:00",
3585                "1969-06-01 10:10:10.410",
3586                "-4:00",
3587            ),
3588            ("1969-06-01 10:10:10.410", "1969-06-01 10:10:10.410", ""),
3589            ("1969-06-01 10:10:10.410+4", "1969-06-01 10:10:10.410", "+4"),
3590            ("1969-06-01 10:10:10.410-4", "1969-06-01 10:10:10.410", "-4"),
3591            ("1969-06-01 10:10:10+4:00", "1969-06-01 10:10:10", "+4:00"),
3592            ("1969-06-01 10:10:10-4:00", "1969-06-01 10:10:10", "-4:00"),
3593            ("1969-06-01 10:10:10 UTC", "1969-06-01 10:10:10", "UTC"),
3594            ("1969-06-01 10:10:10", "1969-06-01 10:10:10", ""),
3595            ("1969-06-01 10:10+4:00", "1969-06-01 10:10", "+4:00"),
3596            ("1969-06-01 10:10-4:00", "1969-06-01 10:10", "-4:00"),
3597            ("1969-06-01 10:10 UTC", "1969-06-01 10:10", "UTC"),
3598            ("1969-06-01 10:10", "1969-06-01 10:10", ""),
3599            ("1969-06-01 UTC", "1969-06-01", "UTC"),
3600            ("1969-06-01 +4:00", "1969-06-01", "+4:00"),
3601            ("1969-06-01 -4:00", "1969-06-01", "-4:00"),
3602            ("1969-06-01 +4", "1969-06-01", "+4"),
3603            ("1969-06-01 -4", "1969-06-01", "-4"),
3604            ("1969-06-01", "1969-06-01", ""),
3605            ("1969-06-01 10:10:10.410Z", "1969-06-01 10:10:10.410", "Z"),
3606            ("1969-06-01 10:10:10.410z", "1969-06-01 10:10:10.410", "z"),
3607            ("1969-06-01Z", "1969-06-01", "Z"),
3608            ("1969-06-01z", "1969-06-01", "z"),
3609            ("1969-06-01 10:10:10.410   ", "1969-06-01 10:10:10.410", ""),
3610            (
3611                "1969-06-01     10:10:10.410   ",
3612                "1969-06-01     10:10:10.410",
3613                "",
3614            ),
3615            ("   1969-06-01 10:10:10.412", "1969-06-01 10:10:10.412", ""),
3616            (
3617                "   1969-06-01 10:10:10.413   ",
3618                "1969-06-01 10:10:10.413",
3619                "",
3620            ),
3621            (
3622                "1969-06-01 10:10:10.410 +4:00",
3623                "1969-06-01 10:10:10.410",
3624                "+4:00",
3625            ),
3626            (
3627                "1969-06-01 10:10:10.410+4 :00",
3628                "1969-06-01 10:10:10.410",
3629                "+4 :00",
3630            ),
3631            (
3632                "1969-06-01 10:10:10.410      +4:00",
3633                "1969-06-01 10:10:10.410",
3634                "+4:00",
3635            ),
3636            (
3637                "1969-06-01 10:10:10.410+4:00     ",
3638                "1969-06-01 10:10:10.410",
3639                "+4:00",
3640            ),
3641            (
3642                "1969-06-01 10:10:10.410  Z  ",
3643                "1969-06-01 10:10:10.410",
3644                "Z",
3645            ),
3646            ("1969-06-01    +4  ", "1969-06-01", "+4"),
3647            ("1969-06-01   Z   ", "1969-06-01", "Z"),
3648        ];
3649
3650        for test in test_cases.iter() {
3651            let (ts, tz, era) = split_timestamp_string(test.0);
3652
3653            assert_eq!(ts, test.1);
3654            assert_eq!(tz, test.2);
3655            assert_eq!(era, CalendarEra::AD);
3656        }
3657    }
3658
3659    #[mz_ore::test]
3660    fn proptest_packed_naive_time_roundtrips() {
3661        let strat = add_arb_duration(NaiveTime::from_hms_opt(0, 0, 0).unwrap());
3662        proptest!(|(time in strat)| {
3663            let packed = PackedNaiveTime::from_value(time);
3664            let rnd = packed.into_value();
3665            prop_assert_eq!(time, rnd);
3666        });
3667    }
3668
3669    #[mz_ore::test]
3670    fn proptest_packed_naive_time_sort_order() {
3671        let time = add_arb_duration(NaiveTime::from_hms_opt(0, 0, 0).unwrap());
3672        let strat = proptest::collection::vec(time, 0..128);
3673        proptest!(|(mut times in strat)| {
3674            let mut packed: Vec<_> = times.iter().copied()
3675                .map(PackedNaiveTime::from_value).collect();
3676
3677            times.sort();
3678            packed.sort();
3679
3680            for (time, packed) in times.into_iter().zip_eq(packed.into_iter()) {
3681                let rnd = packed.into_value();
3682                prop_assert_eq!(time, rnd);
3683            }
3684        });
3685    }
3686}
3687
3688#[mz_ore::test]
3689fn test_parseddatetime_add_field() {
3690    use DateTimeField::*;
3691    let pdt_unit = ParsedDateTime {
3692        millennium: Some(DateTimeFieldValue::new(8, 0)),
3693        century: Some(DateTimeFieldValue::new(9, 0)),
3694        decade: Some(DateTimeFieldValue::new(10, 0)),
3695        year: Some(DateTimeFieldValue::new(1, 0)),
3696        month: Some(DateTimeFieldValue::new(2, 0)),
3697        day: Some(DateTimeFieldValue::new(2, 0)),
3698        hour: Some(DateTimeFieldValue::new(3, 0)),
3699        minute: Some(DateTimeFieldValue::new(4, 0)),
3700        second: Some(DateTimeFieldValue::new(5, 0)),
3701        millisecond: Some(DateTimeFieldValue::new(6, 0)),
3702        microsecond: Some(DateTimeFieldValue::new(7, 0)),
3703        ..Default::default()
3704    };
3705
3706    let pdt_frac = ParsedDateTime {
3707        millennium: Some(DateTimeFieldValue::new(8, 555_555_555)),
3708        century: Some(DateTimeFieldValue::new(9, 555_555_555)),
3709        decade: Some(DateTimeFieldValue::new(10, 555_555_555)),
3710        year: Some(DateTimeFieldValue::new(1, 555_555_555)),
3711        month: Some(DateTimeFieldValue::new(2, 555_555_555)),
3712        day: Some(DateTimeFieldValue::new(2, 555_555_555)),
3713        hour: Some(DateTimeFieldValue::new(3, 555_555_555)),
3714        minute: Some(DateTimeFieldValue::new(4, 555_555_555)),
3715        second: Some(DateTimeFieldValue::new(5, 555_555_555)),
3716        millisecond: Some(DateTimeFieldValue::new(6, 555_555_555)),
3717        microsecond: Some(DateTimeFieldValue::new(7, 555_555_555)),
3718        ..Default::default()
3719    };
3720
3721    let pdt_frac_neg = ParsedDateTime {
3722        millennium: Some(DateTimeFieldValue::new(-8, -555_555_555)),
3723        century: Some(DateTimeFieldValue::new(-9, -555_555_555)),
3724        decade: Some(DateTimeFieldValue::new(-10, -555_555_555)),
3725        year: Some(DateTimeFieldValue::new(-1, -555_555_555)),
3726        month: Some(DateTimeFieldValue::new(-2, -555_555_555)),
3727        day: Some(DateTimeFieldValue::new(-2, -555_555_555)),
3728        hour: Some(DateTimeFieldValue::new(-3, -555_555_555)),
3729        minute: Some(DateTimeFieldValue::new(-4, -555_555_555)),
3730        second: Some(DateTimeFieldValue::new(-5, -555_555_555)),
3731        millisecond: Some(DateTimeFieldValue::new(-6, -555_555_555)),
3732        microsecond: Some(DateTimeFieldValue::new(-7, -555_555_555)),
3733        ..Default::default()
3734    };
3735
3736    let pdt_s_rollover = ParsedDateTime {
3737        millisecond: Some(DateTimeFieldValue::new(1002, 666_666_666)),
3738        microsecond: Some(DateTimeFieldValue::new(1000003, 777_777_777)),
3739        ..Default::default()
3740    };
3741
3742    run_test_parseddatetime_add_field(pdt_unit.clone(), Millennium, (8 * 12 * 1_000, 0, 0));
3743    run_test_parseddatetime_add_field(pdt_unit.clone(), Century, (9 * 12 * 100, 0, 0));
3744    run_test_parseddatetime_add_field(pdt_unit.clone(), Decade, (10 * 12 * 10, 0, 0));
3745    run_test_parseddatetime_add_field(pdt_unit.clone(), Year, (12, 0, 0));
3746    run_test_parseddatetime_add_field(pdt_unit.clone(), Month, (2, 0, 0));
3747    run_test_parseddatetime_add_field(pdt_unit.clone(), Day, (0, 2, 0));
3748    run_test_parseddatetime_add_field(pdt_unit.clone(), Hour, (0, 0, 3 * 60 * 60 * 1_000_000));
3749    run_test_parseddatetime_add_field(pdt_unit.clone(), Minute, (0, 0, 4 * 60 * 1_000_000));
3750    run_test_parseddatetime_add_field(pdt_unit.clone(), Second, (0, 0, 5 * 1_000_000));
3751    run_test_parseddatetime_add_field(pdt_unit.clone(), Milliseconds, (0, 0, 6 * 1_000));
3752    run_test_parseddatetime_add_field(pdt_unit, Microseconds, (0, 0, 7));
3753    run_test_parseddatetime_add_field(pdt_frac.clone(), Millennium, (102_666, 0, 0));
3754    run_test_parseddatetime_add_field(pdt_frac.clone(), Century, (11466, 0, 0));
3755    run_test_parseddatetime_add_field(pdt_frac.clone(), Decade, (1266, 0, 0));
3756    run_test_parseddatetime_add_field(pdt_frac.clone(), Year, (18, 0, 0));
3757    run_test_parseddatetime_add_field(
3758        pdt_frac.clone(),
3759        Month,
3760        (
3761            2,
3762            16,
3763            // 15:59:59.99856
3764            (15 * 60 * 60 * 1_000_000) + (59 * 60 * 1_000_000) + (59 * 1_000_000) + 998_560,
3765        ),
3766    );
3767    run_test_parseddatetime_add_field(
3768        pdt_frac.clone(),
3769        Day,
3770        (
3771            0,
3772            2,
3773            // 13:19:59.999952
3774            (13 * 60 * 60 * 1_000_000) + (19 * 60 * 1_000_000) + (59 * 1_000_000) + 999_952,
3775        ),
3776    );
3777    run_test_parseddatetime_add_field(
3778        pdt_frac.clone(),
3779        Hour,
3780        (
3781            0,
3782            0,
3783            // 03:33:19.999998
3784            (3 * 60 * 60 * 1_000_000) + (33 * 60 * 1_000_000) + (19 * 1_000_000) + 999_998,
3785        ),
3786    );
3787    run_test_parseddatetime_add_field(
3788        pdt_frac.clone(),
3789        Minute,
3790        (
3791            0,
3792            0,
3793            // 00:04:33.333333
3794            (4 * 60 * 1_000_000) + (33 * 1_000_000) + 333_333,
3795        ),
3796    );
3797    run_test_parseddatetime_add_field(
3798        pdt_frac.clone(),
3799        Second,
3800        (
3801            0,
3802            0,
3803            // 00:00:05.555556
3804            (5 * 1_000_000) + 555_556,
3805        ),
3806    );
3807    run_test_parseddatetime_add_field(
3808        pdt_frac.clone(),
3809        Milliseconds,
3810        (
3811            0, 0, // 00:00:00.006556
3812            6_556,
3813        ),
3814    );
3815    run_test_parseddatetime_add_field(
3816        pdt_frac,
3817        Microseconds,
3818        (
3819            0, 0, // 00:00:00.000008
3820            8,
3821        ),
3822    );
3823    run_test_parseddatetime_add_field(pdt_frac_neg.clone(), Year, (-18, 0, 0));
3824    run_test_parseddatetime_add_field(
3825        pdt_frac_neg.clone(),
3826        Month,
3827        (
3828            -2,
3829            -16,
3830            // -15:59:59.99856
3831            (-15 * 60 * 60 * 1_000_000) + (-59 * 60 * 1_000_000) + (-59 * 1_000_000) + -998_560,
3832        ),
3833    );
3834    run_test_parseddatetime_add_field(
3835        pdt_frac_neg.clone(),
3836        Day,
3837        (
3838            0,
3839            -2,
3840            // 13:19:59.999952
3841            (-13 * 60 * 60 * 1_000_000) + (-19 * 60 * 1_000_000) + (-59 * 1_000_000) + -999_952,
3842        ),
3843    );
3844    run_test_parseddatetime_add_field(
3845        pdt_frac_neg.clone(),
3846        Hour,
3847        (
3848            0,
3849            0,
3850            // -03:33:19.999998
3851            (-3 * 60 * 60 * 1_000_000) + (-33 * 60 * 1_000_000) + (-19 * 1_000_000) + -999_998,
3852        ),
3853    );
3854    run_test_parseddatetime_add_field(
3855        pdt_frac_neg.clone(),
3856        Minute,
3857        (
3858            0,
3859            0,
3860            // -00:04:33.333333
3861            (-4 * 60 * 1_000_000) + (-33 * 1_000_000) + -333_333,
3862        ),
3863    );
3864    run_test_parseddatetime_add_field(
3865        pdt_frac_neg.clone(),
3866        Second,
3867        (
3868            0,
3869            0,
3870            // -00:00:05.555556
3871            (-5 * 1_000_000) + -555_556,
3872        ),
3873    );
3874    run_test_parseddatetime_add_field(
3875        pdt_frac_neg.clone(),
3876        Milliseconds,
3877        (
3878            0, 0, // -00:00:00.006556
3879            -6_556,
3880        ),
3881    );
3882    run_test_parseddatetime_add_field(
3883        pdt_frac_neg,
3884        Microseconds,
3885        (
3886            0, 0, // -00:00:00.000008
3887            -8,
3888        ),
3889    );
3890    run_test_parseddatetime_add_field(
3891        pdt_s_rollover.clone(),
3892        Milliseconds,
3893        (
3894            0,
3895            0, // 00:00:01.002667
3896            (1 * 1_000_000) + 2_667,
3897        ),
3898    );
3899    run_test_parseddatetime_add_field(
3900        pdt_s_rollover,
3901        Microseconds,
3902        (
3903            0,
3904            0, // 00:00:01.000004
3905            (1 * 1_000_000) + 4,
3906        ),
3907    );
3908
3909    fn run_test_parseddatetime_add_field(
3910        pdt: ParsedDateTime,
3911        f: DateTimeField,
3912        expected: (i32, i32, i64),
3913    ) {
3914        let mut res = (0, 0, 0);
3915
3916        pdt.add_field(f, &mut res.0, &mut res.1, &mut res.2)
3917            .unwrap();
3918
3919        if res.0 != expected.0 || res.1 != expected.1 || res.2 != expected.2 {
3920            panic!(
3921                "test_parseddatetime_add_field failed \n actual: {:?} \n expected: {:?}",
3922                res, expected
3923            );
3924        }
3925    }
3926}
3927
3928#[mz_ore::test]
3929fn test_parseddatetime_compute_interval() {
3930    run_test_parseddatetime_compute_interval(
3931        ParsedDateTime {
3932            year: Some(DateTimeFieldValue::new(1, 0)),
3933            month: Some(DateTimeFieldValue::new(1, 0)),
3934            ..Default::default()
3935        },
3936        Interval {
3937            months: 13,
3938            ..Default::default()
3939        },
3940    );
3941    run_test_parseddatetime_compute_interval(
3942        ParsedDateTime {
3943            year: Some(DateTimeFieldValue::new(1, 0)),
3944            month: Some(DateTimeFieldValue::new(-1, 0)),
3945            ..Default::default()
3946        },
3947        Interval {
3948            months: 11,
3949            ..Default::default()
3950        },
3951    );
3952    run_test_parseddatetime_compute_interval(
3953        ParsedDateTime {
3954            year: Some(DateTimeFieldValue::new(-1, 0)),
3955            month: Some(DateTimeFieldValue::new(1, 0)),
3956            ..Default::default()
3957        },
3958        Interval {
3959            months: -11,
3960            ..Default::default()
3961        },
3962    );
3963    run_test_parseddatetime_compute_interval(
3964        ParsedDateTime {
3965            day: Some(DateTimeFieldValue::new(1, 0)),
3966            hour: Some(DateTimeFieldValue::new(-2, 0)),
3967            minute: Some(DateTimeFieldValue::new(-3, 0)),
3968            second: Some(DateTimeFieldValue::new(-4, -500_000_000)),
3969            ..Default::default()
3970        },
3971        // 1 day -2:03:04.5
3972        Interval::new(
3973            0,
3974            1,
3975            (-2 * 60 * 60 * 1_000_000) + (-3 * 60 * 1_000_000) + (-4 * 1_000_000) + -500_000,
3976        ),
3977    );
3978    run_test_parseddatetime_compute_interval(
3979        ParsedDateTime {
3980            day: Some(DateTimeFieldValue::new(-1, 0)),
3981            hour: Some(DateTimeFieldValue::new(2, 0)),
3982            minute: Some(DateTimeFieldValue::new(3, 0)),
3983            second: Some(DateTimeFieldValue::new(4, 500_000_000)),
3984            ..Default::default()
3985        },
3986        // -1 day 02:03:04.5
3987        Interval::new(
3988            0,
3989            -1,
3990            (2 * 60 * 60 * 1_000_000) + (3 * 60 * 1_000_000) + (4 * 1_000_000) + 500_000,
3991        ),
3992    );
3993    run_test_parseddatetime_compute_interval(
3994        ParsedDateTime {
3995            day: Some(DateTimeFieldValue::new(1, 0)),
3996            second: Some(DateTimeFieldValue::new(0, -270_000_000)),
3997            ..Default::default()
3998        },
3999        // 1 day -00:00:00.27
4000        Interval::new(0, 1, -270_000),
4001    );
4002    run_test_parseddatetime_compute_interval(
4003        ParsedDateTime {
4004            day: Some(DateTimeFieldValue::new(-1, 0)),
4005            second: Some(DateTimeFieldValue::new(0, 270_000_000)),
4006            ..Default::default()
4007        },
4008        // -1 day 00:00:00.27
4009        Interval::new(0, -1, 270_000),
4010    );
4011    run_test_parseddatetime_compute_interval(
4012        ParsedDateTime {
4013            year: Some(DateTimeFieldValue::new(-1, -555_555_555)),
4014            month: Some(DateTimeFieldValue::new(2, 555_555_555)),
4015            day: Some(DateTimeFieldValue::new(-3, -555_555_555)),
4016            hour: Some(DateTimeFieldValue::new(4, 555_555_555)),
4017            minute: Some(DateTimeFieldValue::new(-5, -555_555_555)),
4018            second: Some(DateTimeFieldValue::new(6, 555_555_555)),
4019            ..Default::default()
4020        },
4021        // -1 year -4 months +13 days +07:07:53.220829
4022        Interval::new(
4023            -16,
4024            13,
4025            (7 * 60 * 60 * 1_000_000) + (7 * 60 * 1_000_000) + (53 * 1_000_000) + 220_829,
4026        ),
4027    );
4028    run_test_parseddatetime_compute_interval(
4029        ParsedDateTime {
4030            second: Some(DateTimeFieldValue::new(1, 0)),
4031            millisecond: Some(DateTimeFieldValue::new(2_003, 0)),
4032            ..Default::default()
4033        },
4034        // 00:00:03.003
4035        Interval::new(0, 0, (3 * 1_000_000) + 3_000),
4036    );
4037    run_test_parseddatetime_compute_interval(
4038        ParsedDateTime {
4039            second: Some(DateTimeFieldValue::new(1, 0)),
4040            microsecond: Some(DateTimeFieldValue::new(2_000_003, 0)),
4041            ..Default::default()
4042        },
4043        // 00:00:03.000003
4044        Interval::new(0, 0, (3 * 1_000_000) + 3),
4045    );
4046    run_test_parseddatetime_compute_interval(
4047        ParsedDateTime {
4048            millisecond: Some(DateTimeFieldValue::new(1, 200_000_000)),
4049            microsecond: Some(DateTimeFieldValue::new(3, 400_000_000)),
4050            ..Default::default()
4051        },
4052        // 00:00:00.0012034
4053        Interval::new(0, 0, 1_203),
4054    );
4055    run_test_parseddatetime_compute_interval(
4056        ParsedDateTime {
4057            millennium: Some(DateTimeFieldValue::new(1, 0)),
4058            century: Some(DateTimeFieldValue::new(2, 0)),
4059            decade: Some(DateTimeFieldValue::new(3, 0)),
4060            year: Some(DateTimeFieldValue::new(4, 0)),
4061            ..Default::default()
4062        },
4063        // 1234 years
4064        Interval::new(1234 * 12, 0, 0),
4065    );
4066
4067    fn run_test_parseddatetime_compute_interval(pdt: ParsedDateTime, expected: Interval) {
4068        let actual = pdt.compute_interval().unwrap();
4069
4070        if actual != expected {
4071            panic!(
4072                "test_interval_compute_interval failed\n input {:?}\nactual {:?}\nexpected {:?}",
4073                pdt, actual, expected
4074            )
4075        }
4076    }
4077}