Skip to main content

rupg_types/datetime/
decode.rs

1//! The text input of `date`, `time`, `timetz`, `timestamp`, `timestamptz` and `interval`.
2//!
3//! This is a port of `ParseDateTime`, `DecodeDateTime`, `DecodeTimeOnly`, `DecodeInterval` and `DecodeISO8601Interval` in `src/backend/utils/adt/datetime.c`, with the helpers that they call. The structure follows the C code closely, so that a difference against the server can be found by reading the two side by side. The field masks, the token types and the error codes have the values of `datetime.h`.
4//!
5//! The server reads some inputs from the session: the field order of `DateStyle`, the time zone, the time zone abbreviations, the zone names and the time of the transaction start, for `now` and `today`. [`DateTimeInput`] holds all of them.
6//!
7//! Lifted from `crates/rudb-pgtypes/src/datetime/decode.rs` of tamnd/rudb at f5f7065a (spec/04 section 4.9).
8
9use std::sync::{Arc, OnceLock};
10
11use rupg_common::SqlState;
12
13use super::{
14    AbbrevMeaning, DATE_END_JULIAN, DATE_INFINITY, DATE_NEGATIVE_INFINITY, DateOrder,
15    END_TIMESTAMP, Interval, IntervalStyle, MIN_TIMESTAMP, POSTGRES_EPOCH_JDATE,
16    TIMESTAMP_INFINITY, TIMESTAMP_NEGATIVE_INFINITY, TimeZone, UNIX_EPOCH_JDATE, USECS_PER_DAY,
17    USECS_PER_SEC, adjust_interval, adjust_time, adjust_timestamp, date2j, j2date, out_of_range,
18};
19use crate::error::TypeError;
20use crate::float::strtod;
21use crate::number::is_space;
22use crate::typmod::INTERVAL_FULL_RANGE;
23
24/// The zones that a name in the input can give, as `pg_tzset` finds them. The name is in lower case, as the input has it.
25pub trait ZoneLookup {
26    fn zone(&self, name: &str) -> Option<Arc<dyn TimeZone + Send + Sync>>;
27}
28
29/// A [`ZoneLookup`] that knows no zone, until the tz database is here.
30#[derive(Debug, Clone, Copy, Default)]
31pub struct NoZones;
32
33impl ZoneLookup for NoZones {
34    fn zone(&self, _: &str) -> Option<Arc<dyn TimeZone + Send + Sync>> {
35        None
36    }
37}
38
39/// One line of a `timezone_abbreviations` file.
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub enum Abbrev {
42    /// A fixed offset east of UTC in seconds, and whether it is daylight saving time.
43    Fixed { offset: i32, dst: bool },
44    /// The name of a zone, for an abbreviation whose offset changed over time.
45    Zone(String),
46}
47
48/// The time zone abbreviations of the `timezone_abbreviations` setting, sorted by the lower case abbreviation.
49#[derive(Debug, Clone, Default, PartialEq, Eq)]
50pub struct ZoneAbbrevs {
51    entries: Vec<(String, Abbrev)>,
52}
53
54impl ZoneAbbrevs {
55    /// Reads a file in the format of `src/timezone/tznames`, as `ParseTzFile` does. The `@INCLUDE` and `@OVERRIDE` lines are not supported.
56    pub fn parse(text: &str) -> Result<ZoneAbbrevs, String> {
57        let mut entries = Vec::new();
58        for (index, line) in text.lines().enumerate() {
59            let lineno = index + 1;
60            let line = line.trim_start_matches(|c: char| c.is_ascii() && is_space(c as u8));
61            if line.is_empty() || line.starts_with('#') {
62                continue;
63            }
64            if line.starts_with('@') {
65                return Err(format!("line {lineno}: {line} is not supported"));
66            }
67            let mut words = line.split([' ', '\t', '\n', '\r']).filter(|w| !w.is_empty());
68            let abbrev = words.next().unwrap_or_default();
69            let Some(offset) = words.next() else {
70                return Err(format!("missing time zone offset in line {lineno}"));
71            };
72            let (abbrev_value, rest) = if offset.starts_with(|c: char| c.is_ascii_digit())
73                || offset.starts_with(['+', '-'])
74            {
75                let Ok(offset) = offset.parse::<i32>() else {
76                    return Err(format!("invalid number for time zone offset in line {lineno}"));
77                };
78                if !(-14 * 3600..=14 * 3600).contains(&offset) {
79                    return Err(format!(
80                        "time zone offset {offset} is out of range in line {lineno}"
81                    ));
82                }
83                match words.next() {
84                    Some(word) if word.eq_ignore_ascii_case("D") => {
85                        (Abbrev::Fixed { offset, dst: true }, words.next())
86                    }
87                    word => (Abbrev::Fixed { offset, dst: false }, word),
88                }
89            } else {
90                (Abbrev::Zone(offset.to_string()), words.next())
91            };
92            if rest.is_some_and(|word| !word.starts_with('#')) {
93                return Err(format!("invalid syntax in line {lineno}"));
94            }
95            if abbrev.len() > TOKMAXLEN {
96                return Err(format!(
97                    "time zone abbreviation \"{abbrev}\" is too long (maximum {TOKMAXLEN} characters) in line {lineno}"
98                ));
99            }
100            entries.push((abbrev.to_ascii_lowercase(), abbrev_value));
101        }
102        entries.sort_by(|a, b| a.0.cmp(&b.0));
103        for pair in entries.windows(2) {
104            if pair[0].0 == pair[1].0 && pair[0].1 != pair[1].1 {
105                return Err(format!(
106                    "time zone abbreviation \"{}\" is multiply defined",
107                    pair[0].0
108                ));
109            }
110        }
111        entries.dedup();
112        Ok(ZoneAbbrevs { entries })
113    }
114
115    /// The `Default` set of PostgreSQL, the default of `timezone_abbreviations`.
116    ///
117    /// # Panics
118    ///
119    /// Only if the vendored file does not parse, which the unit tests check.
120    pub fn postgres_default() -> &'static ZoneAbbrevs {
121        static DEFAULT: OnceLock<ZoneAbbrevs> = OnceLock::new();
122        DEFAULT.get_or_init(|| {
123            ZoneAbbrevs::parse(include_str!("../generated/tznames_default.txt"))
124                .expect("the vendored tznames file is valid")
125        })
126    }
127
128    /// The abbreviation and its meaning. Only the first 10 bytes of the token count, as in `datebsearch`.
129    pub fn get(&self, token: &str) -> Option<(&str, &Abbrev)> {
130        let key = truncate(token.as_bytes());
131        let found = self.entries.binary_search_by(|(abbrev, _)| abbrev.as_bytes().cmp(key)).ok()?;
132        let (abbrev, value) = &self.entries[found];
133        Some((abbrev, value))
134    }
135
136    /// Each abbreviation in lower case and its meaning, in the order of the abbreviations.
137    pub fn iter(&self) -> impl Iterator<Item = (&str, &Abbrev)> {
138        self.entries.iter().map(|(abbrev, value)| (abbrev.as_str(), value))
139    }
140
141    pub fn len(&self) -> usize {
142        self.entries.len()
143    }
144
145    pub fn is_empty(&self) -> bool {
146        self.entries.is_empty()
147    }
148}
149
150/// The session state that the text input of the date and time types reads.
151#[derive(Clone, Copy)]
152pub struct DateTimeInput<'a> {
153    /// The field order part of `DateStyle`.
154    pub order: DateOrder,
155    /// The `TimeZone` setting.
156    pub zone: &'a dyn TimeZone,
157    /// The zone names, such as `america/new_york`.
158    pub zones: &'a dyn ZoneLookup,
159    /// The `timezone_abbreviations` setting.
160    pub abbrevs: &'a ZoneAbbrevs,
161    /// The start of the transaction in microseconds since 2000-01-01, for `now` and `today`.
162    pub now: i64,
163}
164
165impl std::fmt::Debug for DateTimeInput<'_> {
166    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
167        f.debug_struct("DateTimeInput")
168            .field("order", &self.order)
169            .field("abbrevs", &self.abbrevs.len())
170            .field("now", &self.now)
171            .finish_non_exhaustive()
172    }
173}
174
175// The field kinds of `ParseDateTime`.
176const DTK_NUMBER: u8 = 0;
177const DTK_STRING: u8 = 1;
178const DTK_DATE: u8 = 2;
179const DTK_TIME: u8 = 3;
180const DTK_TZ: u8 = 4;
181const DTK_SPECIAL: u8 = 6;
182
183// The values of the reserved words and the units.
184const DTK_EARLY: i32 = 9;
185const DTK_LATE: i32 = 10;
186const DTK_EPOCH: i32 = 11;
187const DTK_NOW: i32 = 12;
188const DTK_YESTERDAY: i32 = 13;
189const DTK_TODAY: i32 = 14;
190const DTK_TOMORROW: i32 = 15;
191const DTK_ZULU: i32 = 16;
192const DTK_DELTA: i32 = 17;
193const DTK_SECOND: i32 = 18;
194const DTK_MINUTE: i32 = 19;
195const DTK_HOUR: i32 = 20;
196const DTK_DAY: i32 = 21;
197const DTK_WEEK: i32 = 22;
198const DTK_MONTH: i32 = 23;
199const DTK_QUARTER: i32 = 24;
200const DTK_YEAR: i32 = 25;
201const DTK_DECADE: i32 = 26;
202const DTK_CENTURY: i32 = 27;
203const DTK_MILLENNIUM: i32 = 28;
204const DTK_MILLISEC: i32 = 29;
205const DTK_MICROSEC: i32 = 30;
206const DTK_JULIAN: i32 = 31;
207const DTK_DOW: i32 = 32;
208const DTK_DOY: i32 = 33;
209const DTK_TZ_HOUR: i32 = 34;
210const DTK_TZ_MINUTE: i32 = 35;
211const DTK_ISOYEAR: i32 = 36;
212const DTK_ISODOW: i32 = 37;
213/// `DTK_DATE` and `DTK_TIME` as the values of a decoded input and of the `t` token.
214const DTK_DATE_VALUE: i32 = 2;
215const DTK_TIME_VALUE: i32 = 3;
216/// `DTK_TZ` as the value of the `timezone` unit.
217const DTK_TZ_VALUE: i32 = 4;
218
219// The token types. Each one is also the bit of the field in a mask.
220const RESERV: i32 = 0;
221const MONTH: i32 = 1;
222const YEAR: i32 = 2;
223const DAY: i32 = 3;
224const TZ: i32 = 5;
225const DTZ: i32 = 6;
226const DYNTZ: i32 = 7;
227const IGNORE_DTF: i32 = 8;
228const AMPM: i32 = 9;
229const HOUR: i32 = 10;
230const MINUTE: i32 = 11;
231const SECOND: i32 = 12;
232const MILLISECOND: i32 = 13;
233const MICROSECOND: i32 = 14;
234const DOY: i32 = 15;
235const DOW: i32 = 16;
236const UNITS: i32 = 17;
237const ADBC: i32 = 18;
238const AGO: i32 = 19;
239const ISOTIME: i32 = 23;
240const WEEK: i32 = 24;
241const DECADE: i32 = 25;
242const CENTURY: i32 = 26;
243const MILLENNIUM: i32 = 27;
244const DTZMOD: i32 = 28;
245const UNKNOWN_FIELD: i32 = 31;
246
247const fn m(t: i32) -> u32 {
248    1 << t
249}
250
251const DATE_M: u32 = m(YEAR) | m(MONTH) | m(DAY);
252const ALL_SECS_M: u32 = m(SECOND) | m(MILLISECOND) | m(MICROSECOND);
253const TIME_M: u32 = m(HOUR) | m(MINUTE) | ALL_SECS_M;
254
255const AM: i32 = 0;
256const PM: i32 = 1;
257const HR24: i32 = 2;
258const BC: i32 = 1;
259
260const MAXDATEFIELDS: usize = 25;
261const TOKMAXLEN: usize = 10;
262/// The work buffers of the input functions: `MAXDATELEN + 1`, `MAXDATELEN + MAXDATEFIELDS` and 256 for `interval`.
263const DATE_BUFLEN: usize = 129;
264const TIMESTAMP_BUFLEN: usize = 153;
265const INTERVAL_BUFLEN: usize = 256;
266
267const USECS_PER_MINUTE: i64 = 60 * USECS_PER_SEC;
268const USECS_PER_HOUR: i64 = 3600 * USECS_PER_SEC;
269const DAYS_PER_MONTH: i32 = 30;
270const MAX_TZDISP_HOUR: i32 = 15;
271
272/// The errors of the decode functions, the `DTERR` codes.
273#[derive(Debug, Clone, PartialEq, Eq)]
274enum DtErr {
275    BadFormat,
276    FieldOverflow,
277    MdFieldOverflow,
278    IntervalOverflow,
279    TzDispOverflow,
280    BadTimezone(String),
281    BadZoneAbbrev { zone: String, abbrev: String },
282}
283
284use DtErr::{BadFormat, FieldOverflow};
285
286impl DtErr {
287    /// `DateTimeParseError`.
288    fn into_error(self, input: &str, type_name: &str) -> TypeError {
289        let overflow = || {
290            TypeError::new(
291                SqlState::DATETIME_FIELD_OVERFLOW,
292                format!("date/time field value out of range: \"{input}\""),
293            )
294        };
295        match self {
296            BadFormat => TypeError::new(
297                SqlState::INVALID_DATETIME_FORMAT,
298                format!("invalid input syntax for type {type_name}: \"{input}\""),
299            ),
300            FieldOverflow => overflow(),
301            DtErr::MdFieldOverflow => TypeError {
302                hint: Some("Perhaps you need a different \"DateStyle\" setting.".to_string()),
303                ..overflow()
304            },
305            DtErr::IntervalOverflow => TypeError::new(
306                SqlState::INTERVAL_FIELD_OVERFLOW,
307                format!("interval field value out of range: \"{input}\""),
308            ),
309            DtErr::TzDispOverflow => TypeError::new(
310                SqlState::INVALID_TIME_ZONE_DISPLACEMENT_VALUE,
311                format!("time zone displacement out of range: \"{input}\""),
312            ),
313            DtErr::BadTimezone(zone) => TypeError::new(
314                SqlState::INVALID_PARAMETER_VALUE,
315                format!("time zone \"{zone}\" not recognized"),
316            ),
317            DtErr::BadZoneAbbrev { zone, abbrev } => TypeError {
318                detail: Some(format!(
319                    "This time zone name appears in the configuration file for time zone abbreviation \"{abbrev}\"."
320                )),
321                ..TypeError::new(
322                    SqlState::CONFIG_FILE_ERROR,
323                    format!("time zone \"{zone}\" not recognized"),
324                )
325            },
326        }
327    }
328}
329
330/// The first `TOKMAXLEN` bytes of a token, which is all that the token tables compare.
331fn truncate(key: &[u8]) -> &[u8] {
332    &key[..key.len().min(TOKMAXLEN)]
333}
334
335/// A row of `datetktbl`.
336fn date_token(key: &[u8]) -> Option<(i32, i32)> {
337    Some(match truncate(key) {
338        b"+infinity" | b"infinity" => (RESERV, DTK_LATE),
339        b"-infinity" => (RESERV, DTK_EARLY),
340        b"ad" => (ADBC, 0),
341        b"allballs" => (RESERV, DTK_ZULU),
342        b"am" => (AMPM, AM),
343        b"apr" | b"april" => (MONTH, 4),
344        b"at" | b"on" => (IGNORE_DTF, 0),
345        b"aug" | b"august" => (MONTH, 8),
346        b"bc" => (ADBC, BC),
347        b"d" => (UNITS, DTK_DAY),
348        b"dec" | b"december" => (MONTH, 12),
349        b"dow" => (UNITS, DTK_DOW),
350        b"doy" => (UNITS, DTK_DOY),
351        b"dst" => (DTZMOD, 3600),
352        b"epoch" => (RESERV, DTK_EPOCH),
353        b"feb" | b"february" => (MONTH, 2),
354        b"fri" | b"friday" => (DOW, 5),
355        b"h" => (UNITS, DTK_HOUR),
356        b"isodow" => (UNITS, DTK_ISODOW),
357        b"isoyear" => (UNITS, DTK_ISOYEAR),
358        b"j" | b"jd" | b"julian" => (UNITS, DTK_JULIAN),
359        b"jan" | b"january" => (MONTH, 1),
360        b"jul" | b"july" => (MONTH, 7),
361        b"jun" | b"june" => (MONTH, 6),
362        b"m" => (UNITS, DTK_MONTH),
363        b"mar" | b"march" => (MONTH, 3),
364        b"may" => (MONTH, 5),
365        b"mm" => (UNITS, DTK_MINUTE),
366        b"mon" | b"monday" => (DOW, 1),
367        b"nov" | b"november" => (MONTH, 11),
368        b"now" => (RESERV, DTK_NOW),
369        b"oct" | b"october" => (MONTH, 10),
370        b"pm" => (AMPM, PM),
371        b"s" => (UNITS, DTK_SECOND),
372        b"sat" | b"saturday" => (DOW, 6),
373        b"sep" | b"sept" | b"september" => (MONTH, 9),
374        b"sun" | b"sunday" => (DOW, 0),
375        b"t" => (ISOTIME, DTK_TIME_VALUE),
376        b"thu" | b"thur" | b"thurs" | b"thursday" => (DOW, 4),
377        b"today" => (RESERV, DTK_TODAY),
378        b"tomorrow" => (RESERV, DTK_TOMORROW),
379        b"tue" | b"tues" | b"tuesday" => (DOW, 2),
380        b"wed" | b"wednesday" | b"weds" => (DOW, 3),
381        b"y" => (UNITS, DTK_YEAR),
382        b"yesterday" => (RESERV, DTK_YESTERDAY),
383        _ => return None,
384    })
385}
386
387/// A row of `deltatktbl`.
388fn delta_token(key: &[u8]) -> Option<(i32, i32)> {
389    let unit = match truncate(key) {
390        b"@" => return Some((IGNORE_DTF, 0)),
391        b"ago" => return Some((AGO, 0)),
392        b"c" | b"cent" | b"centuries" | b"century" => DTK_CENTURY,
393        b"d" | b"day" | b"days" => DTK_DAY,
394        b"dec" | b"decade" | b"decades" | b"decs" => DTK_DECADE,
395        b"h" | b"hour" | b"hours" | b"hr" | b"hrs" => DTK_HOUR,
396        b"m" | b"min" | b"mins" | b"minute" | b"minutes" => DTK_MINUTE,
397        b"microsecon" | b"us" | b"usec" | b"usecond" | b"useconds" | b"usecs" => DTK_MICROSEC,
398        b"mil" | b"millennia" | b"millennium" | b"mils" => DTK_MILLENNIUM,
399        b"millisecon" | b"ms" | b"msec" | b"msecond" | b"mseconds" | b"msecs" => DTK_MILLISEC,
400        b"mon" | b"mons" | b"month" | b"months" => DTK_MONTH,
401        b"qtr" | b"quarter" => DTK_QUARTER,
402        b"s" | b"sec" | b"second" | b"seconds" | b"secs" => DTK_SECOND,
403        b"timezone" => DTK_TZ_VALUE,
404        b"timezone_h" => DTK_TZ_HOUR,
405        b"timezone_m" => DTK_TZ_MINUTE,
406        b"w" | b"week" | b"weeks" => DTK_WEEK,
407        b"y" | b"year" | b"years" | b"yr" | b"yrs" => DTK_YEAR,
408        _ => return None,
409    };
410    Some((UNITS, unit))
411}
412
413/// A unit of `EXTRACT`, `date_part` and `date_trunc`.
414#[derive(Debug, Clone, Copy, PartialEq, Eq)]
415pub enum DateUnit {
416    Microsecond,
417    Millisecond,
418    Second,
419    Minute,
420    Hour,
421    Day,
422    Week,
423    Month,
424    Quarter,
425    Year,
426    Decade,
427    Century,
428    Millennium,
429    Julian,
430    IsoYear,
431    Dow,
432    IsoDow,
433    Doy,
434    Timezone,
435    TimezoneHour,
436    TimezoneMinute,
437    /// The reserved word `epoch`.
438    Epoch,
439    /// Another reserved word, for example `now`.
440    Reserved,
441    /// A token of another type, or no token.
442    Unknown,
443}
444
445impl DateUnit {
446    /// `DecodeUnits`, and then `DecodeSpecial` when `DecodeUnits` finds no token. `lowunits` is in lower case.
447    pub fn decode(lowunits: &str) -> DateUnit {
448        let key = lowunits.as_bytes();
449        DateUnit::of(delta_token(key).or_else(|| date_token(key)))
450    }
451
452    /// `DecodeUnits` alone, as `date_trunc` calls it. `lowunits` is in lower case.
453    pub fn decode_units(lowunits: &str) -> DateUnit {
454        DateUnit::of(delta_token(lowunits.as_bytes()))
455    }
456
457    fn of(token: Option<(i32, i32)>) -> DateUnit {
458        match token {
459            Some((UNITS, unit)) => match unit {
460                DTK_MICROSEC => DateUnit::Microsecond,
461                DTK_MILLISEC => DateUnit::Millisecond,
462                DTK_SECOND => DateUnit::Second,
463                DTK_MINUTE => DateUnit::Minute,
464                DTK_HOUR => DateUnit::Hour,
465                DTK_DAY => DateUnit::Day,
466                DTK_WEEK => DateUnit::Week,
467                DTK_MONTH => DateUnit::Month,
468                DTK_QUARTER => DateUnit::Quarter,
469                DTK_YEAR => DateUnit::Year,
470                DTK_DECADE => DateUnit::Decade,
471                DTK_CENTURY => DateUnit::Century,
472                DTK_MILLENNIUM => DateUnit::Millennium,
473                DTK_JULIAN => DateUnit::Julian,
474                DTK_ISOYEAR => DateUnit::IsoYear,
475                DTK_DOW => DateUnit::Dow,
476                DTK_ISODOW => DateUnit::IsoDow,
477                DTK_DOY => DateUnit::Doy,
478                DTK_TZ_VALUE => DateUnit::Timezone,
479                DTK_TZ_HOUR => DateUnit::TimezoneHour,
480                DTK_TZ_MINUTE => DateUnit::TimezoneMinute,
481                _ => DateUnit::Unknown,
482            },
483            Some((RESERV, DTK_EPOCH)) => DateUnit::Epoch,
484            Some((RESERV, _)) => DateUnit::Reserved,
485            _ => DateUnit::Unknown,
486        }
487    }
488
489    /// Whether the token has the type `UNITS`.
490    pub fn is_units(self) -> bool {
491        !matches!(self, DateUnit::Epoch | DateUnit::Reserved | DateUnit::Unknown)
492    }
493}
494
495/// The byte at `i`, or 0 at the end, as a C string reads.
496fn at(s: &[u8], i: usize) -> u8 {
497    s.get(i).copied().unwrap_or(0)
498}
499
500/// A field as text. The fields hold only ASCII, so this does not fail.
501fn text(field: &[u8]) -> &str {
502    std::str::from_utf8(field).unwrap_or_default()
503}
504
505/// The input up to the first NUL, which ends a C string.
506fn c_string(input: &str) -> &[u8] {
507    let bytes = input.as_bytes();
508    &bytes[..bytes.iter().position(|&b| b == 0).unwrap_or(bytes.len())]
509}
510
511/// `strtol` in base 10 from `start`: the value, the end, and whether the value saturated. With no digits the end is `start`.
512fn strtol(s: &[u8], start: usize) -> (i64, usize, bool) {
513    let mut i = start;
514    while is_space(at(s, i)) {
515        i += 1;
516    }
517    let negative = at(s, i) == b'-';
518    if matches!(at(s, i), b'-' | b'+') {
519        i += 1;
520    }
521    let digits = i;
522    let (mut value, mut range) = (0i64, false);
523    while at(s, i).is_ascii_digit() {
524        let digit = i64::from(at(s, i) - b'0');
525        if !range {
526            let next = value
527                .checked_mul(10)
528                .and_then(|v| if negative { v.checked_sub(digit) } else { v.checked_add(digit) });
529            match next {
530                Some(next) => value = next,
531                None => {
532                    range = true;
533                    value = if negative { i64::MIN } else { i64::MAX };
534                }
535            }
536        }
537        i += 1;
538    }
539    if i == digits {
540        return (0, start, false);
541    }
542    (value, i, range)
543}
544
545/// `strtoint`: `strtol` with `ERANGE` when the value does not fit in an `int`.
546fn strtoint(s: &[u8], start: usize) -> (i32, usize, bool) {
547    let (value, end, range) = strtol(s, start);
548    (value as i32, end, range || i64::from(value as i32) != value)
549}
550
551/// `atoi`, which is `strtol` cut to an `int`.
552fn atoi(s: &[u8]) -> i32 {
553    strtol(s, 0).0 as i32
554}
555
556/// `ParseFraction`: the field from the decimal point to the end, as a number from 0 to 1.
557fn parse_fraction(s: &[u8]) -> Result<f64, DtErr> {
558    if s.len() == 1 {
559        return Ok(0.0);
560    }
561    if !s[1..].iter().all(u8::is_ascii_digit) {
562        return Err(BadFormat);
563    }
564    match strtod::<f64>(s) {
565        Some((value, end, false)) if end == s.len() => Ok(value),
566        _ => Err(BadFormat),
567    }
568}
569
570/// `ParseFractionalSecond`: the fraction in microseconds.
571fn parse_fractional_second(s: &[u8]) -> Result<i32, DtErr> {
572    Ok((parse_fraction(s)? * 1e6).round_ties_even() as i32)
573}
574
575/// The fields of `ParseDateTime` in one buffer, with the bounds and the kind of each field.
576struct Parsed {
577    buf: [u8; INTERVAL_BUFLEN],
578    bounds: [(u16, u16); MAXDATEFIELDS],
579    kinds: [u8; MAXDATEFIELDS],
580    n: usize,
581}
582
583impl Parsed {
584    fn field(&self, i: usize) -> &[u8] {
585        let (start, end) = self.bounds[i];
586        &self.buf[start as usize..end as usize]
587    }
588}
589
590/// `ParseDateTime`: splits the input into fields, in lower case, and gives each one a kind. `buflen` is the size of the work buffer of the caller, which limits the length of the input.
591fn parse_date_time(s: &[u8], buflen: usize) -> Result<Parsed, DtErr> {
592    let mut out = Parsed {
593        buf: [0; INTERVAL_BUFLEN],
594        bounds: [(0, 0); MAXDATEFIELDS],
595        kinds: [0; MAXDATEFIELDS],
596        n: 0,
597    };
598    let mut pos = 0;
599    let mut cp = 0;
600    macro_rules! push {
601        ($c:expr) => {{
602            if pos + 1 >= buflen {
603                return Err(BadFormat);
604            }
605            out.buf[pos] = $c;
606            pos += 1;
607        }};
608    }
609    while cp < s.len() {
610        let c = s[cp];
611        if is_space(c) {
612            cp += 1;
613            continue;
614        }
615        if out.n >= MAXDATEFIELDS {
616            return Err(BadFormat);
617        }
618        let start = pos;
619        let kind;
620        if c.is_ascii_digit() {
621            push!(c);
622            cp += 1;
623            while at(s, cp).is_ascii_digit() {
624                push!(s[cp]);
625                cp += 1;
626            }
627            if at(s, cp) == b':' {
628                kind = DTK_TIME;
629                push!(b':');
630                cp += 1;
631                while matches!(at(s, cp), b'0'..=b'9' | b':' | b'.') {
632                    push!(s[cp]);
633                    cp += 1;
634                }
635            } else if matches!(at(s, cp), b'-' | b'/' | b'.') {
636                let delim = s[cp];
637                push!(delim);
638                cp += 1;
639                if at(s, cp).is_ascii_digit() {
640                    let mut k = if delim == b'.' { DTK_NUMBER } else { DTK_DATE };
641                    while at(s, cp).is_ascii_digit() {
642                        push!(s[cp]);
643                        cp += 1;
644                    }
645                    if at(s, cp) == delim {
646                        k = DTK_DATE;
647                        push!(delim);
648                        cp += 1;
649                        while at(s, cp).is_ascii_digit() || at(s, cp) == delim {
650                            push!(s[cp]);
651                            cp += 1;
652                        }
653                    }
654                    kind = k;
655                } else {
656                    kind = DTK_DATE;
657                    while at(s, cp).is_ascii_alphanumeric() || at(s, cp) == delim {
658                        push!(s[cp].to_ascii_lowercase());
659                        cp += 1;
660                    }
661                }
662            } else {
663                kind = DTK_NUMBER;
664            }
665        } else if c == b'.' {
666            push!(c);
667            cp += 1;
668            while at(s, cp).is_ascii_digit() {
669                push!(s[cp]);
670                cp += 1;
671            }
672            kind = DTK_NUMBER;
673        } else if c.is_ascii_alphabetic() {
674            push!(c.to_ascii_lowercase());
675            cp += 1;
676            while at(s, cp).is_ascii_alphabetic() {
677                push!(s[cp].to_ascii_lowercase());
678                cp += 1;
679            }
680            // A date such as `jan-01-2001`, or a zone name such as `america/new_york` or `etc/gmt+8`, unless the word is a keyword before a digit or a sign.
681            let next = at(s, cp);
682            let is_date = matches!(next, b'-' | b'/' | b'.')
683                || ((next == b'+' || next.is_ascii_digit())
684                    && date_token(&out.buf[start..pos]).is_none());
685            if is_date {
686                kind = DTK_DATE;
687                loop {
688                    push!(s[cp].to_ascii_lowercase());
689                    cp += 1;
690                    let next = at(s, cp);
691                    if !(matches!(next, b'+' | b'-' | b'/' | b'_' | b'.' | b':')
692                        || next.is_ascii_alphanumeric())
693                    {
694                        break;
695                    }
696                }
697            } else {
698                kind = DTK_STRING;
699            }
700        } else if c == b'+' || c == b'-' {
701            push!(c);
702            cp += 1;
703            while is_space(at(s, cp)) {
704                cp += 1;
705            }
706            if at(s, cp).is_ascii_digit() {
707                kind = DTK_TZ;
708                push!(s[cp]);
709                cp += 1;
710                while matches!(at(s, cp), b'0'..=b'9' | b':' | b'.' | b'-') {
711                    push!(s[cp]);
712                    cp += 1;
713                }
714            } else if at(s, cp).is_ascii_alphabetic() {
715                kind = DTK_SPECIAL;
716                push!(s[cp].to_ascii_lowercase());
717                cp += 1;
718                while at(s, cp).is_ascii_alphabetic() {
719                    push!(s[cp].to_ascii_lowercase());
720                    cp += 1;
721                }
722            } else {
723                return Err(BadFormat);
724            }
725        } else if c.is_ascii_punctuation() {
726            cp += 1;
727            continue;
728        } else {
729            return Err(BadFormat);
730        }
731        out.bounds[out.n] = (start as u16, pos as u16);
732        out.kinds[out.n] = kind;
733        out.n += 1;
734        // The NUL after the field, which the C code writes without a check.
735        pos += 1;
736    }
737    Ok(out)
738}
739
740/// `struct pg_tm`, with only the fields that the input uses.
741#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
742struct Tm {
743    year: i32,
744    mon: i32,
745    mday: i32,
746    hour: i32,
747    min: i32,
748    sec: i32,
749    yday: i32,
750}
751
752/// The result of `DecodeDateTime`: the kind of value, the fields, the microseconds and the zone in seconds west of UTC.
753struct Decoded {
754    dtype: i32,
755    tm: Tm,
756    fsec: i32,
757    tz: i32,
758}
759
760/// A zone that the input named: the session zone or a zone from [`ZoneLookup`].
761enum ZoneRef {
762    Session,
763    Other(Arc<dyn TimeZone + Send + Sync>),
764}
765
766impl ZoneRef {
767    fn get<'z>(&'z self, session: &'z dyn TimeZone) -> &'z dyn TimeZone {
768        match self {
769            ZoneRef::Session => session,
770            ZoneRef::Other(zone) => &**zone,
771        }
772    }
773}
774
775fn is_leap(year: i32) -> bool {
776    year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
777}
778
779fn days_in_month(year: i32, month: i32) -> i32 {
780    const DAYS: [i32; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
781    DAYS[month as usize - 1] + i32::from(month == 2 && is_leap(year))
782}
783
784/// `IS_VALID_JULIAN`: the date is in the range of the Julian day functions, to the month.
785fn is_valid_julian(year: i32, month: i32) -> bool {
786    (year > -4713 || (year == -4713 && month >= 11))
787        && (year < 5874898 || (year == 5874898 && month < 6))
788}
789
790/// `j2date` into the fields.
791fn set_date(tm: &mut Tm, julian: i32) {
792    let (year, month, day) = j2date(julian);
793    (tm.year, tm.mon, tm.mday) = (year, month as i32, day as i32);
794}
795
796/// `dt2time` for a time of day in microseconds.
797fn set_time(tm: &mut Tm, fsec: &mut i32, time: i64) {
798    tm.hour = (time / USECS_PER_HOUR) as i32;
799    let time = time - i64::from(tm.hour) * USECS_PER_HOUR;
800    tm.min = (time / USECS_PER_MINUTE) as i32;
801    let time = time - i64::from(tm.min) * USECS_PER_MINUTE;
802    tm.sec = (time / USECS_PER_SEC) as i32;
803    *fsec = (time - i64::from(tm.sec) * USECS_PER_SEC) as i32;
804}
805
806/// `time_overflows`: the time is not in 00:00:00 to 24:00:00.
807fn time_overflows(hour: i32, min: i32, sec: i32, fsec: i32) -> bool {
808    if !(0..=24).contains(&hour)
809        || !(0..60).contains(&min)
810        || !(0..=60).contains(&sec)
811        || !(0..=1_000_000).contains(&fsec)
812    {
813        return true;
814    }
815    ((i64::from(hour) * 60 + i64::from(min)) * 60 + i64::from(sec)) * USECS_PER_SEC
816        + i64::from(fsec)
817        > USECS_PER_DAY
818}
819
820/// `GetCurrentTimeUsec`: the local fields of the transaction start in the session zone, the microseconds, and the zone in seconds west of UTC.
821fn current_time(cx: &DateTimeInput<'_>) -> (Tm, i32, i32) {
822    let unix = cx.now.div_euclid(USECS_PER_SEC) + 946_684_800;
823    let fsec = cx.now.rem_euclid(USECS_PER_SEC) as i32;
824    let offset = cx.zone.at(unix).0;
825    let local = unix + i64::from(offset);
826    let mut tm = Tm::default();
827    set_date(&mut tm, (local.div_euclid(86400) + i64::from(UNIX_EPOCH_JDATE)) as i32);
828    let seconds = local.rem_euclid(86400) as i32;
829    (tm.hour, tm.min, tm.sec) = (seconds / 3600, seconds / 60 % 60, seconds % 60);
830    (tm, fsec, -offset)
831}
832
833/// `DetermineTimeZoneOffsetInternal`: the offset west of UTC of a local time in a zone, and the instant in seconds since 1970-01-01 UTC.
834fn determine_offset(tm: &Tm, zone: &dyn TimeZone) -> (i32, i64) {
835    if !is_valid_julian(tm.year, tm.mon) {
836        return (0, 0);
837    }
838    let day = i64::from(date2j(tm.year, tm.mon, tm.mday) - UNIX_EPOCH_JDATE) * 86400;
839    local_offset(day + i64::from(tm.sec) + (i64::from(tm.min) + i64::from(tm.hour) * 60) * 60, zone)
840}
841
842/// The part of `DetermineTimeZoneOffsetInternal` after the fields: the offset west of UTC of a local time in seconds since 1970-01-01 in a zone, and the instant. In a gap the local time is in the offset before the change, and in an overlap it is in the offset after the change.
843pub fn local_offset(mytime: i64, zone: &dyn TimeZone) -> (i32, i64) {
844    let (before, change) = zone.next_change(mytime - 86400);
845    let before_time = mytime - i64::from(before);
846    let Some((boundary, after)) = change else {
847        return (-before, before_time);
848    };
849    let after_time = mytime - i64::from(after);
850    if before_time < boundary && after_time < boundary {
851        return (-before, before_time);
852    }
853    if before_time > boundary && after_time >= boundary {
854        return (-after, after_time);
855    }
856    if before_time > after_time { (-before, before_time) } else { (-after, after_time) }
857}
858
859/// `DetermineTimeZoneAbbrevOffset`: the offset west of UTC of a local time with an abbreviation whose meaning changed over time.
860fn determine_abbrev_offset(tm: &Tm, abbrev: &[u8], zone: &dyn TimeZone) -> i32 {
861    let (offset, instant) = determine_offset(tm, zone);
862    match zone.abbrev_at(&text(abbrev).to_ascii_uppercase(), instant) {
863        Some((east, _)) => -east,
864        None => offset,
865    }
866}
867
868/// `DecodeTimezoneAbbrev`: the token type, the offset east of UTC, and the zone of a dynamic abbreviation.
869fn decode_timezone_abbrev(
870    field: &[u8],
871    cx: &DateTimeInput<'_>,
872) -> Result<(i32, i32, Option<ZoneRef>), DtErr> {
873    let token = text(field);
874    match cx.zone.abbrev_meaning(&token.to_ascii_uppercase()) {
875        Some(AbbrevMeaning::Fixed { offset, dst }) => {
876            return Ok((if dst { DTZ } else { TZ }, offset, None));
877        }
878        Some(AbbrevMeaning::Varies) => return Ok((DYNTZ, 0, Some(ZoneRef::Session))),
879        None => {}
880    }
881    match cx.abbrevs.get(token) {
882        None => Ok((UNKNOWN_FIELD, 0, None)),
883        Some((_, Abbrev::Fixed { offset, dst })) => {
884            Ok((if *dst { DTZ } else { TZ }, *offset, None))
885        }
886        Some((abbrev, Abbrev::Zone(name))) => match cx.zones.zone(name) {
887            Some(zone) => Ok((DYNTZ, 0, Some(ZoneRef::Other(zone)))),
888            None => Err(DtErr::BadZoneAbbrev { zone: name.clone(), abbrev: abbrev.to_string() }),
889        },
890    }
891}
892
893/// What `DecodeTimezoneName` makes of the name of a zone.
894pub enum ZoneName {
895    /// An abbreviation with one offset, in seconds east of UTC.
896    Fixed(i32),
897    /// An abbreviation whose offset changed over time, and its zone. `None` is the zone of the session.
898    Dynamic(Option<Arc<dyn TimeZone + Send + Sync>>),
899    /// A zone of the data.
900    Zone(Arc<dyn TimeZone + Send + Sync>),
901}
902
903impl std::fmt::Debug for ZoneName {
904    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
905        match self {
906            ZoneName::Fixed(offset) => f.debug_tuple("Fixed").field(offset).finish(),
907            ZoneName::Dynamic(zone) => f.debug_tuple("Dynamic").field(&zone.is_some()).finish(),
908            ZoneName::Zone(_) => f.write_str("Zone"),
909        }
910    }
911}
912
913/// `DecodeTimezoneAbbrevPrefix`: the longest abbreviation at the start of the text, in up to 10 letters of any case, as `DecodeTimezoneAbbrev` finds it, and its length in bytes. A dynamic abbreviation whose zone is not in the data does not match.
914pub fn decode_timezone_abbrev_prefix(
915    text: &str,
916    cx: &DateTimeInput<'_>,
917) -> Option<(usize, ZoneName)> {
918    let low = text.bytes().take(TOKMAXLEN).take_while(u8::is_ascii_alphabetic).collect::<Vec<_>>();
919    let low = low.to_ascii_lowercase();
920    (1..=low.len()).rev().find_map(|len| match decode_timezone_abbrev(&low[..len], cx) {
921        Ok((TZ | DTZ, offset, _)) => Some((len, ZoneName::Fixed(offset))),
922        Ok((DYNTZ, _, Some(ZoneRef::Other(zone)))) => Some((len, ZoneName::Dynamic(Some(zone)))),
923        Ok((DYNTZ, _, _)) => Some((len, ZoneName::Dynamic(None))),
924        _ => None,
925    })
926}
927
928/// `DecodeTimezoneName`: the abbreviations first, as `DecodeTimezoneAbbrev` finds them, and then the zones of the data. The abbreviations use the first 63 bytes of the name in lower case.
929///
930/// # Errors
931///
932/// `22023` when the name is not a zone, and `F0000` when the zone of an abbreviation is not in the data.
933pub fn decode_timezone_name(name: &str, cx: &DateTimeInput<'_>) -> Result<ZoneName, TypeError> {
934    let mut end = name.len().min(63);
935    while !name.is_char_boundary(end) {
936        end -= 1;
937    }
938    let low = name[..end].to_ascii_lowercase();
939    let (ty, offset, zone) =
940        decode_timezone_abbrev(low.as_bytes(), cx).map_err(|e| e.into_error(name, "text"))?;
941    match (ty, zone) {
942        (TZ | DTZ, _) => Ok(ZoneName::Fixed(offset)),
943        (DYNTZ, Some(ZoneRef::Other(zone))) => Ok(ZoneName::Dynamic(Some(zone))),
944        (DYNTZ, _) => Ok(ZoneName::Dynamic(None)),
945        _ => cx.zones.zone(name).map(ZoneName::Zone).ok_or_else(|| {
946            TypeError::new(
947                SqlState::INVALID_PARAMETER_VALUE,
948                format!("time zone \"{name}\" not recognized"),
949            )
950        }),
951    }
952}
953
954/// `DecodeTimezone` as `parse_sane_timezone` uses it: the offset in seconds west of UTC of a numeric zone, or `None` for a name that is not a numeric zone.
955///
956/// # Errors
957///
958/// `22023` when the hours, the minutes or the seconds of a numeric zone are out of range.
959pub fn decode_numeric_timezone(name: &str) -> Result<Option<i32>, TypeError> {
960    match decode_timezone(name.as_bytes()) {
961        Ok(west) => Ok(Some(west)),
962        Err(BadFormat) => Ok(None),
963        Err(DtErr::TzDispOverflow) => Err(TypeError::new(
964            SqlState::INVALID_PARAMETER_VALUE,
965            format!("numeric time zone \"{name}\" out of range"),
966        )),
967        Err(_) => Err(TypeError::new(
968            SqlState::INVALID_PARAMETER_VALUE,
969            format!("time zone \"{name}\" not recognized"),
970        )),
971    }
972}
973
974/// `DecodeTimezone`: a numeric zone such as `+05`, `-0530` or `+05:30:15`, in seconds west of UTC.
975fn decode_timezone(s: &[u8]) -> Result<i32, DtErr> {
976    let sign = at(s, 0);
977    if sign != b'+' && sign != b'-' {
978        return Err(BadFormat);
979    }
980    let (mut hr, mut end, range) = strtoint(s, 1);
981    if range {
982        return Err(DtErr::TzDispOverflow);
983    }
984    let (mut min, mut sec) = (0, 0);
985    if at(s, end) == b':' {
986        let range;
987        (min, end, range) = strtoint(s, end + 1);
988        if range {
989            return Err(DtErr::TzDispOverflow);
990        }
991        if at(s, end) == b':' {
992            let range;
993            (sec, end, range) = strtoint(s, end + 1);
994            if range {
995                return Err(DtErr::TzDispOverflow);
996            }
997        }
998    } else if end == s.len() && s.len() > 3 {
999        min = hr % 100;
1000        hr /= 100;
1001    }
1002    if !(0..=MAX_TZDISP_HOUR).contains(&hr) || !(0..60).contains(&min) || !(0..60).contains(&sec) {
1003        return Err(DtErr::TzDispOverflow);
1004    }
1005    if end != s.len() {
1006        return Err(BadFormat);
1007    }
1008    let tz = (hr * 60 + min) * 60 + sec;
1009    Ok(if sign == b'-' { tz } else { -tz })
1010}
1011
1012/// `DecodeTimeCommon`: `hh:mm`, `hh:mm:ss`, `mm:ss.fff` and so on. Gives the hours, the minutes, the seconds and the microseconds.
1013fn decode_time_common(s: &[u8], range: i32) -> Result<(i64, i32, i32, i32), DtErr> {
1014    let (mut hour, end, overflow) = strtol(s, 0);
1015    if overflow {
1016        return Err(FieldOverflow);
1017    }
1018    if at(s, end) != b':' {
1019        return Err(BadFormat);
1020    }
1021    let (mut min, end, overflow) = strtoint(s, end + 1);
1022    if overflow {
1023        return Err(FieldOverflow);
1024    }
1025    let mut sec = 0;
1026    let mut fsec = 0;
1027    let minutes_seconds = (1 << MINUTE) | (1 << SECOND);
1028    if end == s.len() {
1029        if range == minutes_seconds {
1030            if i32::try_from(hour).is_err() {
1031                return Err(FieldOverflow);
1032            }
1033            (sec, min, hour) = (min, hour as i32, 0);
1034        }
1035    } else if at(s, end) == b'.' {
1036        fsec = parse_fractional_second(&s[end..])?;
1037        if i32::try_from(hour).is_err() {
1038            return Err(FieldOverflow);
1039        }
1040        (sec, min, hour) = (min, hour as i32, 0);
1041    } else if at(s, end) == b':' {
1042        let (value, end, overflow) = strtoint(s, end + 1);
1043        if overflow {
1044            return Err(FieldOverflow);
1045        }
1046        sec = value;
1047        if at(s, end) == b'.' {
1048            fsec = parse_fractional_second(&s[end..])?;
1049        } else if end != s.len() {
1050            return Err(BadFormat);
1051        }
1052    } else {
1053        return Err(BadFormat);
1054    }
1055    if hour < 0
1056        || !(0..60).contains(&min)
1057        || !(0..=60).contains(&sec)
1058        || !(0..=1_000_000).contains(&fsec)
1059    {
1060        return Err(FieldOverflow);
1061    }
1062    Ok((hour, min, sec, fsec))
1063}
1064
1065/// `DecodeTime`: a time field of a date and time. Gives the mask of the fields.
1066fn decode_time(s: &[u8], tm: &mut Tm, fsec: &mut i32) -> Result<u32, DtErr> {
1067    let (hour, min, sec, usec) = decode_time_common(s, INTERVAL_FULL_RANGE)?;
1068    let hour = i32::try_from(hour).map_err(|_| FieldOverflow)?;
1069    (tm.hour, tm.min, tm.sec, *fsec) = (hour, min, sec, usec);
1070    Ok(TIME_M)
1071}
1072
1073/// `DecodeNumberField`: a run of digits that holds a date (`yyyymmdd`, `yymmdd`) or a time (`hhmmss`, `hhmm`), with an optional fraction. Gives the mask of the fields.
1074fn decode_number_field(
1075    s: &[u8],
1076    fmask: u32,
1077    tm: &mut Tm,
1078    fsec: &mut i32,
1079    is2digits: &mut bool,
1080) -> Result<u32, DtErr> {
1081    if !s.iter().all(|&c| c.is_ascii_digit() || c == b'.') {
1082        return Err(BadFormat);
1083    }
1084    let mut s = s;
1085    if let Some(dot) = s.iter().position(|&c| c == b'.') {
1086        *fsec = parse_fractional_second(&s[dot..])?;
1087        s = &s[..dot];
1088    } else if fmask & DATE_M != DATE_M && s.len() >= 6 {
1089        let len = s.len();
1090        tm.mday = atoi(&s[len - 2..]);
1091        tm.mon = atoi(&s[len - 4..len - 2]);
1092        tm.year = atoi(&s[..len - 4]);
1093        if len - 4 == 2 {
1094            *is2digits = true;
1095        }
1096        return Ok(DATE_M);
1097    }
1098    if fmask & TIME_M != TIME_M {
1099        if s.len() == 6 {
1100            tm.sec = atoi(&s[4..]);
1101            tm.min = atoi(&s[2..4]);
1102            tm.hour = atoi(&s[..2]);
1103            return Ok(TIME_M);
1104        }
1105        if s.len() == 4 {
1106            tm.sec = 0;
1107            tm.min = atoi(&s[2..]);
1108            tm.hour = atoi(&s[..2]);
1109            return Ok(TIME_M);
1110        }
1111    }
1112    Err(BadFormat)
1113}
1114
1115/// `DecodeNumber`: one number of a date, read in the field order of `DateStyle`. Gives the mask of the fields.
1116fn decode_number(
1117    s: &[u8],
1118    have_text_month: bool,
1119    fmask: u32,
1120    tm: &mut Tm,
1121    fsec: &mut i32,
1122    is2digits: &mut bool,
1123    order: DateOrder,
1124) -> Result<u32, DtErr> {
1125    let flen = s.len();
1126    let (val, end, overflow) = strtoint(s, 0);
1127    if overflow {
1128        return Err(FieldOverflow);
1129    }
1130    if end == 0 {
1131        return Err(BadFormat);
1132    }
1133    if at(s, end) == b'.' {
1134        // More than two digits before the point is a date or a time run together.
1135        if end > 2 {
1136            return decode_number_field(s, fmask | DATE_M, tm, fsec, is2digits);
1137        }
1138        *fsec = parse_fractional_second(&s[end..])?;
1139    } else if end != flen {
1140        return Err(BadFormat);
1141    }
1142    // A day of the year after a year.
1143    if flen == 3 && fmask & DATE_M == m(YEAR) && (1..=366).contains(&val) {
1144        tm.yday = val;
1145        return Ok(m(DOY) | m(MONTH) | m(DAY));
1146    }
1147    let tmask = match fmask & DATE_M {
1148        0 => {
1149            if flen >= 3 || order == DateOrder::Ymd {
1150                tm.year = val;
1151                m(YEAR)
1152            } else if order == DateOrder::Dmy {
1153                tm.mday = val;
1154                m(DAY)
1155            } else {
1156                tm.mon = val;
1157                m(MONTH)
1158            }
1159        }
1160        mask if mask == m(YEAR) => {
1161            tm.mon = val;
1162            m(MONTH)
1163        }
1164        mask if mask == m(MONTH) => {
1165            if have_text_month && (flen >= 3 || order == DateOrder::Ymd) {
1166                tm.year = val;
1167                m(YEAR)
1168            } else {
1169                tm.mday = val;
1170                m(DAY)
1171            }
1172        }
1173        mask if mask == m(YEAR) | m(MONTH) => {
1174            if have_text_month && flen >= 3 && *is2digits {
1175                tm.mday = tm.year;
1176                tm.year = val;
1177                *is2digits = false;
1178            } else {
1179                tm.mday = val;
1180            }
1181            m(DAY)
1182        }
1183        mask if mask == m(DAY) => {
1184            tm.mon = val;
1185            m(MONTH)
1186        }
1187        mask if mask == m(MONTH) | m(DAY) => {
1188            tm.year = val;
1189            m(YEAR)
1190        }
1191        DATE_M => return decode_number_field(s, fmask, tm, fsec, is2digits),
1192        _ => return Err(BadFormat),
1193    };
1194    if tmask == m(YEAR) {
1195        *is2digits = flen <= 2;
1196    }
1197    Ok(tmask)
1198}
1199
1200/// `DecodeDate`: a date with separators, such as `2001-02-03` or `feb-03-2001`. Gives the mask of the fields.
1201fn decode_date(
1202    s: &[u8],
1203    mut fmask: u32,
1204    is2digits: &mut bool,
1205    tm: &mut Tm,
1206    order: DateOrder,
1207) -> Result<u32, DtErr> {
1208    let mut fields = [(0usize, 0usize); MAXDATEFIELDS];
1209    let mut nf = 0;
1210    let mut i = 0;
1211    while i < s.len() && nf < MAXDATEFIELDS {
1212        while i < s.len() && !s[i].is_ascii_alphanumeric() {
1213            i += 1;
1214        }
1215        if i == s.len() {
1216            return Err(BadFormat);
1217        }
1218        let start = i;
1219        if s[i].is_ascii_digit() {
1220            while at(s, i).is_ascii_digit() {
1221                i += 1;
1222            }
1223        } else {
1224            while at(s, i).is_ascii_alphabetic() {
1225                i += 1;
1226            }
1227        }
1228        fields[nf] = (start, i);
1229        nf += 1;
1230        // The C code ends the field with a NUL over the next byte, whatever it is.
1231        if i < s.len() {
1232            i += 1;
1233        }
1234    }
1235    let mut tmask = 0;
1236    let mut have_text_month = false;
1237    let mut numeric = [false; MAXDATEFIELDS];
1238    for (k, &(start, end)) in fields[..nf].iter().enumerate() {
1239        let field = &s[start..end];
1240        if !field[0].is_ascii_alphabetic() {
1241            numeric[k] = true;
1242            continue;
1243        }
1244        let (ty, val) = date_token(field).unwrap_or((UNKNOWN_FIELD, 0));
1245        if ty == IGNORE_DTF {
1246            continue;
1247        }
1248        if ty != MONTH {
1249            return Err(BadFormat);
1250        }
1251        tm.mon = val;
1252        have_text_month = true;
1253        if fmask & m(MONTH) != 0 {
1254            return Err(BadFormat);
1255        }
1256        fmask |= m(MONTH);
1257        tmask |= m(MONTH);
1258    }
1259    let mut fsec = 0;
1260    for (k, &(start, end)) in fields[..nf].iter().enumerate() {
1261        if !numeric[k] {
1262            continue;
1263        }
1264        let dmask =
1265            decode_number(&s[start..end], have_text_month, fmask, tm, &mut fsec, is2digits, order)?;
1266        if fmask & dmask != 0 {
1267            return Err(BadFormat);
1268        }
1269        fmask |= dmask;
1270        tmask |= dmask;
1271    }
1272    if fmask & !(m(DOY) | m(TZ)) != DATE_M {
1273        return Err(BadFormat);
1274    }
1275    Ok(tmask)
1276}
1277
1278/// `ValidateDate`: the checks and the changes of the year, the month and the day after all the fields are read.
1279fn validate_date(
1280    fmask: u32,
1281    isjulian: bool,
1282    is2digits: bool,
1283    bc: bool,
1284    tm: &mut Tm,
1285) -> Result<(), DtErr> {
1286    if fmask & m(YEAR) != 0 {
1287        if isjulian {
1288            // The year came from a Julian day.
1289        } else if bc {
1290            if tm.year <= 0 {
1291                return Err(FieldOverflow);
1292            }
1293            tm.year = -(tm.year - 1);
1294        } else if is2digits {
1295            if tm.year < 0 {
1296                return Err(FieldOverflow);
1297            }
1298            if tm.year < 70 {
1299                tm.year += 2000;
1300            } else if tm.year < 100 {
1301                tm.year += 1900;
1302            }
1303        } else if tm.year <= 0 {
1304            return Err(FieldOverflow);
1305        }
1306    }
1307    if fmask & m(DOY) != 0 {
1308        set_date(tm, date2j(tm.year, 1, 1).wrapping_add(tm.yday).wrapping_sub(1));
1309    }
1310    if fmask & m(MONTH) != 0 && !(1..=12).contains(&tm.mon) {
1311        return Err(DtErr::MdFieldOverflow);
1312    }
1313    if fmask & m(DAY) != 0 && !(1..=31).contains(&tm.mday) {
1314        return Err(DtErr::MdFieldOverflow);
1315    }
1316    if fmask & DATE_M == DATE_M && tm.mday > days_in_month(tm.year, tm.mon) {
1317        return Err(FieldOverflow);
1318    }
1319    Ok(())
1320}
1321
1322/// The `AM` and `PM` change of the hour, the same in `DecodeDateTime` and `DecodeTimeOnly`.
1323fn apply_meridian(mer: i32, tm: &mut Tm) -> Result<(), DtErr> {
1324    if mer != HR24 && tm.hour > 12 {
1325        return Err(FieldOverflow);
1326    }
1327    if mer == AM && tm.hour == 12 {
1328        tm.hour = 0;
1329    } else if mer == PM && tm.hour != 12 {
1330        tm.hour += 12;
1331    }
1332    Ok(())
1333}
1334
1335/// `DecodeDateTime`: the fields of a date or a timestamp.
1336fn decode_date_time(p: &Parsed, cx: &DateTimeInput<'_>) -> Result<Decoded, DtErr> {
1337    let mut fmask = 0u32;
1338    let mut ptype = 0;
1339    let mut dtype = DTK_DATE_VALUE;
1340    let mut tm = Tm::default();
1341    let mut fsec = 0;
1342    let mut tz = 0;
1343    let mut mer = HR24;
1344    let mut have_text_month = false;
1345    let mut isjulian = false;
1346    let mut is2digits = false;
1347    let mut bc = false;
1348    let mut named: Option<Arc<dyn TimeZone + Send + Sync>> = None;
1349    let mut dynamic: Option<(ZoneRef, &[u8])> = None;
1350
1351    for i in 0..p.n {
1352        let field = p.field(i);
1353        let tmask = match p.kinds[i] {
1354            DTK_DATE => {
1355                if ptype == DTK_JULIAN {
1356                    // A Julian day with a zone, such as `j2451187-08`.
1357                    let (jday, end, overflow) = strtoint(field, 0);
1358                    if overflow || jday < 0 {
1359                        return Err(FieldOverflow);
1360                    }
1361                    set_date(&mut tm, jday);
1362                    isjulian = true;
1363                    tz = decode_timezone(&field[end..])?;
1364                    ptype = 0;
1365                    DATE_M | TIME_M | m(TZ)
1366                } else if ptype != 0 || fmask & (m(MONTH) | m(DAY)) == m(MONTH) | m(DAY) {
1367                    // A zone name, or a time run together with a zone such as `040506-08`.
1368                    if field[0].is_ascii_digit() || ptype != 0 {
1369                        if ptype != 0 {
1370                            if ptype != DTK_TIME_VALUE {
1371                                return Err(BadFormat);
1372                            }
1373                            ptype = 0;
1374                        }
1375                        if fmask & TIME_M == TIME_M {
1376                            return Err(BadFormat);
1377                        }
1378                        let Some(dash) = field.iter().position(|&c| c == b'-') else {
1379                            return Err(BadFormat);
1380                        };
1381                        tz = decode_timezone(&field[dash..])?;
1382                        let tmask = decode_number_field(
1383                            &field[..dash],
1384                            fmask,
1385                            &mut tm,
1386                            &mut fsec,
1387                            &mut is2digits,
1388                        )?;
1389                        tmask | m(TZ)
1390                    } else {
1391                        let Some(zone) = cx.zones.zone(text(field)) else {
1392                            return Err(DtErr::BadTimezone(text(field).to_string()));
1393                        };
1394                        named = Some(zone);
1395                        m(TZ)
1396                    }
1397                } else {
1398                    decode_date(field, fmask, &mut is2digits, &mut tm, cx.order)?
1399                }
1400            }
1401            DTK_TIME => {
1402                if ptype != 0 {
1403                    if ptype != DTK_TIME_VALUE {
1404                        return Err(BadFormat);
1405                    }
1406                    ptype = 0;
1407                }
1408                let tmask = decode_time(field, &mut tm, &mut fsec)?;
1409                if time_overflows(tm.hour, tm.min, tm.sec, fsec) {
1410                    return Err(FieldOverflow);
1411                }
1412                tmask
1413            }
1414            DTK_TZ => {
1415                tz = decode_timezone(field)?;
1416                m(TZ)
1417            }
1418            DTK_NUMBER if ptype != 0 => {
1419                // The field after a `j` or a `t`.
1420                let (value, end, overflow) = strtoint(field, 0);
1421                if overflow {
1422                    return Err(FieldOverflow);
1423                }
1424                if at(field, end) != b'.' && end != field.len() {
1425                    return Err(BadFormat);
1426                }
1427                let tmask = match ptype {
1428                    DTK_JULIAN => {
1429                        if value < 0 {
1430                            return Err(FieldOverflow);
1431                        }
1432                        set_date(&mut tm, value);
1433                        isjulian = true;
1434                        if at(field, end) == b'.' {
1435                            let time = parse_fraction(&field[end..])? * USECS_PER_DAY as f64;
1436                            set_time(&mut tm, &mut fsec, time as i64);
1437                            DATE_M | TIME_M
1438                        } else {
1439                            DATE_M
1440                        }
1441                    }
1442                    DTK_TIME_VALUE => {
1443                        let tmask = decode_number_field(
1444                            field,
1445                            fmask | DATE_M,
1446                            &mut tm,
1447                            &mut fsec,
1448                            &mut is2digits,
1449                        )?;
1450                        if tmask != TIME_M {
1451                            return Err(BadFormat);
1452                        }
1453                        tmask
1454                    }
1455                    _ => return Err(BadFormat),
1456                };
1457                ptype = 0;
1458                dtype = DTK_DATE_VALUE;
1459                tmask
1460            }
1461            DTK_NUMBER => {
1462                let flen = field.len();
1463                let dot = field.iter().position(|&c| c == b'.');
1464                if dot.is_some() && fmask & DATE_M == 0 {
1465                    decode_date(field, fmask, &mut is2digits, &mut tm, cx.order)?
1466                } else if dot.is_some_and(|dot| dot > 2)
1467                    || (flen >= 6 && (fmask & DATE_M == 0 || fmask & TIME_M == 0))
1468                {
1469                    decode_number_field(field, fmask, &mut tm, &mut fsec, &mut is2digits)?
1470                } else {
1471                    decode_number(
1472                        field,
1473                        have_text_month,
1474                        fmask,
1475                        &mut tm,
1476                        &mut fsec,
1477                        &mut is2digits,
1478                        cx.order,
1479                    )?
1480                }
1481            }
1482            DTK_STRING | DTK_SPECIAL => {
1483                let (mut ty, mut val, valtz) = decode_timezone_abbrev(field, cx)?;
1484                if ty == UNKNOWN_FIELD {
1485                    (ty, val) = date_token(field).unwrap_or((UNKNOWN_FIELD, 0));
1486                }
1487                if ty == IGNORE_DTF {
1488                    continue;
1489                }
1490                let mut tmask = m(ty);
1491                match ty {
1492                    RESERV => match val {
1493                        DTK_NOW => {
1494                            tmask = DATE_M | TIME_M | m(TZ);
1495                            dtype = DTK_DATE_VALUE;
1496                            (tm, fsec, tz) = current_time(cx);
1497                        }
1498                        DTK_YESTERDAY | DTK_TODAY | DTK_TOMORROW => {
1499                            tmask = DATE_M;
1500                            dtype = DTK_DATE_VALUE;
1501                            let (now, _, _) = current_time(cx);
1502                            let shift = match val {
1503                                DTK_YESTERDAY => -1,
1504                                DTK_TOMORROW => 1,
1505                                _ => 0,
1506                            };
1507                            set_date(&mut tm, date2j(now.year, now.mon, now.mday) + shift);
1508                        }
1509                        DTK_ZULU => {
1510                            tmask = TIME_M | m(TZ);
1511                            dtype = DTK_DATE_VALUE;
1512                            (tm.hour, tm.min, tm.sec) = (0, 0, 0);
1513                            tz = 0;
1514                        }
1515                        _ => {
1516                            tmask = DATE_M | TIME_M | m(TZ);
1517                            dtype = val;
1518                        }
1519                    },
1520                    MONTH => {
1521                        // A number before a month name was the day, as in `1 jan 2001`.
1522                        if fmask & m(MONTH) != 0
1523                            && !have_text_month
1524                            && fmask & m(DAY) == 0
1525                            && (1..=31).contains(&tm.mon)
1526                        {
1527                            tm.mday = tm.mon;
1528                            tmask = m(DAY);
1529                        }
1530                        have_text_month = true;
1531                        tm.mon = val;
1532                    }
1533                    DTZMOD => {
1534                        tmask |= m(DTZ);
1535                        tz -= val;
1536                    }
1537                    DTZ => {
1538                        tmask |= m(TZ);
1539                        tz = -val;
1540                    }
1541                    TZ => tz = -val,
1542                    DYNTZ => {
1543                        tmask |= m(TZ);
1544                        dynamic = valtz.map(|zone| (zone, field));
1545                    }
1546                    AMPM => mer = val,
1547                    ADBC => bc = val == BC,
1548                    DOW => {}
1549                    UNITS => {
1550                        tmask = 0;
1551                        if ptype != 0 {
1552                            return Err(BadFormat);
1553                        }
1554                        ptype = val;
1555                    }
1556                    ISOTIME => {
1557                        tmask = 0;
1558                        if fmask & DATE_M != DATE_M || ptype != 0 {
1559                            return Err(BadFormat);
1560                        }
1561                        ptype = val;
1562                    }
1563                    UNKNOWN_FIELD => {
1564                        named = Some(cx.zones.zone(text(field)).ok_or(BadFormat)?);
1565                        tmask = m(TZ);
1566                    }
1567                    _ => return Err(BadFormat),
1568                }
1569                tmask
1570            }
1571            _ => return Err(BadFormat),
1572        };
1573        if tmask & fmask != 0 {
1574            return Err(BadFormat);
1575        }
1576        fmask |= tmask;
1577    }
1578    if ptype != 0 {
1579        return Err(BadFormat);
1580    }
1581    if dtype == DTK_DATE_VALUE {
1582        validate_date(fmask, isjulian, is2digits, bc, &mut tm)?;
1583        apply_meridian(mer, &mut tm)?;
1584        // A time with no date is an error for every caller.
1585        if fmask & DATE_M != DATE_M {
1586            return Err(BadFormat);
1587        }
1588        if let Some(zone) = &named {
1589            if fmask & m(DTZMOD) != 0 {
1590                return Err(BadFormat);
1591            }
1592            tz = determine_offset(&tm, &**zone).0;
1593        }
1594        if let Some((zone, abbrev)) = &dynamic {
1595            if fmask & m(DTZMOD) != 0 {
1596                return Err(BadFormat);
1597            }
1598            tz = determine_abbrev_offset(&tm, abbrev, zone.get(cx.zone));
1599        }
1600        if fmask & m(TZ) == 0 {
1601            if fmask & m(DTZMOD) != 0 {
1602                return Err(BadFormat);
1603            }
1604            tz = determine_offset(&tm, cx.zone).0;
1605        }
1606    }
1607    Ok(Decoded { dtype, tm, fsec, tz })
1608}
1609
1610/// `DecodeTimeOnly`: the fields of a `time` or a `timetz`. Gives the fields, the microseconds and the zone in seconds west of UTC.
1611fn decode_time_only(p: &Parsed, cx: &DateTimeInput<'_>) -> Result<(Tm, i32, i32), DtErr> {
1612    let nf = p.n;
1613    let mut fmask = 0u32;
1614    let mut ptype = 0;
1615    let mut tm = Tm::default();
1616    let mut fsec = 0;
1617    let mut tz = 0;
1618    let mut isjulian = false;
1619    let mut is2digits = false;
1620    let mut bc = false;
1621    let mut mer = HR24;
1622    let mut named: Option<Arc<dyn TimeZone + Send + Sync>> = None;
1623    let mut dynamic: Option<(ZoneRef, &[u8])> = None;
1624
1625    for i in 0..nf {
1626        let field = p.field(i);
1627        let tmask = match p.kinds[i] {
1628            DTK_DATE => {
1629                if i == 0 && nf >= 2 && (p.kinds[nf - 1] == DTK_DATE || p.kinds[1] == DTK_TIME) {
1630                    decode_date(field, fmask, &mut is2digits, &mut tm, cx.order)?
1631                } else if field[0].is_ascii_digit() {
1632                    if fmask & TIME_M == TIME_M {
1633                        return Err(BadFormat);
1634                    }
1635                    let Some(dash) = field.iter().position(|&c| c == b'-') else {
1636                        return Err(BadFormat);
1637                    };
1638                    tz = decode_timezone(&field[dash..])?;
1639                    let tmask = decode_number_field(
1640                        &field[..dash],
1641                        fmask | DATE_M,
1642                        &mut tm,
1643                        &mut fsec,
1644                        &mut is2digits,
1645                    )?;
1646                    tmask | m(TZ)
1647                } else {
1648                    let Some(zone) = cx.zones.zone(text(field)) else {
1649                        return Err(DtErr::BadTimezone(text(field).to_string()));
1650                    };
1651                    named = Some(zone);
1652                    m(TZ)
1653                }
1654            }
1655            DTK_TIME => {
1656                if ptype != 0 {
1657                    if ptype != DTK_TIME_VALUE {
1658                        return Err(BadFormat);
1659                    }
1660                    ptype = 0;
1661                }
1662                decode_time(field, &mut tm, &mut fsec)?
1663            }
1664            DTK_TZ => {
1665                tz = decode_timezone(field)?;
1666                m(TZ)
1667            }
1668            DTK_NUMBER if ptype != 0 => {
1669                let (value, end, overflow) = strtoint(field, 0);
1670                if overflow {
1671                    return Err(FieldOverflow);
1672                }
1673                if at(field, end) != b'.' && end != field.len() {
1674                    return Err(BadFormat);
1675                }
1676                let tmask = match ptype {
1677                    DTK_JULIAN => {
1678                        if value < 0 {
1679                            return Err(FieldOverflow);
1680                        }
1681                        set_date(&mut tm, value);
1682                        isjulian = true;
1683                        if at(field, end) == b'.' {
1684                            let time = parse_fraction(&field[end..])? * USECS_PER_DAY as f64;
1685                            set_time(&mut tm, &mut fsec, time as i64);
1686                            DATE_M | TIME_M
1687                        } else {
1688                            DATE_M
1689                        }
1690                    }
1691                    DTK_TIME_VALUE => {
1692                        let tmask = decode_number_field(
1693                            field,
1694                            fmask | DATE_M,
1695                            &mut tm,
1696                            &mut fsec,
1697                            &mut is2digits,
1698                        )?;
1699                        if tmask != TIME_M {
1700                            return Err(BadFormat);
1701                        }
1702                        tmask
1703                    }
1704                    _ => return Err(BadFormat),
1705                };
1706                ptype = 0;
1707                tmask
1708            }
1709            DTK_NUMBER => {
1710                let flen = field.len();
1711                match field.iter().position(|&c| c == b'.') {
1712                    Some(_) if i == 0 && nf >= 2 && p.kinds[nf - 1] == DTK_DATE => {
1713                        decode_date(field, fmask, &mut is2digits, &mut tm, cx.order)?
1714                    }
1715                    Some(dot) if dot > 2 => decode_number_field(
1716                        field,
1717                        fmask | DATE_M,
1718                        &mut tm,
1719                        &mut fsec,
1720                        &mut is2digits,
1721                    )?,
1722                    Some(_) => return Err(BadFormat),
1723                    None if flen > 4 => decode_number_field(
1724                        field,
1725                        fmask | DATE_M,
1726                        &mut tm,
1727                        &mut fsec,
1728                        &mut is2digits,
1729                    )?,
1730                    None => decode_number(
1731                        field,
1732                        false,
1733                        fmask | DATE_M,
1734                        &mut tm,
1735                        &mut fsec,
1736                        &mut is2digits,
1737                        cx.order,
1738                    )?,
1739                }
1740            }
1741            DTK_STRING | DTK_SPECIAL => {
1742                let (mut ty, mut val, valtz) = decode_timezone_abbrev(field, cx)?;
1743                if ty == UNKNOWN_FIELD {
1744                    (ty, val) = date_token(field).unwrap_or((UNKNOWN_FIELD, 0));
1745                }
1746                if ty == IGNORE_DTF {
1747                    continue;
1748                }
1749                let mut tmask = m(ty);
1750                match ty {
1751                    RESERV => match val {
1752                        DTK_NOW => {
1753                            tmask = TIME_M;
1754                            (tm, fsec, _) = current_time(cx);
1755                        }
1756                        DTK_ZULU => {
1757                            tmask = TIME_M | m(TZ);
1758                            (tm.hour, tm.min, tm.sec) = (0, 0, 0);
1759                        }
1760                        _ => return Err(BadFormat),
1761                    },
1762                    DTZMOD => {
1763                        tmask |= m(DTZ);
1764                        tz -= val;
1765                    }
1766                    DTZ => {
1767                        tmask |= m(TZ);
1768                        tz = -val;
1769                    }
1770                    TZ => tz = -val,
1771                    DYNTZ => {
1772                        tmask |= m(TZ);
1773                        dynamic = valtz.map(|zone| (zone, field));
1774                    }
1775                    AMPM => mer = val,
1776                    ADBC => bc = val == BC,
1777                    UNITS | ISOTIME => {
1778                        tmask = 0;
1779                        if ptype != 0 {
1780                            return Err(BadFormat);
1781                        }
1782                        ptype = val;
1783                    }
1784                    UNKNOWN_FIELD => {
1785                        named = Some(cx.zones.zone(text(field)).ok_or(BadFormat)?);
1786                        tmask = m(TZ);
1787                    }
1788                    _ => return Err(BadFormat),
1789                }
1790                tmask
1791            }
1792            _ => return Err(BadFormat),
1793        };
1794        if tmask & fmask != 0 {
1795            return Err(BadFormat);
1796        }
1797        fmask |= tmask;
1798    }
1799    if ptype != 0 {
1800        return Err(BadFormat);
1801    }
1802    validate_date(fmask, isjulian, is2digits, bc, &mut tm)?;
1803    apply_meridian(mer, &mut tm)?;
1804    if time_overflows(tm.hour, tm.min, tm.sec, fsec) {
1805        return Err(FieldOverflow);
1806    }
1807    if fmask & TIME_M != TIME_M {
1808        return Err(BadFormat);
1809    }
1810    // The date of the zone rules: the date of the input, or today when there is none.
1811    let local = |tm: &Tm| -> Result<Tm, DtErr> {
1812        let mut local = if fmask & DATE_M == 0 {
1813            current_time(cx).0
1814        } else if fmask & DATE_M != DATE_M {
1815            return Err(BadFormat);
1816        } else {
1817            *tm
1818        };
1819        (local.hour, local.min, local.sec) = (tm.hour, tm.min, tm.sec);
1820        Ok(local)
1821    };
1822    if let Some(zone) = &named {
1823        if fmask & m(DTZMOD) != 0 {
1824            return Err(BadFormat);
1825        }
1826        tz = match zone.fixed_offset() {
1827            Some(offset) => -offset,
1828            None => {
1829                if fmask & DATE_M != DATE_M {
1830                    return Err(BadFormat);
1831                }
1832                determine_offset(&tm, &**zone).0
1833            }
1834        };
1835    }
1836    if let Some((zone, abbrev)) = &dynamic {
1837        if fmask & m(DTZMOD) != 0 {
1838            return Err(BadFormat);
1839        }
1840        tz = determine_abbrev_offset(&local(&tm)?, abbrev, zone.get(cx.zone));
1841    }
1842    if fmask & m(TZ) == 0 {
1843        if fmask & m(DTZMOD) != 0 {
1844            return Err(BadFormat);
1845        }
1846        tz = determine_offset(&local(&tm)?, cx.zone).0;
1847    }
1848    Ok((tm, fsec, tz))
1849}
1850
1851/// `struct pg_itm_in`: the fields of an interval while it is read.
1852#[derive(Debug, Clone, Copy, Default)]
1853struct Itm {
1854    usec: i64,
1855    mday: i32,
1856    mon: i32,
1857    year: i32,
1858}
1859
1860impl Itm {
1861    /// `AdjustFractMicroseconds`.
1862    fn fract_usecs(&mut self, frac: f64, scale: i64) -> bool {
1863        if frac == 0.0 {
1864            return true;
1865        }
1866        let frac = frac * scale as f64;
1867        let mut usec = frac as i64;
1868        let rest = frac - usec as f64;
1869        if rest > 0.5 {
1870            usec += 1;
1871        } else if rest < -0.5 {
1872            usec -= 1;
1873        }
1874        self.usec.checked_add(usec).map(|usec| self.usec = usec).is_some()
1875    }
1876
1877    /// `AdjustFractDays`.
1878    fn fract_days(&mut self, frac: f64, scale: i32) -> bool {
1879        if frac == 0.0 {
1880            return true;
1881        }
1882        let frac = frac * f64::from(scale);
1883        let days = frac as i32;
1884        match self.mday.checked_add(days) {
1885            Some(mday) => self.mday = mday,
1886            None => return false,
1887        }
1888        self.fract_usecs(frac - f64::from(days), USECS_PER_DAY)
1889    }
1890
1891    /// `AdjustFractYears`.
1892    fn fract_years(&mut self, frac: f64, scale: i32) -> bool {
1893        let months = (frac * f64::from(scale) * 12.0).round_ties_even() as i32;
1894        self.mon.checked_add(months).map(|mon| self.mon = mon).is_some()
1895    }
1896
1897    /// `AdjustMicroseconds`.
1898    fn usecs(&mut self, val: i64, fval: f64, scale: i64) -> bool {
1899        mul_add(val, scale, &mut self.usec) && self.fract_usecs(fval, scale)
1900    }
1901
1902    /// `AdjustDays`.
1903    fn days(&mut self, val: i64, scale: i32) -> bool {
1904        let Ok(val) = i32::try_from(val) else { return false };
1905        match val.checked_mul(scale).and_then(|days| self.mday.checked_add(days)) {
1906            Some(mday) => {
1907                self.mday = mday;
1908                true
1909            }
1910            None => false,
1911        }
1912    }
1913
1914    /// `AdjustMonths`.
1915    fn months(&mut self, val: i64) -> bool {
1916        let Ok(val) = i32::try_from(val) else { return false };
1917        self.mon.checked_add(val).map(|mon| self.mon = mon).is_some()
1918    }
1919
1920    /// `AdjustYears`.
1921    fn years(&mut self, val: i64, scale: i32) -> bool {
1922        let Ok(val) = i32::try_from(val) else { return false };
1923        match val.checked_mul(scale).and_then(|years| self.year.checked_add(years)) {
1924            Some(year) => {
1925                self.year = year;
1926                true
1927            }
1928            None => false,
1929        }
1930    }
1931}
1932
1933/// `int64_multiply_add`.
1934fn mul_add(val: i64, multiplier: i64, sum: &mut i64) -> bool {
1935    match val.checked_mul(multiplier).and_then(|product| sum.checked_add(product)) {
1936        Some(value) => {
1937            *sum = value;
1938            true
1939        }
1940        None => false,
1941    }
1942}
1943
1944fn check(ok: bool) -> Result<(), DtErr> {
1945    if ok { Ok(()) } else { Err(FieldOverflow) }
1946}
1947
1948/// `DecodeTimeForInterval`. The time replaces the microseconds read so far, as in the C code.
1949fn decode_time_for_interval(s: &[u8], range: i32, itm: &mut Itm) -> Result<(), DtErr> {
1950    let (hour, min, sec, fsec) = decode_time_common(s, range)?;
1951    itm.usec = i64::from(fsec);
1952    check(
1953        mul_add(hour, USECS_PER_HOUR, &mut itm.usec)
1954            && mul_add(i64::from(min), USECS_PER_MINUTE, &mut itm.usec)
1955            && mul_add(i64::from(sec), USECS_PER_SEC, &mut itm.usec),
1956    )
1957}
1958
1959/// The unit of the rightmost number of an interval with no unit, from the range of the typmod.
1960fn default_unit(range: i32) -> i32 {
1961    let mask = |fields: &[i32]| fields.iter().fold(0, |mask, &field| mask | 1 << field);
1962    match range {
1963        r if r == mask(&[YEAR]) => DTK_YEAR,
1964        r if r == mask(&[MONTH]) || r == mask(&[YEAR, MONTH]) => DTK_MONTH,
1965        r if r == mask(&[DAY]) => DTK_DAY,
1966        r if r == mask(&[HOUR]) || r == mask(&[DAY, HOUR]) => DTK_HOUR,
1967        r if r == mask(&[MINUTE])
1968            || r == mask(&[HOUR, MINUTE])
1969            || r == mask(&[DAY, HOUR, MINUTE]) =>
1970        {
1971            DTK_MINUTE
1972        }
1973        _ => DTK_SECOND,
1974    }
1975}
1976
1977/// `DecodeInterval`: the fields of an interval in the PostgreSQL and the SQL formats. The fields are read from the right, so that a unit comes before its number.
1978fn decode_interval(p: &Parsed, range: i32, style: IntervalStyle) -> Result<(i32, Itm), DtErr> {
1979    let nf = p.n;
1980    let mut is_before = false;
1981    let mut parsing_unit_val = false;
1982    let mut fmask = 0u32;
1983    let mut ty = IGNORE_DTF;
1984    let mut dtype = DTK_DELTA;
1985    let mut itm = Itm::default();
1986
1987    // In the SQL standard style, a leading minus applies to all the fields when no other field has a sign.
1988    let force_negative = style == IntervalStyle::SqlStandard
1989        && nf > 0
1990        && p.field(0)[0] == b'-'
1991        && !(1..nf).any(|i| matches!(p.field(i)[0], b'-' | b'+'));
1992
1993    for i in (0..nf).rev() {
1994        let field = p.field(i);
1995        let kind = p.kinds[i];
1996        let tmask = if kind == DTK_TIME {
1997            decode_time_for_interval(field, range, &mut itm)?;
1998            if force_negative && itm.usec > 0 {
1999                itm.usec = -itm.usec;
2000            }
2001            ty = DTK_DAY;
2002            parsing_unit_val = false;
2003            TIME_M
2004        } else if kind == DTK_TZ
2005            && field[1..].contains(&b':')
2006            && decode_time_for_interval(&field[1..], range, &mut itm).is_ok()
2007        {
2008            // A signed time such as `-01:02:03`.
2009            if field[0] == b'-' {
2010                if itm.usec == i64::MIN {
2011                    return Err(FieldOverflow);
2012                }
2013                itm.usec = -itm.usec;
2014            }
2015            if force_negative && itm.usec > 0 {
2016                itm.usec = -itm.usec;
2017            }
2018            ty = DTK_DAY;
2019            parsing_unit_val = false;
2020            TIME_M
2021        } else if matches!(kind, DTK_TZ | DTK_DATE | DTK_NUMBER) {
2022            if ty == IGNORE_DTF {
2023                ty = default_unit(range);
2024            }
2025            let (mut val, end, overflow) = strtol(field, 0);
2026            if overflow {
2027                return Err(FieldOverflow);
2028            }
2029            let mut fval;
2030            match at(field, end) {
2031                b'-' => {
2032                    // The SQL `years-months` form.
2033                    let (val2, end, overflow) = strtoint(field, end + 1);
2034                    if overflow || !(0..12).contains(&val2) {
2035                        return Err(FieldOverflow);
2036                    }
2037                    if end != field.len() {
2038                        return Err(BadFormat);
2039                    }
2040                    ty = DTK_MONTH;
2041                    let val2 = if field[0] == b'-' { -val2 } else { val2 };
2042                    val = val
2043                        .checked_mul(12)
2044                        .and_then(|v| v.checked_add(i64::from(val2)))
2045                        .ok_or(FieldOverflow)?;
2046                    fval = 0.0;
2047                }
2048                b'.' => {
2049                    fval = parse_fraction(&field[end..])?;
2050                    if field[0] == b'-' {
2051                        fval = -fval;
2052                    }
2053                }
2054                _ if end == field.len() => fval = 0.0,
2055                _ => return Err(BadFormat),
2056            }
2057            if force_negative {
2058                if val > 0 {
2059                    val = -val;
2060                }
2061                if fval > 0.0 {
2062                    fval = -fval;
2063                }
2064            }
2065            let tmask = match ty {
2066                DTK_MICROSEC => {
2067                    check(itm.usecs(val, fval, 1))?;
2068                    m(MICROSECOND)
2069                }
2070                DTK_MILLISEC => {
2071                    check(itm.usecs(val, fval, 1000))?;
2072                    m(MILLISECOND)
2073                }
2074                DTK_SECOND => {
2075                    check(itm.usecs(val, fval, USECS_PER_SEC))?;
2076                    // A fraction of a second also fills the smaller units.
2077                    if fval == 0.0 { m(SECOND) } else { ALL_SECS_M }
2078                }
2079                DTK_MINUTE => {
2080                    check(itm.usecs(val, fval, USECS_PER_MINUTE))?;
2081                    m(MINUTE)
2082                }
2083                DTK_HOUR => {
2084                    check(itm.usecs(val, fval, USECS_PER_HOUR))?;
2085                    ty = DTK_DAY;
2086                    m(HOUR)
2087                }
2088                DTK_DAY => {
2089                    check(itm.days(val, 1) && itm.fract_usecs(fval, USECS_PER_DAY))?;
2090                    m(DAY)
2091                }
2092                DTK_WEEK => {
2093                    check(itm.days(val, 7) && itm.fract_days(fval, 7))?;
2094                    m(WEEK)
2095                }
2096                DTK_MONTH => {
2097                    check(itm.months(val) && itm.fract_days(fval, DAYS_PER_MONTH))?;
2098                    m(MONTH)
2099                }
2100                DTK_YEAR => {
2101                    check(itm.years(val, 1) && itm.fract_years(fval, 1))?;
2102                    m(YEAR)
2103                }
2104                DTK_DECADE => {
2105                    check(itm.years(val, 10) && itm.fract_years(fval, 10))?;
2106                    m(DECADE)
2107                }
2108                DTK_CENTURY => {
2109                    check(itm.years(val, 100) && itm.fract_years(fval, 100))?;
2110                    m(CENTURY)
2111                }
2112                DTK_MILLENNIUM => {
2113                    check(itm.years(val, 1000) && itm.fract_years(fval, 1000))?;
2114                    m(MILLENNIUM)
2115                }
2116                _ => return Err(BadFormat),
2117            };
2118            parsing_unit_val = false;
2119            tmask
2120        } else if matches!(kind, DTK_STRING | DTK_SPECIAL) {
2121            if parsing_unit_val {
2122                return Err(BadFormat);
2123            }
2124            let (t, uval) =
2125                delta_token(field).or_else(|| date_token(field)).unwrap_or((UNKNOWN_FIELD, 0));
2126            ty = t;
2127            if ty == IGNORE_DTF {
2128                continue;
2129            }
2130            match ty {
2131                UNITS => {
2132                    ty = uval;
2133                    parsing_unit_val = true;
2134                    0
2135                }
2136                AGO => {
2137                    // Only at the end.
2138                    if i != nf - 1 {
2139                        return Err(BadFormat);
2140                    }
2141                    is_before = true;
2142                    ty = uval;
2143                    0
2144                }
2145                RESERV => {
2146                    // Only `infinity` and `-infinity`, and nothing after them.
2147                    if (uval != DTK_LATE && uval != DTK_EARLY) || i != nf - 1 {
2148                        return Err(BadFormat);
2149                    }
2150                    dtype = uval;
2151                    DATE_M | TIME_M
2152                }
2153                _ => return Err(BadFormat),
2154            }
2155        } else {
2156            return Err(BadFormat);
2157        };
2158        if tmask & fmask != 0 {
2159            return Err(BadFormat);
2160        }
2161        fmask |= tmask;
2162    }
2163    if fmask == 0 || parsing_unit_val {
2164        return Err(BadFormat);
2165    }
2166    if is_before {
2167        if itm.usec == i64::MIN
2168            || itm.mday == i32::MIN
2169            || itm.mon == i32::MIN
2170            || itm.year == i32::MIN
2171        {
2172            return Err(FieldOverflow);
2173        }
2174        itm = Itm { usec: -itm.usec, mday: -itm.mday, mon: -itm.mon, year: -itm.year };
2175    }
2176    Ok((dtype, itm))
2177}
2178
2179/// `ParseISO8601Number`: a number as `strtod` reads it, cut into the integer part and the fraction. Gives the two parts and the end.
2180fn iso8601_number(s: &[u8], pos: usize) -> Result<(i64, f64, usize), DtErr> {
2181    let c = at(s, pos);
2182    if !(c.is_ascii_digit() || c == b'-' || c == b'.') {
2183        return Err(BadFormat);
2184    }
2185    let (value, len) = match strtod::<f64>(&s[pos..]) {
2186        Some((value, len, false)) if len > 0 => (value, len),
2187        _ => return Err(BadFormat),
2188    };
2189    if value.is_nan() || !(-1.0e15..=1.0e15).contains(&value) {
2190        return Err(FieldOverflow);
2191    }
2192    let ipart = value.trunc() as i64;
2193    Ok((ipart, value - ipart as f64, pos + len))
2194}
2195
2196/// `ISO8601IntegerWidth`: the number of digits before the point, after an optional minus.
2197fn iso8601_width(s: &[u8], pos: usize) -> usize {
2198    let pos = pos + usize::from(at(s, pos) == b'-');
2199    s[pos.min(s.len())..].iter().take_while(|c| c.is_ascii_digit()).count()
2200}
2201
2202/// `DecodeISO8601Interval`: `P1Y2M3DT4H5M6S` and the alternative forms `P0001-02-03T04:05:06` and `P00010203T040506`.
2203fn decode_iso8601_interval(s: &[u8]) -> Result<(i32, Itm), DtErr> {
2204    let mut itm = Itm::default();
2205    if s.len() < 2 || s[0] != b'P' {
2206        return Err(BadFormat);
2207    }
2208    let mut pos = 1;
2209    let mut datepart = true;
2210    let mut havefield = false;
2211    while at(s, pos) != 0 {
2212        if at(s, pos) == b'T' {
2213            datepart = false;
2214            havefield = false;
2215            pos += 1;
2216            continue;
2217        }
2218        let fieldstart = pos;
2219        let (val, fval, end) = iso8601_number(s, pos)?;
2220        let unit = at(s, end);
2221        pos = end + 1;
2222        if datepart {
2223            match unit {
2224                b'Y' => check(itm.years(val, 1) && itm.fract_years(fval, 1))?,
2225                b'M' => check(itm.months(val) && itm.fract_days(fval, DAYS_PER_MONTH))?,
2226                b'W' => check(itm.days(val, 7) && itm.fract_days(fval, 7))?,
2227                b'D' => check(itm.days(val, 1) && itm.fract_usecs(fval, USECS_PER_DAY))?,
2228                b'T' | 0 | b'-' => {
2229                    if unit != b'-' && iso8601_width(s, fieldstart) == 8 && !havefield {
2230                        // The basic alternative form, `yyyymmdd`.
2231                        check(
2232                            itm.years(val / 10000, 1)
2233                                && itm.months((val / 100) % 100)
2234                                && itm.days(val % 100, 1)
2235                                && itm.fract_usecs(fval, USECS_PER_DAY),
2236                        )?;
2237                        if unit == 0 {
2238                            return Ok((DTK_DELTA, itm));
2239                        }
2240                        datepart = false;
2241                        havefield = false;
2242                        continue;
2243                    }
2244                    // The extended alternative form, `yyyy-mm-dd`.
2245                    if havefield {
2246                        return Err(BadFormat);
2247                    }
2248                    check(itm.years(val, 1) && itm.fract_years(fval, 1))?;
2249                    if unit == 0 {
2250                        return Ok((DTK_DELTA, itm));
2251                    }
2252                    if unit == b'T' {
2253                        datepart = false;
2254                        havefield = false;
2255                        continue;
2256                    }
2257                    let (val, fval, end) = iso8601_number(s, pos)?;
2258                    pos = end;
2259                    check(itm.months(val) && itm.fract_days(fval, DAYS_PER_MONTH))?;
2260                    if at(s, pos) == 0 {
2261                        return Ok((DTK_DELTA, itm));
2262                    }
2263                    if at(s, pos) == b'T' {
2264                        datepart = false;
2265                        havefield = false;
2266                        continue;
2267                    }
2268                    if at(s, pos) != b'-' {
2269                        return Err(BadFormat);
2270                    }
2271                    let (val, fval, end) = iso8601_number(s, pos + 1)?;
2272                    pos = end;
2273                    check(itm.days(val, 1) && itm.fract_usecs(fval, USECS_PER_DAY))?;
2274                    if at(s, pos) == 0 {
2275                        return Ok((DTK_DELTA, itm));
2276                    }
2277                    if at(s, pos) == b'T' {
2278                        datepart = false;
2279                        havefield = false;
2280                        continue;
2281                    }
2282                    return Err(BadFormat);
2283                }
2284                _ => return Err(BadFormat),
2285            }
2286        } else {
2287            match unit {
2288                b'H' => check(itm.usecs(val, fval, USECS_PER_HOUR))?,
2289                b'M' => check(itm.usecs(val, fval, USECS_PER_MINUTE))?,
2290                b'S' => check(itm.usecs(val, fval, USECS_PER_SEC))?,
2291                0 | b':' => {
2292                    if unit == 0 && iso8601_width(s, fieldstart) == 6 && !havefield {
2293                        // The basic alternative form, `hhmmss`.
2294                        check(
2295                            itm.usecs(val / 10000, 0.0, USECS_PER_HOUR)
2296                                && itm.usecs((val / 100) % 100, 0.0, USECS_PER_MINUTE)
2297                                && itm.usecs(val % 100, 0.0, USECS_PER_SEC)
2298                                && itm.fract_usecs(fval, 1),
2299                        )?;
2300                        return Ok((DTK_DELTA, itm));
2301                    }
2302                    // The extended alternative form, `hh:mm:ss`.
2303                    if havefield {
2304                        return Err(BadFormat);
2305                    }
2306                    check(itm.usecs(val, fval, USECS_PER_HOUR))?;
2307                    if unit == 0 {
2308                        return Ok((DTK_DELTA, itm));
2309                    }
2310                    let (val, fval, end) = iso8601_number(s, pos)?;
2311                    pos = end;
2312                    check(itm.usecs(val, fval, USECS_PER_MINUTE))?;
2313                    if at(s, pos) == 0 {
2314                        return Ok((DTK_DELTA, itm));
2315                    }
2316                    if at(s, pos) != b':' {
2317                        return Err(BadFormat);
2318                    }
2319                    let (val, fval, end) = iso8601_number(s, pos + 1)?;
2320                    pos = end;
2321                    check(itm.usecs(val, fval, USECS_PER_SEC))?;
2322                    if at(s, pos) == 0 {
2323                        return Ok((DTK_DELTA, itm));
2324                    }
2325                    return Err(BadFormat);
2326                }
2327                _ => return Err(BadFormat),
2328            }
2329        }
2330        havefield = true;
2331    }
2332    Ok((DTK_DELTA, itm))
2333}
2334
2335/// `tm2timestamp`: the timestamp of the fields, with the zone in seconds west of UTC for a `timestamptz`.
2336fn tm2timestamp(tm: &Tm, fsec: i32, tz: Option<i32>) -> Option<i64> {
2337    if !is_valid_julian(tm.year, tm.mon) {
2338        return None;
2339    }
2340    let date = i64::from(date2j(tm.year, tm.mon, tm.mday) - POSTGRES_EPOCH_JDATE);
2341    let time = ((i64::from(tm.hour) * 60 + i64::from(tm.min)) * 60 + i64::from(tm.sec))
2342        * USECS_PER_SEC
2343        + i64::from(fsec);
2344    let mut ts = date.checked_mul(USECS_PER_DAY)?.checked_add(time)?;
2345    if let Some(tz) = tz {
2346        ts = ts.wrapping_add(i64::from(tz) * USECS_PER_SEC);
2347    }
2348    (MIN_TIMESTAMP..END_TIMESTAMP).contains(&ts).then_some(ts)
2349}
2350
2351/// `date_in`: the days since 2000-01-01.
2352pub fn date_in(input: &str, cx: &DateTimeInput<'_>) -> Result<i32, TypeError> {
2353    match iso_date(input.as_bytes()) {
2354        Some(date) => Ok(date),
2355        None => parsed_date(input, cx),
2356    }
2357}
2358
2359/// `date_in` through `ParseDateTime` and `DecodeDateTime`.
2360fn parsed_date(input: &str, cx: &DateTimeInput<'_>) -> Result<i32, TypeError> {
2361    let error = |e: DtErr| e.into_error(input, "date");
2362    let parsed = parse_date_time(c_string(input), DATE_BUFLEN).map_err(error)?;
2363    let decoded = decode_date_time(&parsed, cx).map_err(error)?;
2364    let tm = match decoded.dtype {
2365        DTK_DATE_VALUE => decoded.tm,
2366        DTK_EPOCH => Tm { year: 1970, mon: 1, mday: 1, ..Tm::default() },
2367        DTK_LATE => return Ok(DATE_INFINITY),
2368        DTK_EARLY => return Ok(DATE_NEGATIVE_INFINITY),
2369        _ => return Err(error(BadFormat)),
2370    };
2371    let range = || {
2372        TypeError::new(
2373            SqlState::DATETIME_VALUE_OUT_OF_RANGE,
2374            format!("date out of range: \"{input}\""),
2375        )
2376    };
2377    if !is_valid_julian(tm.year, tm.mon) {
2378        return Err(range());
2379    }
2380    let date = date2j(tm.year, tm.mon, tm.mday) - POSTGRES_EPOCH_JDATE;
2381    if !(-POSTGRES_EPOCH_JDATE..DATE_END_JULIAN - POSTGRES_EPOCH_JDATE).contains(&date) {
2382        return Err(range());
2383    }
2384    Ok(date)
2385}
2386
2387/// The date of a valid `YYYY-MM-DD`, which every `DateStyle` reads the same way. Other input gives `None` and goes through the full parser, which also gives the errors.
2388fn iso_date(b: &[u8]) -> Option<i32> {
2389    if b.len() != 10 || b[4] != b'-' || b[7] != b'-' {
2390        return None;
2391    }
2392    let number = |range: std::ops::Range<usize>| {
2393        b[range]
2394            .iter()
2395            .try_fold(0, |n: i32, &c| c.is_ascii_digit().then(|| n * 10 + (c - b'0') as i32))
2396    };
2397    let (year, month, day) = (number(0..4)?, number(5..7)?, number(8..10)?);
2398    if year == 0 || !(1..=12).contains(&month) || day == 0 || day > days_in_month(year, month) {
2399        return None;
2400    }
2401    Some(date2j(year, month, day) - POSTGRES_EPOCH_JDATE)
2402}
2403
2404/// `DecodeTimeOnly` for `time_in` and `timetz_in`: the time in microseconds and the zone.
2405fn time_value(
2406    input: &str,
2407    type_name: &str,
2408    cx: &DateTimeInput<'_>,
2409) -> Result<(i64, i32), TypeError> {
2410    let error = |e: DtErr| e.into_error(input, type_name);
2411    let parsed = parse_date_time(c_string(input), DATE_BUFLEN).map_err(error)?;
2412    let (tm, fsec, tz) = decode_time_only(&parsed, cx).map_err(error)?;
2413    let time = ((i64::from(tm.hour) * 60 + i64::from(tm.min)) * 60 + i64::from(tm.sec))
2414        * USECS_PER_SEC
2415        + i64::from(fsec);
2416    Ok((time, tz))
2417}
2418
2419/// `time_in`: the microseconds since midnight, rounded to the typmod.
2420pub fn time_in(input: &str, typmod: i32, cx: &DateTimeInput<'_>) -> Result<i64, TypeError> {
2421    let (time, _) = time_value(input, "time", cx)?;
2422    Ok(adjust_time(time, typmod))
2423}
2424
2425/// `timetz_in`: the time and the zone in seconds west of UTC.
2426pub fn timetz_in(
2427    input: &str,
2428    typmod: i32,
2429    cx: &DateTimeInput<'_>,
2430) -> Result<(i64, i32), TypeError> {
2431    let (time, tz) = time_value(input, "time with time zone", cx)?;
2432    Ok((adjust_time(time, typmod), tz))
2433}
2434
2435fn timestamp_value(
2436    input: &str,
2437    typmod: i32,
2438    cx: &DateTimeInput<'_>,
2439    with_zone: bool,
2440) -> Result<i64, TypeError> {
2441    let type_name = if with_zone { "timestamp with time zone" } else { "timestamp" };
2442    let error = |e: DtErr| e.into_error(input, type_name);
2443    let parsed = parse_date_time(c_string(input), TIMESTAMP_BUFLEN).map_err(error)?;
2444    let decoded = decode_date_time(&parsed, cx).map_err(error)?;
2445    let ts = match decoded.dtype {
2446        DTK_DATE_VALUE => tm2timestamp(&decoded.tm, decoded.fsec, with_zone.then_some(decoded.tz))
2447            .ok_or_else(|| {
2448                TypeError::new(
2449                    SqlState::DATETIME_VALUE_OUT_OF_RANGE,
2450                    format!("timestamp out of range: \"{input}\""),
2451                )
2452            })?,
2453        DTK_EPOCH => -946_684_800 * USECS_PER_SEC,
2454        DTK_LATE => TIMESTAMP_INFINITY,
2455        _ => TIMESTAMP_NEGATIVE_INFINITY,
2456    };
2457    adjust_timestamp(ts, typmod)
2458}
2459
2460/// `timestamp_in`: the microseconds since 2000-01-01 in local time, rounded to the typmod.
2461pub fn timestamp_in(input: &str, typmod: i32, cx: &DateTimeInput<'_>) -> Result<i64, TypeError> {
2462    timestamp_value(input, typmod, cx, false)
2463}
2464
2465/// `timestamptz_in`: the microseconds since 2000-01-01 UTC, rounded to the typmod. A value with no zone is in the session zone.
2466pub fn timestamptz_in(input: &str, typmod: i32, cx: &DateTimeInput<'_>) -> Result<i64, TypeError> {
2467    timestamp_value(input, typmod, cx, true)
2468}
2469
2470/// `interval_in`: the PostgreSQL, SQL and ISO 8601 forms. The typmod gives the unit of a number with no unit, and the result is cut and rounded to it.
2471pub fn interval_in(input: &str, typmod: i32, style: IntervalStyle) -> Result<Interval, TypeError> {
2472    let range = if typmod >= 0 { (typmod >> 16) & 0x7fff } else { INTERVAL_FULL_RANGE };
2473    let bytes = c_string(input);
2474    let mut result =
2475        parse_date_time(bytes, INTERVAL_BUFLEN).and_then(|p| decode_interval(&p, range, style));
2476    if matches!(result, Err(BadFormat)) {
2477        result = decode_iso8601_interval(bytes);
2478    }
2479    let (dtype, itm) = result.map_err(|e| {
2480        let e = if e == FieldOverflow { DtErr::IntervalOverflow } else { e };
2481        e.into_error(input, "interval")
2482    })?;
2483    let period = match dtype {
2484        DTK_LATE => Interval::INFINITY,
2485        DTK_EARLY => Interval::NEGATIVE_INFINITY,
2486        _ => {
2487            let months = i64::from(itm.year) * 12 + i64::from(itm.mon);
2488            let month = i32::try_from(months).map_err(|_| out_of_range("interval"))?;
2489            Interval { time: itm.usec, day: itm.mday, month }
2490        }
2491    };
2492    adjust_interval(period, typmod)
2493}
2494
2495#[cfg(test)]
2496mod tests {
2497    use super::*;
2498    use crate::datetime::FixedZone;
2499
2500    fn fields(input: &str) -> Vec<(u8, String)> {
2501        let p = parse_date_time(input.as_bytes(), INTERVAL_BUFLEN).unwrap();
2502        (0..p.n).map(|i| (p.kinds[i], text(p.field(i)).to_string())).collect()
2503    }
2504
2505    #[test]
2506    fn the_fields_have_the_kinds_of_parse_date_time() {
2507        assert_eq!(
2508            fields("Feb-7-1997 15:23:27.5 +05:30"),
2509            [
2510                (DTK_DATE, "feb-7-1997".to_string()),
2511                (DTK_TIME, "15:23:27.5".to_string()),
2512                (DTK_TZ, "+05:30".to_string()),
2513            ]
2514        );
2515        assert_eq!(fields("1997.038"), [(DTK_NUMBER, "1997.038".to_string())]);
2516        assert_eq!(fields("America/New_York"), [(DTK_DATE, "america/new_york".to_string())]);
2517        assert_eq!(
2518            fields("j2451187"),
2519            [(DTK_STRING, "j".to_string()), (DTK_NUMBER, "2451187".to_string())]
2520        );
2521        assert_eq!(fields("- infinity"), [(DTK_SPECIAL, "-infinity".to_string())]);
2522        assert_eq!(parse_date_time("1 \u{e9}".as_bytes(), 256).err(), Some(BadFormat));
2523    }
2524
2525    #[test]
2526    fn the_zone_abbreviations_parse() {
2527        let abbrevs = ZoneAbbrevs::postgres_default();
2528        assert!(abbrevs.len() > 150);
2529        assert_eq!(
2530            abbrevs.get("est"),
2531            Some(("est", &Abbrev::Fixed { offset: -18000, dst: false }))
2532        );
2533        assert_eq!(abbrevs.get("pdt"), Some(("pdt", &Abbrev::Fixed { offset: -25200, dst: true })));
2534        assert_eq!(abbrevs.get("msk"), Some(("msk", &Abbrev::Zone("Europe/Moscow".to_string()))));
2535        assert!(ZoneAbbrevs::parse("TOOLONGABBREV 0").is_err());
2536        assert!(ZoneAbbrevs::parse("X 0 junk").is_err());
2537        assert!(ZoneAbbrevs::parse("X 0\nX 3600").is_err());
2538    }
2539
2540    #[test]
2541    fn the_input_reads_the_common_forms() {
2542        let utc = FixedZone::utc();
2543        let cx = DateTimeInput {
2544            order: DateOrder::Mdy,
2545            zone: &utc,
2546            zones: &NoZones,
2547            abbrevs: ZoneAbbrevs::postgres_default(),
2548            now: 0,
2549        };
2550        assert_eq!(date_in("2000-01-02", &cx), Ok(1));
2551        assert_eq!(date_in("today", &cx), Ok(0));
2552        assert_eq!(timestamp_in("2000-01-01 00:00:01.5", -1, &cx), Ok(1_500_000));
2553        assert_eq!(timestamptz_in("2000-01-01 00:00 EST", -1, &cx), Ok(5 * USECS_PER_HOUR));
2554        assert_eq!(time_in("allballs", -1, &cx), Ok(0));
2555        assert_eq!(
2556            timetz_in("04:05 -08", -1, &cx),
2557            Ok((4 * USECS_PER_HOUR + 5 * USECS_PER_MINUTE, 28800))
2558        );
2559        let day = Interval { time: 0, day: 1, month: 0 };
2560        assert_eq!(interval_in("1 day", -1, IntervalStyle::Postgres), Ok(day));
2561        assert_eq!(interval_in("P1D", -1, IntervalStyle::Postgres), Ok(day));
2562        let error = date_in("2000-13-01", &cx).unwrap_err();
2563        assert_eq!(
2564            error.hint.as_deref(),
2565            Some("Perhaps you need a different \"DateStyle\" setting.")
2566        );
2567    }
2568
2569    #[test]
2570    fn the_iso_date_path_agrees_with_the_parser() {
2571        let utc = FixedZone::utc();
2572        for order in [DateOrder::Mdy, DateOrder::Dmy, DateOrder::Ymd] {
2573            let cx = DateTimeInput {
2574                order,
2575                zone: &utc,
2576                zones: &NoZones,
2577                abbrevs: ZoneAbbrevs::postgres_default(),
2578                now: 0,
2579            };
2580            for year in [1, 4, 100, 1582, 1900, 1999, 2000, 2024, 2100, 9999] {
2581                for month in 0..=13 {
2582                    for day in 0..=32 {
2583                        let input = format!("{year:04}-{month:02}-{day:02}");
2584                        match iso_date(input.as_bytes()) {
2585                            Some(date) => assert_eq!(parsed_date(&input, &cx), Ok(date), "{input}"),
2586                            None => assert!(parsed_date(&input, &cx).is_err(), "{input}"),
2587                        }
2588                    }
2589                }
2590            }
2591        }
2592    }
2593}