1use core::num::NonZero;
4
5use num_conv::prelude::*;
6
7use crate::format_description::{Period, modifier};
8use crate::parsing::ParsedItem;
9use crate::parsing::combinator::{
10 ExactlyNDigits, Sign, any_digit, exactly_n_digits_padded, n_to_m_digits, n_to_m_digits_padded,
11 opt, sign,
12};
13use crate::unit::*;
14use crate::{Month, Weekday};
15
16#[inline]
21pub(crate) fn parse_calendar_year_full_extended_range(
22 input: &[u8],
23 modifiers: modifier::CalendarYearFullExtendedRange,
24) -> Option<ParsedItem<'_, i32>> {
25 let ParsedItem(input, sign) = opt(sign)(input);
26
27 if let Some(sign) = sign {
28 let ParsedItem(input, year) = n_to_m_digits_padded::<4, 6, u32>(modifiers.padding)(input)?;
29
30 Some(ParsedItem(
31 input,
32 match sign {
33 Sign::Negative => -year.cast_signed(),
34 Sign::Positive => year.cast_signed(),
35 },
36 ))
37 } else if modifiers.sign_is_mandatory {
38 None
39 } else {
40 let ParsedItem(input, year) = exactly_n_digits_padded::<4, u32>(modifiers.padding)(input)?;
41 Some(ParsedItem(input, year.cast_signed()))
42 }
43}
44
45#[inline]
50pub(crate) fn parse_calendar_year_full_standard_range(
51 input: &[u8],
52 modifiers: modifier::CalendarYearFullStandardRange,
53) -> Option<ParsedItem<'_, i32>> {
54 let ParsedItem(input, sign) = opt(sign)(input);
55
56 if let Some(sign) = sign {
57 let ParsedItem(input, year) = exactly_n_digits_padded::<4, u32>(modifiers.padding)(input)?;
58
59 Some(ParsedItem(
60 input,
61 match sign {
62 Sign::Negative => -year.cast_signed(),
63 Sign::Positive => year.cast_signed(),
64 },
65 ))
66 } else if modifiers.sign_is_mandatory {
67 None
68 } else {
69 let ParsedItem(input, year) = exactly_n_digits_padded::<4, u32>(modifiers.padding)(input)?;
70 Some(ParsedItem(input, year.cast_signed()))
71 }
72}
73
74#[inline]
79pub(crate) fn parse_iso_year_full_extended_range(
80 input: &[u8],
81 modifiers: modifier::IsoYearFullExtendedRange,
82) -> Option<ParsedItem<'_, i32>> {
83 let ParsedItem(input, sign) = opt(sign)(input);
84
85 if let Some(sign) = sign {
86 let ParsedItem(input, year) = n_to_m_digits_padded::<4, 6, u32>(modifiers.padding)(input)?;
87
88 Some(ParsedItem(
89 input,
90 match sign {
91 Sign::Negative => -year.cast_signed(),
92 Sign::Positive => year.cast_signed(),
93 },
94 ))
95 } else if modifiers.sign_is_mandatory {
96 None
97 } else {
98 let ParsedItem(input, year) = exactly_n_digits_padded::<4, u32>(modifiers.padding)(input)?;
99 Some(ParsedItem(input, year.cast_signed()))
100 }
101}
102
103#[inline]
108pub(crate) fn parse_iso_year_full_standard_range(
109 input: &[u8],
110 modifiers: modifier::IsoYearFullStandardRange,
111) -> Option<ParsedItem<'_, i32>> {
112 let ParsedItem(input, sign) = opt(sign)(input);
113
114 if let Some(sign) = sign {
115 let ParsedItem(input, year) = exactly_n_digits_padded::<4, u32>(modifiers.padding)(input)?;
116
117 Some(ParsedItem(
118 input,
119 match sign {
120 Sign::Negative => -year.cast_signed(),
121 Sign::Positive => year.cast_signed(),
122 },
123 ))
124 } else if modifiers.sign_is_mandatory {
125 None
126 } else {
127 let ParsedItem(input, year) = exactly_n_digits_padded::<4, u32>(modifiers.padding)(input)?;
128 Some(ParsedItem(input, year.cast_signed()))
129 }
130}
131
132#[inline]
137pub(crate) fn parse_calendar_year_century_extended_range(
138 input: &[u8],
139 modifiers: modifier::CalendarYearCenturyExtendedRange,
140) -> Option<ParsedItem<'_, (i16, bool)>> {
141 let ParsedItem(input, sign) = opt(sign)(input);
142
143 if let Some(sign) = sign {
144 let ParsedItem(input, year) = n_to_m_digits_padded::<2, 4, u16>(modifiers.padding)(input)?;
145
146 Some(ParsedItem(
147 input,
148 match sign {
149 Sign::Negative => (-year.cast_signed(), true),
150 Sign::Positive => (year.cast_signed(), false),
151 },
152 ))
153 } else if modifiers.sign_is_mandatory {
154 None
155 } else {
156 let ParsedItem(input, year) = n_to_m_digits_padded::<1, 2, u16>(modifiers.padding)(input)?;
157 Some(ParsedItem(input, (year.cast_signed(), false)))
158 }
159}
160
161#[inline]
166pub(crate) fn parse_calendar_year_century_standard_range(
167 input: &[u8],
168 modifiers: modifier::CalendarYearCenturyStandardRange,
169) -> Option<ParsedItem<'_, (i16, bool)>> {
170 let ParsedItem(input, sign) = opt(sign)(input);
171
172 if let Some(sign) = sign {
173 let ParsedItem(input, year) = exactly_n_digits_padded::<2, u16>(modifiers.padding)(input)?;
174
175 Some(ParsedItem(
176 input,
177 match sign {
178 Sign::Negative => (-year.cast_signed(), true),
179 Sign::Positive => (year.cast_signed(), false),
180 },
181 ))
182 } else if modifiers.sign_is_mandatory {
183 None
184 } else {
185 let ParsedItem(input, year) = n_to_m_digits_padded::<1, 2, u16>(modifiers.padding)(input)?;
186 Some(ParsedItem(input, (year.cast_signed(), false)))
187 }
188}
189
190#[inline]
195pub(crate) fn parse_iso_year_century_extended_range(
196 input: &[u8],
197 modifiers: modifier::IsoYearCenturyExtendedRange,
198) -> Option<ParsedItem<'_, (i16, bool)>> {
199 let ParsedItem(input, sign) = opt(sign)(input);
200
201 if let Some(sign) = sign {
202 let ParsedItem(input, year) = n_to_m_digits_padded::<2, 4, u16>(modifiers.padding)(input)?;
203
204 Some(ParsedItem(
205 input,
206 match sign {
207 Sign::Negative => (-year.cast_signed(), true),
208 Sign::Positive => (year.cast_signed(), false),
209 },
210 ))
211 } else if modifiers.sign_is_mandatory {
212 None
213 } else {
214 let ParsedItem(input, year) = n_to_m_digits_padded::<1, 2, u16>(modifiers.padding)(input)?;
215 Some(ParsedItem(input, (year.cast_signed(), false)))
216 }
217}
218
219#[inline]
224pub(crate) fn parse_iso_year_century_standard_range(
225 input: &[u8],
226 modifiers: modifier::IsoYearCenturyStandardRange,
227) -> Option<ParsedItem<'_, (i16, bool)>> {
228 let ParsedItem(input, sign) = opt(sign)(input);
229
230 if let Some(sign) = sign {
231 let ParsedItem(input, year) = exactly_n_digits_padded::<2, u16>(modifiers.padding)(input)?;
232
233 Some(ParsedItem(
234 input,
235 match sign {
236 Sign::Negative => (-year.cast_signed(), true),
237 Sign::Positive => (year.cast_signed(), false),
238 },
239 ))
240 } else if modifiers.sign_is_mandatory {
241 None
242 } else {
243 let ParsedItem(input, year) = n_to_m_digits_padded::<1, 2, u16>(modifiers.padding)(input)?;
244 Some(ParsedItem(input, (year.cast_signed(), false)))
245 }
246}
247
248#[inline]
250pub(crate) fn parse_calendar_year_last_two(
251 input: &[u8],
252 modifiers: modifier::CalendarYearLastTwo,
253) -> Option<ParsedItem<'_, u8>> {
254 exactly_n_digits_padded::<2, _>(modifiers.padding)(input)
255}
256
257#[inline]
259pub(crate) fn parse_iso_year_last_two(
260 input: &[u8],
261 modifiers: modifier::IsoYearLastTwo,
262) -> Option<ParsedItem<'_, u8>> {
263 exactly_n_digits_padded::<2, _>(modifiers.padding)(input)
264}
265
266#[expect(deprecated)]
268pub(crate) fn parse_year(
269 input: &[u8],
270 modifiers: modifier::Year,
271) -> Option<ParsedItem<'_, (i32, bool)>> {
272 match modifiers.repr {
273 modifier::YearRepr::Full => {
274 let ParsedItem(input, sign) = opt(sign)(input);
275
276 if let Some(sign) = sign {
277 let ParsedItem(input, year) = if cfg!(feature = "large-dates")
278 && modifiers.range == modifier::YearRange::Extended
279 {
280 n_to_m_digits_padded::<4, 6, u32>(modifiers.padding)(input)?
281 } else {
282 exactly_n_digits_padded::<4, u32>(modifiers.padding)(input)?
283 };
284
285 Some(ParsedItem(
286 input,
287 match sign {
288 Sign::Negative => (-year.cast_signed(), true),
289 Sign::Positive => (year.cast_signed(), false),
290 },
291 ))
292 } else if modifiers.sign_is_mandatory {
293 None
294 } else {
295 let ParsedItem(input, year) =
296 exactly_n_digits_padded::<4, u32>(modifiers.padding)(input)?;
297 Some(ParsedItem(input, (year.cast_signed(), false)))
298 }
299 }
300 modifier::YearRepr::Century => {
301 let ParsedItem(input, sign) = opt(sign)(input);
302
303 if let Some(sign) = sign {
304 let ParsedItem(input, year) = if cfg!(feature = "large-dates")
305 && modifiers.range == modifier::YearRange::Extended
306 {
307 n_to_m_digits_padded::<2, 4, u32>(modifiers.padding)(input)?
308 } else {
309 exactly_n_digits_padded::<2, u32>(modifiers.padding)(input)?
310 };
311
312 Some(ParsedItem(
313 input,
314 match sign {
315 Sign::Negative => (-year.cast_signed(), true),
316 Sign::Positive => (year.cast_signed(), false),
317 },
318 ))
319 } else if modifiers.sign_is_mandatory {
320 None
321 } else {
322 let ParsedItem(input, year) =
323 n_to_m_digits_padded::<1, 2, u32>(modifiers.padding)(input)?;
324 Some(ParsedItem(input, (year.cast_signed(), false)))
325 }
326 }
327 modifier::YearRepr::LastTwo => Some(
328 exactly_n_digits_padded::<2, u32>(modifiers.padding)(input)?
329 .map(|v| (v.cast_signed(), false)),
330 ),
331 }
332}
333
334#[inline]
336pub(crate) fn parse_month_short(
337 input: &[u8],
338 modifiers: modifier::MonthShort,
339) -> Option<ParsedItem<'_, Month>> {
340 let [first, second, third, rest @ ..] = input else {
341 return None;
342 };
343 let byte = if modifiers.case_sensitive {
344 u32::from_ne_bytes([0, *first, *second, *third])
345 } else {
346 u32::from_ne_bytes([
347 0,
348 first.to_ascii_uppercase(),
349 second.to_ascii_lowercase(),
350 third.to_ascii_lowercase(),
351 ])
352 };
353 const WEEKDAYS: [u32; 12] = [
354 u32::from_ne_bytes([0, b'J', b'a', b'n']),
355 u32::from_ne_bytes([0, b'F', b'e', b'b']),
356 u32::from_ne_bytes([0, b'M', b'a', b'r']),
357 u32::from_ne_bytes([0, b'A', b'p', b'r']),
358 u32::from_ne_bytes([0, b'M', b'a', b'y']),
359 u32::from_ne_bytes([0, b'J', b'u', b'n']),
360 u32::from_ne_bytes([0, b'J', b'u', b'l']),
361 u32::from_ne_bytes([0, b'A', b'u', b'g']),
362 u32::from_ne_bytes([0, b'S', b'e', b'p']),
363 u32::from_ne_bytes([0, b'O', b'c', b't']),
364 u32::from_ne_bytes([0, b'N', b'o', b'v']),
365 u32::from_ne_bytes([0, b'D', b'e', b'c']),
366 ];
367
368 let bitmask = ((WEEKDAYS[0] == byte) as u32) << 1
369 | ((WEEKDAYS[1] == byte) as u32) << 2
370 | ((WEEKDAYS[2] == byte) as u32) << 3
371 | ((WEEKDAYS[3] == byte) as u32) << 4
372 | ((WEEKDAYS[4] == byte) as u32) << 5
373 | ((WEEKDAYS[5] == byte) as u32) << 6
374 | ((WEEKDAYS[6] == byte) as u32) << 7
375 | ((WEEKDAYS[7] == byte) as u32) << 8
376 | ((WEEKDAYS[8] == byte) as u32) << 9
377 | ((WEEKDAYS[9] == byte) as u32) << 10
378 | ((WEEKDAYS[10] == byte) as u32) << 11
379 | ((WEEKDAYS[11] == byte) as u32) << 12;
380 if bitmask == 0 {
381 return None;
382 }
383 let index = if cfg!(target_endian = "little") {
384 bitmask.trailing_zeros() as u8
385 } else {
386 31 - bitmask.leading_zeros() as u8
387 };
388
389 let month = unsafe { Month::from_number(NonZero::new(index)?).unwrap_unchecked() };
393
394 Some(ParsedItem(rest, month))
395}
396
397#[inline]
399pub(crate) fn parse_month_long(
400 input: &[u8],
401 modifiers: modifier::MonthLong,
402) -> Option<ParsedItem<'_, Month>> {
403 use Month::*;
404
405 let ParsedItem(rest, month) = parse_month_short(
406 input,
407 modifier::MonthShort {
408 case_sensitive: modifiers.case_sensitive,
409 },
410 )?;
411
412 let expected_remaining = match month {
413 January => b"uary".as_slice(),
414 February => b"ruary".as_slice(),
415 March => b"ch".as_slice(),
416 April => b"il".as_slice(),
417 May => b"".as_slice(),
418 June => b"e".as_slice(),
419 July => b"y".as_slice(),
420 August => b"ust".as_slice(),
421 September => b"tember".as_slice(),
422 October => b"ober".as_slice(),
423 November | December => b"ember".as_slice(),
424 };
425
426 if modifiers.case_sensitive {
427 rest.strip_prefix(expected_remaining)
428 .map(|remaining| ParsedItem(remaining, month))
429 } else {
430 let (head, tail) = rest.split_at_checked(expected_remaining.len())?;
431 core::iter::zip(head, expected_remaining)
432 .all(|(a, b)| a.eq_ignore_ascii_case(b))
433 .then_some(ParsedItem(tail, month))
434 }
435}
436
437#[inline]
439pub(crate) fn parse_month_numerical(
440 input: &[u8],
441 modifiers: modifier::MonthNumerical,
442) -> Option<ParsedItem<'_, Month>> {
443 exactly_n_digits_padded::<2, _>(modifiers.padding)(input)?
444 .flat_map(|n| Month::from_number(NonZero::new(n)?).ok())
445}
446
447#[inline]
450pub(crate) fn parse_week_number_iso(
451 input: &[u8],
452 modifiers: modifier::WeekNumberIso,
453) -> Option<ParsedItem<'_, u8>> {
454 exactly_n_digits_padded::<2, _>(modifiers.padding)(input)
455}
456
457#[inline]
460pub(crate) fn parse_week_number_sunday(
461 input: &[u8],
462 modifiers: modifier::WeekNumberSunday,
463) -> Option<ParsedItem<'_, u8>> {
464 exactly_n_digits_padded::<2, _>(modifiers.padding)(input)
465}
466
467#[inline]
470pub(crate) fn parse_week_number_monday(
471 input: &[u8],
472 modifiers: modifier::WeekNumberMonday,
473) -> Option<ParsedItem<'_, u8>> {
474 exactly_n_digits_padded::<2, _>(modifiers.padding)(input)
475}
476
477#[inline]
479pub(crate) fn parse_weekday_short(
480 input: &[u8],
481 modifiers: modifier::WeekdayShort,
482) -> Option<ParsedItem<'_, Weekday>> {
483 let [first, second, third, rest @ ..] = input else {
484 return None;
485 };
486 let byte = if modifiers.case_sensitive {
487 u32::from_ne_bytes([0, *first, *second, *third])
488 } else {
489 u32::from_ne_bytes([
490 0,
491 first.to_ascii_uppercase(),
492 second.to_ascii_lowercase(),
493 third.to_ascii_lowercase(),
494 ])
495 };
496 const WEEKDAYS: [u32; 7] = [
497 u32::from_ne_bytes([0, b'M', b'o', b'n']),
498 u32::from_ne_bytes([0, b'T', b'u', b'e']),
499 u32::from_ne_bytes([0, b'W', b'e', b'd']),
500 u32::from_ne_bytes([0, b'T', b'h', b'u']),
501 u32::from_ne_bytes([0, b'F', b'r', b'i']),
502 u32::from_ne_bytes([0, b'S', b'a', b't']),
503 u32::from_ne_bytes([0, b'S', b'u', b'n']),
504 ];
505
506 let bitmask = ((WEEKDAYS[0] == byte) as u32)
507 | ((WEEKDAYS[1] == byte) as u32) << 1
508 | ((WEEKDAYS[2] == byte) as u32) << 2
509 | ((WEEKDAYS[3] == byte) as u32) << 3
510 | ((WEEKDAYS[4] == byte) as u32) << 4
511 | ((WEEKDAYS[5] == byte) as u32) << 5
512 | ((WEEKDAYS[6] == byte) as u32) << 6;
513 if bitmask == 0 {
514 return None;
515 }
516 let index = if cfg!(target_endian = "little") {
517 bitmask.trailing_zeros()
518 } else {
519 31 - bitmask.leading_zeros()
520 };
521
522 if index > 6 {
523 return None;
524 }
525 let weekday = unsafe { core::mem::transmute::<u8, Weekday>(index.truncate()) };
529
530 Some(ParsedItem(rest, weekday))
531}
532
533#[inline]
535pub(crate) fn parse_weekday_long(
536 input: &[u8],
537 modifiers: modifier::WeekdayLong,
538) -> Option<ParsedItem<'_, Weekday>> {
539 let ParsedItem(rest, weekday) = parse_weekday_short(
540 input,
541 modifier::WeekdayShort {
542 case_sensitive: modifiers.case_sensitive,
543 },
544 )?;
545
546 let expected_remaining = match weekday {
547 Weekday::Monday | Weekday::Friday | Weekday::Sunday => b"day".as_slice(),
548 Weekday::Tuesday => b"sday".as_slice(),
549 Weekday::Wednesday => b"nesday".as_slice(),
550 Weekday::Thursday => b"rsday".as_slice(),
551 Weekday::Saturday => b"urday".as_slice(),
552 };
553
554 if modifiers.case_sensitive {
555 rest.strip_prefix(expected_remaining)
556 .map(|remaining| ParsedItem(remaining, weekday))
557 } else {
558 let (head, tail) = rest.split_at_checked(expected_remaining.len())?;
559 core::iter::zip(head, expected_remaining)
560 .all(|(a, b)| a.eq_ignore_ascii_case(b))
561 .then_some(ParsedItem(tail, weekday))
562 }
563}
564
565#[inline]
568pub(crate) fn parse_weekday_sunday(
569 input: &[u8],
570 modifiers: modifier::WeekdaySunday,
571) -> Option<ParsedItem<'_, Weekday>> {
572 let [digit, rest @ ..] = input else {
573 return None;
574 };
575 let mut digit = digit
576 .wrapping_sub(b'0')
577 .wrapping_sub(u8::from(modifiers.one_indexed));
578 if digit > 6 {
579 return None;
580 }
581
582 digit = (digit + 6) % 7;
584
585 let weekday = unsafe { core::mem::transmute::<u8, Weekday>(digit) };
587 Some(ParsedItem(rest, weekday))
588}
589
590#[inline]
593pub(crate) fn parse_weekday_monday(
594 input: &[u8],
595 modifiers: modifier::WeekdayMonday,
596) -> Option<ParsedItem<'_, Weekday>> {
597 let [digit, rest @ ..] = input else {
598 return None;
599 };
600 let digit = digit
601 .wrapping_sub(b'0')
602 .wrapping_sub(u8::from(modifiers.one_indexed));
603 if digit > 6 {
604 return None;
605 }
606
607 let weekday = unsafe { core::mem::transmute::<u8, Weekday>(digit) };
609 Some(ParsedItem(rest, weekday))
610}
611
612#[inline]
614pub(crate) fn parse_ordinal(
615 input: &[u8],
616 modifiers: modifier::Ordinal,
617) -> Option<ParsedItem<'_, NonZero<u16>>> {
618 exactly_n_digits_padded::<3, _>(modifiers.padding)(input)
619 .and_then(|parsed| parsed.flat_map(NonZero::new))
620}
621
622#[inline]
624pub(crate) fn parse_day(
625 input: &[u8],
626 modifiers: modifier::Day,
627) -> Option<ParsedItem<'_, NonZero<u8>>> {
628 exactly_n_digits_padded::<2, _>(modifiers.padding)(input)
629 .and_then(|parsed| parsed.flat_map(NonZero::new))
630}
631
632#[inline]
634pub(crate) fn parse_hour_12(
635 input: &[u8],
636 modifiers: modifier::Hour12,
637) -> Option<ParsedItem<'_, u8>> {
638 exactly_n_digits_padded::<2, _>(modifiers.padding)(input)
639}
640
641#[inline]
643pub(crate) fn parse_hour_24(
644 input: &[u8],
645 modifiers: modifier::Hour24,
646) -> Option<ParsedItem<'_, u8>> {
647 exactly_n_digits_padded::<2, _>(modifiers.padding)(input)
648}
649
650#[inline]
652pub(crate) fn parse_minute(
653 input: &[u8],
654 modifiers: modifier::Minute,
655) -> Option<ParsedItem<'_, u8>> {
656 exactly_n_digits_padded::<2, _>(modifiers.padding)(input)
657}
658
659#[inline]
661pub(crate) fn parse_second(
662 input: &[u8],
663 modifiers: modifier::Second,
664) -> Option<ParsedItem<'_, u8>> {
665 exactly_n_digits_padded::<2, _>(modifiers.padding)(input)
666}
667
668#[inline]
670pub(crate) fn parse_period(
671 input: &[u8],
672 modifiers: modifier::Period,
673) -> Option<ParsedItem<'_, Period>> {
674 let [first, second, rest @ ..] = input else {
675 return None;
676 };
677 let mut first = *first;
678 let mut second = *second;
679
680 if modifiers.is_uppercase && modifiers.case_sensitive {
681 match [first, second].as_slice() {
682 b"AM" => Some(ParsedItem(rest, Period::Am)),
683 b"PM" => Some(ParsedItem(rest, Period::Pm)),
684 _ => None,
685 }
686 } else {
687 first = first.to_ascii_lowercase();
688 second = second.to_ascii_lowercase();
689
690 match &[first, second] {
691 b"am" => Some(ParsedItem(rest, Period::Am)),
692 b"pm" => Some(ParsedItem(rest, Period::Pm)),
693 _ => None,
694 }
695 }
696}
697
698pub(crate) fn parse_subsecond(
700 input: &[u8],
701 modifiers: modifier::Subsecond,
702) -> Option<ParsedItem<'_, u32>> {
703 use modifier::SubsecondDigits::*;
704 Some(match modifiers.digits {
705 One => ExactlyNDigits::<1>::parse(input)?.map(|v| v.extend::<u32>() * 100_000_000),
706 Two => ExactlyNDigits::<2>::parse(input)?.map(|v| v.extend::<u32>() * 10_000_000),
707 Three => ExactlyNDigits::<3>::parse(input)?.map(|v| v.extend::<u32>() * 1_000_000),
708 Four => ExactlyNDigits::<4>::parse(input)?.map(|v| v.extend::<u32>() * 100_000),
709 Five => ExactlyNDigits::<5>::parse(input)?.map(|v| v * 10_000),
710 Six => ExactlyNDigits::<6>::parse(input)?.map(|v| v * 1_000),
711 Seven => ExactlyNDigits::<7>::parse(input)?.map(|v| v * 100),
712 Eight => ExactlyNDigits::<8>::parse(input)?.map(|v| v * 10),
713 Nine => ExactlyNDigits::<9>::parse(input)?,
714 OneOrMore => {
715 let ParsedItem(mut input, mut value) =
716 any_digit(input)?.map(|v| (v - b'0').extend::<u32>() * 100_000_000);
717
718 let mut multiplier = 10_000_000;
719 while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
720 value += (digit - b'0').extend::<u32>() * multiplier;
721 input = new_input;
722 multiplier /= 10;
723 }
724
725 ParsedItem(input, value)
726 }
727 })
728}
729
730#[inline]
734pub(crate) fn parse_offset_hour(
735 input: &[u8],
736 modifiers: modifier::OffsetHour,
737) -> Option<ParsedItem<'_, (i8, bool)>> {
738 let ParsedItem(input, sign) = opt(sign)(input);
739 let ParsedItem(input, hour) = exactly_n_digits_padded::<2, u8>(modifiers.padding)(input)?;
740 match sign {
741 Some(Sign::Negative) => Some(ParsedItem(input, (-hour.cast_signed(), true))),
742 None if modifiers.sign_is_mandatory => None,
743 _ => Some(ParsedItem(input, (hour.cast_signed(), false))),
744 }
745}
746
747#[inline]
749pub(crate) fn parse_offset_minute(
750 input: &[u8],
751 modifiers: modifier::OffsetMinute,
752) -> Option<ParsedItem<'_, i8>> {
753 Some(
754 exactly_n_digits_padded::<2, u8>(modifiers.padding)(input)?
755 .map(|offset_minute| offset_minute.cast_signed()),
756 )
757}
758
759#[inline]
761pub(crate) fn parse_offset_second(
762 input: &[u8],
763 modifiers: modifier::OffsetSecond,
764) -> Option<ParsedItem<'_, i8>> {
765 Some(
766 exactly_n_digits_padded::<2, u8>(modifiers.padding)(input)?
767 .map(|offset_second| offset_second.cast_signed()),
768 )
769}
770
771#[inline]
773pub(crate) fn parse_ignore(
774 input: &[u8],
775 modifiers: modifier::Ignore,
776) -> Option<ParsedItem<'_, ()>> {
777 let modifier::Ignore { count } = modifiers;
778 let input = input.get((count.get().extend())..)?;
779 Some(ParsedItem(input, ()))
780}
781
782#[inline]
784pub(crate) fn parse_unix_timestamp_second(
785 input: &[u8],
786 modifiers: modifier::UnixTimestampSecond,
787) -> Option<ParsedItem<'_, i128>> {
788 let ParsedItem(input, sign) = opt(sign)(input);
789 let ParsedItem(input, nano_timestamp) =
790 n_to_m_digits::<1, 14, u128>(input)?.map(|val| val * Nanosecond::per_t::<u128>(Second));
791
792 match sign {
793 Some(Sign::Negative) => Some(ParsedItem(input, -nano_timestamp.cast_signed())),
794 None if modifiers.sign_is_mandatory => None,
795 _ => Some(ParsedItem(input, nano_timestamp.cast_signed())),
796 }
797}
798
799#[inline]
802pub(crate) fn parse_unix_timestamp_millisecond(
803 input: &[u8],
804 modifiers: modifier::UnixTimestampMillisecond,
805) -> Option<ParsedItem<'_, i128>> {
806 let ParsedItem(input, sign) = opt(sign)(input);
807 let ParsedItem(input, nano_timestamp) = n_to_m_digits::<1, 17, u128>(input)?
808 .map(|val| val * Nanosecond::per_t::<u128>(Millisecond));
809
810 match sign {
811 Some(Sign::Negative) => Some(ParsedItem(input, -nano_timestamp.cast_signed())),
812 None if modifiers.sign_is_mandatory => None,
813 _ => Some(ParsedItem(input, nano_timestamp.cast_signed())),
814 }
815}
816
817#[inline]
820pub(crate) fn parse_unix_timestamp_microsecond(
821 input: &[u8],
822 modifiers: modifier::UnixTimestampMicrosecond,
823) -> Option<ParsedItem<'_, i128>> {
824 let ParsedItem(input, sign) = opt(sign)(input);
825 let ParsedItem(input, nano_timestamp) = n_to_m_digits::<1, 20, u128>(input)?
826 .map(|val| val * Nanosecond::per_t::<u128>(Microsecond));
827
828 match sign {
829 Some(Sign::Negative) => Some(ParsedItem(input, -nano_timestamp.cast_signed())),
830 None if modifiers.sign_is_mandatory => None,
831 _ => Some(ParsedItem(input, nano_timestamp.cast_signed())),
832 }
833}
834
835#[inline]
837pub(crate) fn parse_unix_timestamp_nanosecond(
838 input: &[u8],
839 modifiers: modifier::UnixTimestampNanosecond,
840) -> Option<ParsedItem<'_, i128>> {
841 let ParsedItem(input, sign) = opt(sign)(input);
842 let ParsedItem(input, nano_timestamp) = n_to_m_digits::<1, 23, u128>(input)?;
843
844 match sign {
845 Some(Sign::Negative) => Some(ParsedItem(input, -nano_timestamp.cast_signed())),
846 None if modifiers.sign_is_mandatory => None,
847 _ => Some(ParsedItem(input, nano_timestamp.cast_signed())),
848 }
849}
850
851#[inline]
855pub(crate) fn parse_end(input: &[u8], end: modifier::End) -> Option<ParsedItem<'_, ()>> {
856 let modifier::End { trailing_input } = end;
857
858 if trailing_input == modifier::TrailingInput::Discard || input.is_empty() {
859 Some(ParsedItem(b"", ()))
860 } else {
861 None
862 }
863}