1use crate::error::{DtErr, DtErrKind};
2use crate::{Dt, Meridiem, Offset, TimeParts, Weekday, an_err};
3use core::result::Result;
4use core::str;
5
6#[cfg(feature = "alloc")]
7use crate::Scale;
8
9#[derive(Debug, Clone, Copy)]
21pub struct StrPTimeFmt {
22 fmt: [u8; Self::MAX_FORMAT_LEN],
23 len: usize,
24}
25
26impl StrPTimeFmt {
27 pub const MAX_FORMAT_LEN: usize = 256;
28
29 pub fn new(fmt: &str) -> Result<Self, DtErr> {
52 if fmt.len() > Self::MAX_FORMAT_LEN {
53 return Err(an_err!(
54 DtErrKind::UnexpectedEnd,
55 "format string too long (max {} bytes)",
56 Self::MAX_FORMAT_LEN
57 ));
58 }
59 let fmt = fmt.as_bytes();
60 if !fmt.is_ascii() {
61 return Err(an_err!(
62 DtErrKind::UnexpectedEnd,
63 "format string must be ASCII"
64 ));
65 }
66
67 Self::validate_format(fmt)?;
68
69 let mut buffer = [0u8; Self::MAX_FORMAT_LEN];
70 buffer[..fmt.len()].copy_from_slice(fmt);
71
72 Ok(Self {
73 fmt: buffer,
74 len: fmt.len(),
75 })
76 }
77
78 pub fn to_dt(
103 &self,
104 s: &str,
105 inp_can_end_before_fmt: bool,
106 fmt_can_end_before_inp: bool,
107 allow_partial_date: bool,
108 ) -> Result<Dt, DtErr> {
109 TimeParts::from_str(
110 self.as_str()?,
111 s,
112 inp_can_end_before_fmt,
113 fmt_can_end_before_inp,
114 allow_partial_date,
115 )
116 .and_then(|p| p.to_dt())
117 }
118
119 #[cfg(feature = "alloc")]
142 pub fn to_str(
143 &self,
144 s: &str,
145 output_fmt: &str,
146 inp_can_end_before_fmt: bool,
147 fmt_can_end_before_inp: bool,
148 allow_partial_date: bool,
149 ) -> Result<alloc::string::String, DtErr> {
150 self.to_dt(
151 s,
152 inp_can_end_before_fmt,
153 fmt_can_end_before_inp,
154 allow_partial_date,
155 )?
156 .to_str(Scale::TAI, output_fmt)
157 }
158
159 fn validate_format(mut fmt: &[u8]) -> Result<(), DtErr> {
160 while !fmt.is_empty() {
161 if fmt[0] != b'%' {
162 fmt = &fmt[1..];
164 continue;
165 }
166
167 if fmt.len() == 1 {
169 return Err(an_err!(DtErrKind::UnexpectedEnd, "after %"));
170 }
171 fmt = &fmt[1..]; let (_, _, _, new_fmt) = Parser::parse_format_extensions(fmt, 0);
175 fmt = new_fmt;
176
177 if fmt.is_empty() {
178 return Err(an_err!(DtErrKind::UnexpectedEnd, "expected directive"));
179 }
180
181 let directive = fmt[0];
182
183 match directive {
184 b'%' | b'A' | b'a' | b'B' | b'b' | b'h' | b'C' | b'd' | b'e' |
186 b'f' | b'N' | b'G' | b'g' | b'H' | b'k' | b'I' | b'l' | b'j' |
187 b'M' | b'm' | b'n' | b't' | b'P' | b'p' | b'Q' | b'S' | b's' |
188 b'U' | b'u' | b'V' | b'W' | b'w' | b'Y' | b'y' | b'z' |
189 b'F' | b'D' | b'T' | b'R' |
191 b'*' => {
193 fmt = &fmt[1..];
194 }
195
196 b'.' => {
197 fmt = &fmt[1..]; while !fmt.is_empty() && fmt[0].is_ascii_digit() {
202 fmt = &fmt[1..];
203 }
204
205 let next = fmt.first().copied().unwrap_or(0);
206 if !matches!(next, b'f' | b'N') {
207 return Err(an_err!(DtErrKind::BadFractional, "{}", char::from(next)));
208 }
209 fmt = &fmt[1..];
210 }
211
212 b'c' | b'r' | b'X' | b'x' | b'Z' => {
214 return Err(an_err!(
215 DtErrKind::UnsupportedDirective,
216 "{}",
217 char::from(directive)
218 ));
219 }
220
221 _ => {
222 return Err(an_err!(DtErrKind::UnknownDirective));
223 }
224 }
225 }
226
227 Ok(())
228 }
229
230 #[inline]
231 fn as_bytes(&self) -> &[u8] {
232 &self.fmt[..self.len]
233 }
234
235 #[inline]
236 fn as_str(&self) -> Result<&str, DtErr> {
237 match core::str::from_utf8(self.as_bytes()) {
238 Ok(f) => Ok(f),
239 Err(e) => Err(an_err!(DtErrKind::InvalidBytes, "{}", e)),
240 }
241 }
242}
243
244pub(crate) struct Parser<'f, 'i, 't> {
245 pub(crate) fmt: &'f [u8], pub(crate) inp: &'i [u8], tm: &'t mut TimeParts,
248 inp_can_end_before_fmt: bool,
249}
250
251impl<'f, 'i, 't> Parser<'f, 'i, 't> {
252 pub(crate) fn new(
253 fmt: &'f [u8],
254 inp: &'i [u8],
255 tm: &'t mut TimeParts,
256 inp_can_end_before_fmt: bool,
257 ) -> Self {
258 Self {
259 fmt,
260 inp,
261 tm,
262 inp_can_end_before_fmt,
263 }
264 }
265
266 #[inline]
267 fn current_format_byte(&self) -> u8 {
268 self.fmt[0]
269 }
270
271 #[inline]
272 fn current_input_byte(&self) -> u8 {
273 self.inp[0]
274 }
275
276 #[inline]
277 fn advance_format(&mut self) -> bool {
278 self.fmt = &self.fmt[1..];
279 !self.fmt.is_empty()
280 }
281
282 #[inline]
283 fn advance_input(&mut self) -> bool {
284 self.inp = &self.inp[1..];
285 !self.inp.is_empty()
286 }
287
288 pub(crate) fn parse(&mut self) -> Result<(), DtErr> {
289 while !self.fmt.is_empty() {
290 if self.current_format_byte() != b'%' {
291 self.parse_literal_character()?;
292 continue;
293 }
294 if !self.advance_format() {
295 return Err(an_err!(DtErrKind::UnexpectedEnd, "after %"));
296 }
297
298 let (flag, width, colons, new_fmt) = Self::parse_format_extensions(self.fmt, 0);
299 self.fmt = new_fmt;
300
301 let directive = self.fmt.first().copied().unwrap_or(0);
302
303 if self.inp.is_empty() {
304 if self.inp_can_end_before_fmt {
305 if !matches!(directive, b'.' | b'f' | b'N') {
306 return Err(an_err!(DtErrKind::UnexpectedEnd, "input exhausted"));
307 }
308 } else {
309 return Ok(());
310 }
311 }
312
313 match directive {
314 b'%' => self.parse_percent_sign()?,
315 b'A' => self.parse_weekday_full()?,
316 b'a' => self.parse_weekday_abbrev()?,
317 b'B' => self.parse_month_name_full()?,
318 b'b' | b'h' => self.parse_month_name_abbrev()?,
319 b'C' => self.parse_century(flag, width, colons)?,
320 b'd' | b'e' => self.parse_day_of_month(flag, width, colons, true)?,
321 b'f' | b'N' => {
322 self.parse_fractional_seconds(flag, width, colons)?;
323 self.advance_format();
324 }
325 b'G' => self.parse_iso_week_year(flag, width, colons)?,
326 b'g' => self.parse_two_digit_iso_week_year(flag, width, colons)?,
327 b'H' | b'k' => self.parse_hour24(flag, width, colons, true)?,
328 b'I' | b'l' => self.parse_hour12(flag, width, colons)?,
329 b'j' => self.parse_day_of_year(flag, width, colons)?,
330 b'M' => self.parse_minute(flag, width, colons, true)?,
331 b'm' => self.parse_month_number(flag, width, colons, true)?,
332 b'n' | b't' => self.skip_whitespace()?,
333 b'P' | b'p' => self.parse_ampm()?,
334 b'Q' => self.parse_iana_or_offset(flag, width, colons)?,
335 b'S' => self.parse_second(flag, width, colons, true)?,
336 b's' => self.parse_unix_timestamp(flag, width, colons)?,
337 b'U' => self.parse_week_number_sunday_based(flag, width, colons)?,
338 b'u' => self.parse_weekday_number_monday_based(flag, width, colons)?,
339 b'V' => self.parse_week_iso(flag, width, colons)?,
340 b'W' => self.parse_week_number_monday_based(flag, width, colons)?,
341 b'w' => self.parse_weekday_number_sunday_based(flag, width, colons)?,
342 b'Y' => self.parse_full_year(flag, width, colons, true)?,
343 b'y' => self.parse_two_digit_year(flag, width, colons, true)?,
344 b'z' => self.parse_timezone_offset(flag, width, colons)?,
345 b'.' => {
346 if !self.advance_format() {
347 return Err(an_err!(DtErrKind::UnexpectedEnd, "after ."));
348 }
349
350 let width = if !self.fmt.is_empty()
351 && self.current_format_byte().is_ascii_digit()
352 {
353 let start = self.fmt;
354 while !self.fmt.is_empty() && self.current_format_byte().is_ascii_digit() {
355 self.advance_format();
356 }
357 core::str::from_utf8(&start[..start.len() - self.fmt.len()])
358 .ok()
359 .and_then(|s| s.parse::<u8>().ok())
360 } else {
361 None
362 };
363
364 let next: u8 = self.fmt.first().copied().unwrap_or(0);
365 if !matches!(next, b'f' | b'N') {
366 return Err(an_err!(DtErrKind::BadFractional, "{}", char::from(next)));
367 }
368 self.advance_format();
369
370 self.parse_optional_dot_fractional(flag, width, colons)?;
371 }
372 b'F' => self.parse_iso_date()?,
374 b'D' => self.parse_us_date_shortcut()?,
375 b'T' => self.parse_time_with_seconds_shortcut()?,
376 b'R' => self.parse_time_without_seconds_shortcut()?,
377 b'*' => self.parse_unbounded_year()?,
379 b'c' | b'r' | b'X' | b'x' | b'Z' => {
381 return Err(an_err!(
382 DtErrKind::UnsupportedDirective,
383 "{}",
384 char::from(directive)
385 ));
386 }
387 _ => {
388 return Err(an_err!(DtErrKind::UnknownDirective));
389 }
390 }
391 }
392 Ok(())
393 }
394
395 fn parse_literal_character(&mut self) -> Result<(), DtErr> {
396 let c = self.current_format_byte();
397 if c.is_ascii_whitespace() {
398 while !self.inp.is_empty() && self.current_input_byte().is_ascii_whitespace() {
399 self.advance_input();
400 }
401 } else if self.inp.is_empty() || self.current_input_byte() != c {
402 return Err(an_err!(DtErrKind::MismatchedLiteral, "literal"));
403 } else {
404 self.advance_input();
405 }
406 self.advance_format();
407 Ok(())
408 }
409
410 #[inline]
411 fn skip_whitespace(&mut self) -> Result<(), DtErr> {
412 while !self.inp.is_empty() && self.current_input_byte().is_ascii_whitespace() {
413 self.advance_input();
414 }
415 self.advance_format();
416 Ok(())
417 }
418
419 #[inline]
420 fn parse_percent_sign(&mut self) -> Result<(), DtErr> {
421 if self.inp.is_empty() || self.current_input_byte() != b'%' {
422 return Err(an_err!(
423 DtErrKind::MismatchedLiteral,
424 "% got: {}",
425 char::from(self.current_input_byte())
426 ));
427 }
428 self.advance_input();
429 self.advance_format();
430 Ok(())
431 }
432
433 #[inline]
434 fn parse_optional_dot_fractional(
435 &mut self,
436 flag: Option<u8>,
437 width: Option<u8>,
438 colons: u8,
439 ) -> Result<(), DtErr> {
440 if !self.inp.is_empty() && self.current_input_byte() == b'.' {
443 self.advance_input();
444 }
445 self.parse_fractional_seconds(flag, width, colons)?;
446 Ok(())
447 }
448
449 #[inline]
450 fn parse_full_year(
451 &mut self,
452 flag: Option<u8>,
453 width: Option<u8>,
454 _colons: u8,
455 advance: bool,
456 ) -> Result<(), DtErr> {
457 let (y, remaining) = match Self::parse_padded_i64(self.inp, flag, width, 4, b'0') {
458 Ok(v) => v,
459 Err(_) => return Err(an_err!(DtErrKind::ExpectedValue, "year")),
460 };
461 self.tm.yr = Some(y);
462 self.inp = remaining;
463 if advance {
464 self.advance_format();
465 }
466 Ok(())
467 }
468
469 #[inline]
470 fn parse_unbounded_year(&mut self) -> Result<(), DtErr> {
471 let (y, remaining) = match Self::parse_arbitrary_i64(self.inp) {
472 Ok(v) => v,
473 Err(_) => return Err(an_err!(DtErrKind::ExpectedValue, "year")),
474 };
475 self.tm.yr = Some(y);
476 self.inp = remaining;
477 self.advance_format();
478 Ok(())
479 }
480
481 #[inline]
482 fn parse_two_digit_year(
483 &mut self,
484 flag: Option<u8>,
485 width: Option<u8>,
486 _colons: u8,
487 advance: bool,
488 ) -> Result<(), DtErr> {
489 let (y, remaining) = match Self::parse_u8_padded(self.inp, flag, width, 2, b'0') {
490 Ok(v) => v,
491 Err(_) => return Err(an_err!(DtErrKind::ExpectedValue, "year")),
492 };
493 self.inp = remaining;
494 let year = if y <= 68 {
495 2000i64 + (y as i64)
496 } else {
497 1900i64 + (y as i64)
498 };
499 self.tm.yr = Some(year);
500 if advance {
501 self.advance_format();
502 }
503 Ok(())
504 }
505
506 #[inline]
507 fn parse_century(
508 &mut self,
509 flag: Option<u8>,
510 width: Option<u8>,
511 _colons: u8,
512 ) -> Result<(), DtErr> {
513 let (sign, after_sign) = Self::parse_optional_sign(self.inp);
514 let (c, remaining) = match Self::parse_padded_i64(after_sign, flag, width, 2, b'_') {
515 Ok(v) => v,
516 Err(_) => return Err(an_err!(DtErrKind::ExpectedValue, "century")),
517 };
518 self.inp = remaining;
519 let year = if sign < 0 { -c * 100 } else { c * 100 };
520 self.tm.yr = Some(year);
521 self.advance_format();
522 Ok(())
523 }
524
525 #[inline]
526 fn parse_iso_week_year(
527 &mut self,
528 flag: Option<u8>,
529 width: Option<u8>,
530 _colons: u8,
531 ) -> Result<(), DtErr> {
532 let (y, remaining) = match Self::parse_padded_i64(self.inp, flag, width, 4, b'0') {
533 Ok(v) => v,
534 Err(_) => return Err(an_err!(DtErrKind::ExpectedValue, "iso week year")),
535 };
536 self.tm.iso_wk_yr = Some(y);
537 self.inp = remaining;
538 self.advance_format();
539 Ok(())
540 }
541
542 #[inline]
543 fn parse_two_digit_iso_week_year(
544 &mut self,
545 flag: Option<u8>,
546 width: Option<u8>,
547 _colons: u8,
548 ) -> Result<(), DtErr> {
549 let (y, remaining) = match Self::parse_u8_padded(self.inp, flag, width, 2, b'0') {
550 Ok(v) => v,
551 Err(_) => {
552 return Err(an_err!(DtErrKind::ExpectedValue, "iso week year"));
553 }
554 };
555 self.inp = remaining;
556 let year = if y <= 68 {
557 2000i64 + (y as i64)
558 } else {
559 1900i64 + (y as i64)
560 };
561 self.tm.iso_wk_yr = Some(year);
562 self.advance_format();
563 Ok(())
564 }
565
566 #[inline]
567 fn parse_month_number(
568 &mut self,
569 flag: Option<u8>,
570 width: Option<u8>,
571 _colons: u8,
572 advance: bool,
573 ) -> Result<(), DtErr> {
574 let (m, remaining) = match Self::parse_u8_padded(self.inp, flag, width, 2, b'0') {
575 Ok(v) => v,
576 Err(_) => return Err(an_err!(DtErrKind::ExpectedValue, "digit month")),
577 };
578 if !(1..=12).contains(&m) {
579 return Err(an_err!(DtErrKind::OutOfRange, "month (1..=12): {}", m));
580 }
581 self.tm.mo = Some(m);
582 self.inp = remaining;
583 if advance {
584 self.advance_format();
585 }
586 Ok(())
587 }
588
589 #[inline]
590 fn parse_day_of_month(
591 &mut self,
592 flag: Option<u8>,
593 width: Option<u8>,
594 _colons: u8,
595 advance: bool,
596 ) -> Result<(), DtErr> {
597 let (d, remaining) = match Self::parse_u8_padded(self.inp, flag, width, 2, b'0') {
598 Ok(v) => v,
599 Err(_) => return Err(an_err!(DtErrKind::ExpectedValue, "day")),
600 };
601 if !(1..=31).contains(&d) {
602 return Err(an_err!(DtErrKind::OutOfRange, "day (1..=31): {}", d));
603 }
604 self.tm.day = Some(d);
605 self.inp = remaining;
606 if advance {
607 self.advance_format();
608 }
609 Ok(())
610 }
611
612 #[inline]
613 fn parse_day_of_year(
614 &mut self,
615 flag: Option<u8>,
616 width: Option<u8>,
617 _colons: u8,
618 ) -> Result<(), DtErr> {
619 let (n, remaining) = match Self::parse_padded_number(self.inp, flag, width, 3, b'0') {
620 Ok(v) => v,
621 Err(_) => return Err(an_err!(DtErrKind::ExpectedValue, "day of year")),
622 };
623 let day = n as u16;
624 if !(1..=366).contains(&day) {
625 return Err(an_err!(
626 DtErrKind::OutOfRange,
627 "day of year (1..=366): {}",
628 day
629 ));
630 }
631 self.tm.day_of_yr = Some(day);
632 self.inp = remaining;
633 self.advance_format();
634 Ok(())
635 }
636
637 #[inline]
638 fn parse_hour24(
639 &mut self,
640 flag: Option<u8>,
641 width: Option<u8>,
642 _colons: u8,
643 advance: bool,
644 ) -> Result<(), DtErr> {
645 let (h, remaining) = match Self::parse_u8_padded(self.inp, flag, width, 2, b'0') {
646 Ok(v) => v,
647 Err(_) => return Err(an_err!(DtErrKind::ExpectedValue, "hour")),
648 };
649 if h > 23 {
650 return Err(an_err!(DtErrKind::OutOfRange, "hour (0..=23): {}", h));
651 }
652 self.tm.hr = Some(h);
653 self.inp = remaining;
654 if advance {
655 self.advance_format();
656 }
657 Ok(())
658 }
659
660 #[inline]
661 fn parse_hour12(
662 &mut self,
663 flag: Option<u8>,
664 width: Option<u8>,
665 _colons: u8,
666 ) -> Result<(), DtErr> {
667 let (h, remaining) = match Self::parse_u8_padded(self.inp, flag, width, 2, b'0') {
668 Ok(v) => v,
669 Err(_) => return Err(an_err!(DtErrKind::ExpectedValue, "hour")),
670 };
671 if !(1..=12).contains(&h) {
672 return Err(an_err!(DtErrKind::OutOfRange, "hour (1..=12): {}", h));
673 }
674 self.tm.hr = Some(h);
675 self.inp = remaining;
676 self.advance_format();
677 Ok(())
678 }
679
680 #[inline]
681 fn parse_minute(
682 &mut self,
683 flag: Option<u8>,
684 width: Option<u8>,
685 _colons: u8,
686 advance: bool,
687 ) -> Result<(), DtErr> {
688 let (m, remaining) = match Self::parse_u8_padded(self.inp, flag, width, 2, b'0') {
689 Ok(v) => v,
690 Err(_) => return Err(an_err!(DtErrKind::ExpectedValue, "minute")),
691 };
692 if m > 59 {
693 return Err(an_err!(DtErrKind::OutOfRange, "minute (0..=59): {}", m));
694 }
695 self.tm.min = Some(m);
696 self.inp = remaining;
697 if advance {
698 self.advance_format();
699 }
700 Ok(())
701 }
702
703 #[inline]
704 fn parse_second(
705 &mut self,
706 flag: Option<u8>,
707 width: Option<u8>,
708 _colons: u8,
709 advance: bool,
710 ) -> Result<(), DtErr> {
711 let (s, remaining) = match Self::parse_u8_padded(self.inp, flag, width, 2, b'0') {
712 Ok(v) => v,
713 Err(_) => return Err(an_err!(DtErrKind::ExpectedValue, "seconds")),
714 };
715 if s > 60 {
716 return Err(an_err!(DtErrKind::OutOfRange, "seconds (0..=60): {}", s));
717 }
718 self.tm.sec = Some(s);
719 self.tm.is_leap_sec = s == 60;
720 self.inp = remaining;
721 if advance {
722 self.advance_format();
723 }
724 Ok(())
725 }
726
727 #[inline]
728 fn parse_fractional_seconds(
729 &mut self,
730 _flag: Option<u8>,
731 width: Option<u8>,
732 _colons: u8,
733 ) -> Result<(), DtErr> {
734 if !self.inp.is_empty() && self.current_input_byte() == b'.' {
737 self.advance_input();
738 }
739 let max_digits = width.map(|w| w as usize).unwrap_or(usize::MAX);
740 const TARGET_DIGITS: usize = 18; let mut frac: u64 = 0;
742 let mut digits_read = 0usize;
743 while !self.inp.is_empty()
744 && self.current_input_byte().is_ascii_digit()
745 && digits_read < max_digits
746 {
747 if digits_read < TARGET_DIGITS {
748 let d = (self.current_input_byte() - b'0') as u64;
749 frac = frac * 10 + d;
750 }
751 self.advance_input();
752 digits_read += 1;
753 }
754 if digits_read == 0 {
755 return Err(an_err!(DtErrKind::ExpectedValue, "frac seconds"));
756 }
757 let attos = if digits_read >= TARGET_DIGITS {
758 frac
759 } else {
760 let multiplier = 10u64.pow((TARGET_DIGITS - digits_read) as u32);
761 frac * multiplier
762 };
763 self.tm.attos = Some(attos);
764 Ok(())
765 }
766
767 #[inline]
768 fn parse_unix_timestamp(
769 &mut self,
770 flag: Option<u8>,
771 width: Option<u8>,
772 _colons: u8,
773 ) -> Result<(), DtErr> {
774 let (sign, after_sign) = Self::parse_optional_sign(self.inp);
775 let (n, remaining) = match Self::parse_padded_number(after_sign, flag, width, 19, b' ') {
776 Ok(v) => v,
777 Err(_) => return Err(an_err!(DtErrKind::ExpectedValue, "timestamp")),
778 };
779 let timestamp = if sign < 0 {
780 match n.checked_neg() {
781 Some(ts) => ts,
782 None => return Err(an_err!(DtErrKind::OutOfRange, "timestamp")),
783 }
784 } else {
785 n
786 };
787 self.tm.unix_timestamp_seconds = Some(timestamp);
788 self.inp = remaining;
789 self.advance_format();
790 Ok(())
791 }
792
793 #[inline]
794 fn parse_month_name_abbrev(&mut self) -> Result<(), DtErr> {
795 if self.inp.len() < 3 {
796 return Err(an_err!(DtErrKind::InvalidName, "abbrev. month name"));
797 }
798 let x = &self.inp[..3];
799 let candidate = [
800 x[0].to_ascii_lowercase(),
801 x[1].to_ascii_lowercase(),
802 x[2].to_ascii_lowercase(),
803 ];
804 let index = match &candidate {
805 b"jan" => 0,
806 b"feb" => 1,
807 b"mar" => 2,
808 b"apr" => 3,
809 b"may" => 4,
810 b"jun" => 5,
811 b"jul" => 6,
812 b"aug" => 7,
813 b"sep" => 8,
814 b"oct" => 9,
815 b"nov" => 10,
816 b"dec" => 11,
817 _ => {
818 return Err(an_err!(DtErrKind::InvalidName, "abbrev. month name"));
819 }
820 };
821 self.inp = &self.inp[3..];
822 self.tm.mo = Some(index + 1);
823 self.advance_format();
824 Ok(())
825 }
826
827 #[inline]
828 fn parse_month_name_full(&mut self) -> Result<(), DtErr> {
829 static CHOICES: &[&[u8]] = &[
830 b"January",
831 b"February",
832 b"March",
833 b"April",
834 b"May",
835 b"June",
836 b"July",
837 b"August",
838 b"September",
839 b"October",
840 b"November",
841 b"December",
842 ];
843 let (index, remaining) = match Self::match_from_choice_list(self.inp, CHOICES) {
844 Ok(v) => v,
845 Err(_) => return Err(an_err!(DtErrKind::InvalidName, "month name")),
846 };
847 self.inp = remaining;
848 self.tm.mo = Some(index + 1);
849 self.advance_format();
850 Ok(())
851 }
852
853 #[inline]
854 fn parse_weekday_abbrev(&mut self) -> Result<(), DtErr> {
855 if self.inp.len() < 3 {
856 return Err(an_err!(DtErrKind::InvalidName, "abbrev. weekday"));
857 }
858 let x = &self.inp[..3];
859 let candidate = [
860 x[0].to_ascii_lowercase(),
861 x[1].to_ascii_lowercase(),
862 x[2].to_ascii_lowercase(),
863 ];
864 let index = match &candidate {
865 b"sun" => 0,
866 b"mon" => 1,
867 b"tue" => 2,
868 b"wed" => 3,
869 b"thu" => 4,
870 b"fri" => 5,
871 b"sat" => 6,
872 _ => {
873 return Err(an_err!(DtErrKind::InvalidName, "abbrev. weekday"));
874 }
875 };
876 self.inp = &self.inp[3..];
877 self.tm.wkday = Some(
878 Weekday::from_sunday_zero_offset(index)
879 .ok_or_else(|| an_err!(DtErrKind::InvalidName, "abbrev. weekday"))?,
880 );
881 self.advance_format();
882 Ok(())
883 }
884
885 #[inline]
886 fn parse_weekday_full(&mut self) -> Result<(), DtErr> {
887 static CHOICES: &[&[u8]] = &[
888 b"Sunday",
889 b"Monday",
890 b"Tuesday",
891 b"Wednesday",
892 b"Thursday",
893 b"Friday",
894 b"Saturday",
895 ];
896 let (index, remaining) = match Self::match_from_choice_list(self.inp, CHOICES) {
897 Ok(v) => v,
898 Err(_) => return Err(an_err!(DtErrKind::InvalidName, "weekday")),
899 };
900 self.inp = remaining;
901 self.tm.wkday = Some(
902 Weekday::from_sunday_zero_offset(index)
903 .ok_or_else(|| an_err!(DtErrKind::InvalidName, "weekday"))?,
904 );
905 self.advance_format();
906 Ok(())
907 }
908
909 #[inline]
910 fn parse_weekday_number_monday_based(
911 &mut self,
912 flag: Option<u8>,
913 width: Option<u8>,
914 _colons: u8,
915 ) -> Result<(), DtErr> {
916 let (w, remaining) = match Self::parse_u8_padded(self.inp, flag, width, 1, b'_') {
917 Ok(v) => v,
918 Err(_) => {
919 return Err(an_err!(
920 DtErrKind::ExpectedValue,
921 "monday based weekday number"
922 ));
923 }
924 };
925 let wd = Weekday::from_monday_one_offset(w)
926 .ok_or_else(|| an_err!(DtErrKind::OutOfRange, "monday based weekday number"))?;
927 self.tm.wkday = Some(wd);
928 self.inp = remaining;
929 self.advance_format();
930 Ok(())
931 }
932
933 #[inline]
934 fn parse_weekday_number_sunday_based(
935 &mut self,
936 flag: Option<u8>,
937 width: Option<u8>,
938 _colons: u8,
939 ) -> Result<(), DtErr> {
940 let (w, remaining) = match Self::parse_u8_padded(self.inp, flag, width, 1, b'_') {
941 Ok(v) => v,
942 Err(_) => {
943 return Err(an_err!(
944 DtErrKind::ExpectedValue,
945 "sunday based weekday number"
946 ));
947 }
948 };
949 let wd = Weekday::from_sunday_zero_offset(w)
950 .ok_or_else(|| an_err!(DtErrKind::OutOfRange, "sunday based weekday number"))?;
951 self.tm.wkday = Some(wd);
952 self.inp = remaining;
953 self.advance_format();
954 Ok(())
955 }
956
957 #[inline]
958 fn parse_ampm(&mut self) -> Result<(), DtErr> {
959 if self.inp.len() < 2 {
960 return Err(an_err!(DtErrKind::InvalidName, "am/pm"));
961 }
962 let slice = &self.inp[..2];
963 self.tm.meridiem = Some(if slice.eq_ignore_ascii_case(b"am") {
964 Meridiem::AM
965 } else if slice.eq_ignore_ascii_case(b"pm") {
966 Meridiem::PM
967 } else {
968 return Err(an_err!(DtErrKind::InvalidName, "am/pm"));
969 });
970 self.inp = &self.inp[2..];
971 self.advance_format();
972 Ok(())
973 }
974
975 #[inline]
976 fn parse_week_number_sunday_based(
977 &mut self,
978 flag: Option<u8>,
979 width: Option<u8>,
980 _colons: u8,
981 ) -> Result<(), DtErr> {
982 let (w, remaining) = match Self::parse_u8_padded(self.inp, flag, width, 2, b'0') {
983 Ok(v) => v,
984 Err(_) => {
985 return Err(an_err!(
986 DtErrKind::ExpectedValue,
987 "week number sunday based"
988 ));
989 }
990 };
991 self.tm.wk_sun = Some(w);
992 self.inp = remaining;
993 self.advance_format();
994 Ok(())
995 }
996
997 #[inline]
998 fn parse_week_number_monday_based(
999 &mut self,
1000 flag: Option<u8>,
1001 width: Option<u8>,
1002 _colons: u8,
1003 ) -> Result<(), DtErr> {
1004 let (w, remaining) = match Self::parse_u8_padded(self.inp, flag, width, 2, b'0') {
1005 Ok(v) => v,
1006 Err(_) => {
1007 return Err(an_err!(
1008 DtErrKind::ExpectedValue,
1009 "week number monday based"
1010 ));
1011 }
1012 };
1013 self.tm.wk_mon = Some(w);
1014 self.inp = remaining;
1015 self.advance_format();
1016 Ok(())
1017 }
1018
1019 #[inline]
1020 fn parse_week_iso(
1021 &mut self,
1022 flag: Option<u8>,
1023 width: Option<u8>,
1024 _colons: u8,
1025 ) -> Result<(), DtErr> {
1026 let (w, remaining) = match Self::parse_u8_padded(self.inp, flag, width, 2, b'0') {
1027 Ok(v) => v,
1028 Err(_) => return Err(an_err!(DtErrKind::ExpectedValue, "iso week")),
1029 };
1030 if !(1..=53).contains(&w) {
1031 return Err(an_err!(DtErrKind::OutOfRange, "iso week (1..=53): {}", w));
1032 }
1033 self.tm.iso_wk = Some(w);
1034 self.inp = remaining;
1035 self.advance_format();
1036 Ok(())
1037 }
1038
1039 #[inline]
1040 fn parse_timezone_offset(
1041 &mut self,
1042 _flag: Option<u8>,
1043 _width: Option<u8>,
1044 colons: u8,
1045 ) -> Result<(), DtErr> {
1046 let sign = match self.inp.first() {
1047 Some(b'+') => 1i32,
1048 Some(b'-') => -1i32,
1049 _ => {
1050 return Err(an_err!(
1051 DtErrKind::InvalidTimezoneOffset,
1052 "must start with + or -"
1053 ));
1054 }
1055 };
1056 self.advance_input();
1057
1058 let mut total_seconds = self.parse_offset_hours()? * 3600;
1059
1060 match colons {
1061 0 => {
1062 let minutes = match self.parse_offset_mm_ss() {
1063 Ok(m) => m,
1064 Err(_) => {
1065 return Err(an_err!(DtErrKind::InvalidTimezoneOffset, "minutes"));
1066 }
1067 };
1068 total_seconds += minutes * 60;
1069 if self.inp.len() >= 2
1070 && let Ok(seconds) = self.parse_offset_mm_ss()
1071 {
1072 total_seconds += seconds;
1073 }
1074 }
1075 1..=3 => {
1076 let minutes_required = colons != 3;
1077 if self.inp.first() == Some(&b':') {
1078 self.advance_input();
1079 let minutes = match self.parse_offset_mm_ss() {
1080 Ok(m) => m,
1081 Err(_) => {
1082 return Err(an_err!(DtErrKind::InvalidTimezoneOffset, "minutes"));
1083 }
1084 };
1085 total_seconds += minutes * 60;
1086 if self.inp.first() == Some(&b':') {
1087 self.advance_input();
1088 let seconds = match self.parse_offset_mm_ss() {
1089 Ok(s) => s,
1090 Err(_) => {
1091 return Err(an_err!(DtErrKind::InvalidTimezoneOffset, "seconds",));
1092 }
1093 };
1094 total_seconds += seconds;
1095 } else if colons == 2 {
1096 return Err(an_err!(DtErrKind::InvalidTimezoneOffset, "num colons"));
1097 }
1098 } else if minutes_required {
1099 return Err(an_err!(DtErrKind::InvalidTimezoneOffset, "num colons"));
1100 }
1101 }
1102 _ => {
1103 return Err(an_err!(DtErrKind::InvalidTimezoneOffset, "num colons"));
1104 }
1105 }
1106
1107 self.tm.offset = Some(Offset::Fixed(sign * total_seconds));
1109 self.advance_format();
1110 Ok(())
1111 }
1112
1113 #[inline]
1114 fn parse_offset_hours(&mut self) -> Result<i32, DtErr> {
1115 let mut n = 0i32;
1116 let mut digits = 0;
1117 while digits < 2 && !self.inp.is_empty() && self.current_input_byte().is_ascii_digit() {
1118 n = n * 10 + (self.current_input_byte() - b'0') as i32;
1119 self.advance_input();
1120 digits += 1;
1121 }
1122 if digits == 0 {
1123 return Err(an_err!(DtErrKind::InvalidTimezoneOffset, "hour"));
1124 }
1125 if n > 23 {
1126 return Err(an_err!(
1127 DtErrKind::InvalidTimezoneOffset,
1128 "hour (0..=23): {}",
1129 n
1130 ));
1131 }
1132 Ok(n)
1133 }
1134
1135 #[inline]
1136 fn parse_offset_mm_ss(&mut self) -> Result<i32, DtErr> {
1137 if self.inp.len() < 2 {
1138 return Err(an_err!(
1139 DtErrKind::InvalidTimezoneOffset,
1140 "minutes or seconds"
1141 ));
1142 }
1143 let slice = &self.inp[..2];
1144 let n = match core::str::from_utf8(slice)
1145 .ok()
1146 .and_then(|s| s.parse::<i32>().ok())
1147 .filter(|&n| (0..=59).contains(&n))
1148 {
1149 Some(n) => n,
1150 None => {
1151 return Err(an_err!(
1152 DtErrKind::InvalidTimezoneOffset,
1153 "minutes or seconds"
1154 ));
1155 }
1156 };
1157 self.inp = &self.inp[2..];
1158 Ok(n)
1159 }
1160
1161 #[inline]
1162 fn parse_iana_or_offset(
1163 &mut self,
1164 _flag: Option<u8>,
1165 _width: Option<u8>,
1166 colons: u8,
1167 ) -> Result<(), DtErr> {
1168 if !self.inp.is_empty() && matches!(self.current_input_byte(), b'+' | b'-') {
1169 return self.parse_timezone_offset(_flag, _width, colons);
1170 }
1171 let (iana_str, remaining) = match Self::parse_iana(self.inp) {
1172 Ok(v) => v,
1173 Err(_) => {
1174 return Err(an_err!(
1175 DtErrKind::InvalidTimezoneOffset,
1176 "expected iana or offset"
1177 ));
1178 }
1179 };
1180 let name_to_use = if iana_str.len() > 50 {
1181 &iana_str[0..50]
1182 } else {
1183 iana_str
1184 };
1185 self.tm.set_iana_name(Some(name_to_use));
1186 self.tm.offset = Some(Offset::None);
1187 self.inp = remaining;
1188 self.advance_format();
1189 Ok(())
1190 }
1191
1192 #[inline]
1215 fn parse_iso_date(&mut self) -> Result<(), DtErr> {
1216 self.parse_full_year(None, None, 0, false)?;
1217 self.parse_literal_character_byte(b'-')?;
1218 self.parse_month_number(None, None, 0, false)?;
1219 self.parse_literal_character_byte(b'-')?;
1220 self.parse_day_of_month(None, None, 0, false)?;
1221 self.advance_format(); Ok(())
1223 }
1224
1225 #[inline]
1226 fn parse_us_date_shortcut(&mut self) -> Result<(), DtErr> {
1227 self.parse_month_number(None, None, 0, false)?;
1228 self.parse_literal_character_byte(b'/')?;
1229 self.parse_day_of_month(None, None, 0, false)?;
1230 self.parse_literal_character_byte(b'/')?;
1231 self.parse_two_digit_year(None, None, 0, false)?;
1232 self.advance_format(); Ok(())
1234 }
1235
1236 #[inline]
1237 fn parse_time_with_seconds_shortcut(&mut self) -> Result<(), DtErr> {
1238 self.parse_hour24(None, None, 0, false)?;
1239 self.parse_literal_character_byte(b':')?;
1240 self.parse_minute(None, None, 0, false)?;
1241 self.parse_literal_character_byte(b':')?;
1242 self.parse_second(None, None, 0, false)?;
1243 self.advance_format(); Ok(())
1245 }
1246
1247 #[inline]
1248 fn parse_time_without_seconds_shortcut(&mut self) -> Result<(), DtErr> {
1249 self.parse_hour24(None, None, 0, false)?;
1250 self.parse_literal_character_byte(b':')?;
1251 self.parse_minute(None, None, 0, false)?;
1252 self.advance_format(); Ok(())
1254 }
1255
1256 #[inline]
1257 fn parse_literal_character_byte(&mut self, expected: u8) -> Result<(), DtErr> {
1258 if self.inp.is_empty() || self.current_input_byte() != expected {
1259 return Err(an_err!(
1260 DtErrKind::MismatchedLiteral,
1261 "Expected literal char"
1262 ));
1263 }
1264 self.advance_input();
1265 Ok(())
1266 }
1267
1268 #[inline]
1269 pub(crate) fn parse_format_extensions(
1270 fmt: &[u8],
1271 mut pos: usize,
1272 ) -> (Option<u8>, Option<u8>, u8, &[u8]) {
1273 let mut flag = None;
1274 let mut width = None;
1275 let mut colons = 0u8;
1276 if matches!(fmt.get(pos), Some(b'-' | b'_' | b'0' | b'^' | b'#')) {
1277 flag = Some(fmt[pos]);
1278 pos += 1;
1279 }
1280 if matches!(fmt.get(pos), Some(c) if c.is_ascii_digit()) {
1282 let mut w = 0u16;
1283 while pos < fmt.len() && fmt[pos].is_ascii_digit() {
1284 w = w * 10 + u16::from(fmt[pos] - b'0');
1285 pos += 1;
1286 }
1287 if w <= u8::MAX as u16 {
1288 width = Some(w as u8);
1289 }
1290 }
1291 while matches!(fmt.get(pos), Some(b':')) {
1293 colons += 1;
1294 pos += 1;
1295 }
1296 (flag, width, colons, &fmt[pos..])
1297 }
1298
1299 fn parse_optional_sign(inp: &[u8]) -> (i32, &[u8]) {
1300 if let Some(b'-') = inp.first() {
1301 (-1, &inp[1..])
1302 } else if let Some(b'+') = inp.first() {
1303 (1, &inp[1..])
1304 } else {
1305 (1, inp)
1306 }
1307 }
1308
1309 #[inline]
1310 fn parse_digits(inp: &[u8]) -> (&[u8], &[u8]) {
1311 let mut pos = 0;
1312 while pos < inp.len() && inp[pos].is_ascii_digit() {
1313 pos += 1;
1314 }
1315 (&inp[..pos], &inp[pos..])
1316 }
1317
1318 #[inline]
1319 fn parse_padded_number(
1320 inp: &[u8],
1321 flag: Option<u8>,
1322 width: Option<u8>,
1323 default_pad_width: usize,
1324 default_flag: u8,
1325 ) -> Result<(i64, &[u8]), ()> {
1326 let mut pos = 0;
1327 while pos < inp.len() && inp[pos].is_ascii_whitespace() {
1329 pos += 1;
1330 }
1331 if pos >= inp.len() {
1332 return Err(());
1333 }
1334 let effective_flag = match flag {
1336 Some(b'^') | Some(b'#') => default_flag,
1337 Some(f) => f,
1338 None => default_flag,
1339 };
1340 let zero_pad_width = match effective_flag {
1341 b'_' | b' ' | b'-' => 0, _ => width.map(usize::from).unwrap_or(default_pad_width),
1343 };
1344 let max_digits = default_pad_width.max(zero_pad_width);
1345 let mut n: i64 = 0;
1346 let mut digits = 0usize;
1347 while digits < zero_pad_width && pos + digits < inp.len() && inp[pos + digits] == b'0' {
1348 digits += 1;
1349 }
1350 while digits < max_digits && pos + digits < inp.len() && inp[pos + digits].is_ascii_digit()
1352 {
1353 let digit = i64::from(inp[pos + digits] - b'0');
1354 n = n
1355 .checked_mul(10)
1356 .and_then(|x| x.checked_add(digit))
1357 .ok_or(())?;
1358 digits += 1;
1359 }
1360 if digits == 0 {
1361 return Err(());
1362 }
1363 Ok((n, &inp[pos + digits..]))
1364 }
1365
1366 #[inline]
1367 fn parse_u8_padded(
1368 inp: &[u8],
1369 flag: Option<u8>,
1370 width: Option<u8>,
1371 default_pad_width: usize,
1372 default_flag: u8,
1373 ) -> Result<(u8, &[u8]), ()> {
1374 let (n, remaining) =
1375 Self::parse_padded_number(inp, flag, width, default_pad_width, default_flag)?;
1376 if !(0..=255).contains(&n) {
1377 return Err(());
1378 }
1379 Ok((n as u8, remaining))
1380 }
1381
1382 #[inline]
1383 fn match_from_choice_list<'a>(inp: &'a [u8], choices: &[&[u8]]) -> Result<(u8, &'a [u8]), ()> {
1384 for (i, choice) in choices.iter().enumerate() {
1385 if inp.len() < choice.len() {
1386 continue;
1387 }
1388 let candidate = &inp[..choice.len()];
1389 if candidate.eq_ignore_ascii_case(choice) {
1390 return Ok((i as u8, &inp[choice.len()..]));
1391 }
1392 }
1393 Err(())
1394 }
1395
1396 #[inline]
1397 fn parse_iana(inp: &[u8]) -> Result<(&str, &[u8]), ()> {
1398 let start = inp;
1399 let mut pos = 0;
1400
1401 if pos >= inp.len() || !matches!(inp[pos], b'_' | b'.' | b'A'..=b'Z' | b'a'..=b'z') {
1402 return Err(());
1403 }
1404 pos += 1;
1405
1406 while pos < inp.len() {
1407 if matches!(
1408 inp[pos],
1409 b'_' | b'.' | b'+' | b'-' | b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z'
1410 ) {
1411 pos += 1;
1412 } else if inp[pos] == b'/' {
1413 pos += 1;
1414 if pos >= inp.len() || !matches!(inp[pos], b'_' | b'.' | b'A'..=b'Z' | b'a'..=b'z')
1415 {
1416 return Err(());
1417 }
1418 pos += 1;
1419 } else {
1420 break;
1421 }
1422 }
1423 let iana = core::str::from_utf8(&start[..pos]).map_err(|_| ())?;
1424 Ok((iana, &start[pos..]))
1425 }
1426
1427 #[inline]
1428 fn parse_padded_i64(
1429 inp: &[u8],
1430 flag: Option<u8>,
1431 width: Option<u8>,
1432 default_pad_width: usize,
1433 default_flag: u8,
1434 ) -> Result<(i64, &[u8]), ()> {
1435 let (sign, after_sign) = Self::parse_optional_sign(inp);
1436 let (n, remaining) =
1437 Self::parse_padded_number(after_sign, flag, width, default_pad_width, default_flag)?;
1438 let mut y = n;
1439 if sign < 0 {
1440 y = -y;
1441 }
1442 Ok((y, remaining))
1443 }
1444
1445 #[inline]
1446 fn parse_arbitrary_i64(inp: &[u8]) -> Result<(i64, &[u8]), ()> {
1447 let (sign, after_sign) = Self::parse_optional_sign(inp);
1448 let (digits, remaining) = Self::parse_digits(after_sign);
1449 if digits.is_empty() {
1450 return Err(());
1451 }
1452 let mut y: i64 = 0;
1453 for &byte in digits {
1454 let d = (byte - b'0') as i64;
1455 y = y.checked_mul(10).and_then(|x| x.checked_add(d)).ok_or(())?;
1456 }
1457 if sign < 0 {
1458 y = y.checked_neg().ok_or(())?;
1459 }
1460 Ok((y, remaining))
1461 }
1462}