1use 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
24pub trait ZoneLookup {
26 fn zone(&self, name: &str) -> Option<Arc<dyn TimeZone + Send + Sync>>;
27}
28
29#[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#[derive(Debug, Clone, PartialEq, Eq)]
41pub enum Abbrev {
42 Fixed { offset: i32, dst: bool },
44 Zone(String),
46}
47
48#[derive(Debug, Clone, Default, PartialEq, Eq)]
50pub struct ZoneAbbrevs {
51 entries: Vec<(String, Abbrev)>,
52}
53
54impl ZoneAbbrevs {
55 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 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 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 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#[derive(Clone, Copy)]
152pub struct DateTimeInput<'a> {
153 pub order: DateOrder,
155 pub zone: &'a dyn TimeZone,
157 pub zones: &'a dyn ZoneLookup,
159 pub abbrevs: &'a ZoneAbbrevs,
161 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
175const 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
183const 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;
213const DTK_DATE_VALUE: i32 = 2;
215const DTK_TIME_VALUE: i32 = 3;
216const DTK_TZ_VALUE: i32 = 4;
218
219const 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;
262const 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#[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 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
330fn truncate(key: &[u8]) -> &[u8] {
332 &key[..key.len().min(TOKMAXLEN)]
333}
334
335fn 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
387fn 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#[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 Epoch,
439 Reserved,
441 Unknown,
443}
444
445impl DateUnit {
446 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 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 pub fn is_units(self) -> bool {
491 !matches!(self, DateUnit::Epoch | DateUnit::Reserved | DateUnit::Unknown)
492 }
493}
494
495fn at(s: &[u8], i: usize) -> u8 {
497 s.get(i).copied().unwrap_or(0)
498}
499
500fn text(field: &[u8]) -> &str {
502 std::str::from_utf8(field).unwrap_or_default()
503}
504
505fn 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
511fn 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
545fn 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
551fn atoi(s: &[u8]) -> i32 {
553 strtol(s, 0).0 as i32
554}
555
556fn 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
570fn parse_fractional_second(s: &[u8]) -> Result<i32, DtErr> {
572 Ok((parse_fraction(s)? * 1e6).round_ties_even() as i32)
573}
574
575struct 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
590fn 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 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 pos += 1;
736 }
737 Ok(out)
738}
739
740#[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
752struct Decoded {
754 dtype: i32,
755 tm: Tm,
756 fsec: i32,
757 tz: i32,
758}
759
760enum 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
784fn is_valid_julian(year: i32, month: i32) -> bool {
786 (year > -4713 || (year == -4713 && month >= 11))
787 && (year < 5874898 || (year == 5874898 && month < 6))
788}
789
790fn 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
796fn 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
806fn 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
820fn 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
833fn 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
842pub 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
859fn 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
868fn 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
893pub enum ZoneName {
895 Fixed(i32),
897 Dynamic(Option<Arc<dyn TimeZone + Send + Sync>>),
899 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
913pub 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
928pub 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
954pub 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
974fn 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
1012fn 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
1065fn 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
1073fn 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
1115fn 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 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 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
1200fn 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 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
1278fn 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 } 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
1322fn 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
1335fn 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 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 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 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 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 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
1610fn 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 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#[derive(Debug, Clone, Copy, Default)]
1853struct Itm {
1854 usec: i64,
1855 mday: i32,
1856 mon: i32,
1857 year: i32,
1858}
1859
1860impl Itm {
1861 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 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 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 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 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 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 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
1933fn 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
1948fn 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
1959fn 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
1977fn 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 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 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 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 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 if i != nf - 1 {
2139 return Err(BadFormat);
2140 }
2141 is_before = true;
2142 ty = uval;
2143 0
2144 }
2145 RESERV => {
2146 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
2179fn 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
2196fn 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
2202fn 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 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 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 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 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
2335fn 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
2351pub 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
2359fn 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
2387fn 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
2404fn 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
2419pub 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
2425pub 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
2460pub fn timestamp_in(input: &str, typmod: i32, cx: &DateTimeInput<'_>) -> Result<i64, TypeError> {
2462 timestamp_value(input, typmod, cx, false)
2463}
2464
2465pub fn timestamptz_in(input: &str, typmod: i32, cx: &DateTimeInput<'_>) -> Result<i64, TypeError> {
2467 timestamp_value(input, typmod, cx, true)
2468}
2469
2470pub 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}