Coverage Report

Created: 2025-11-09 06:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/time-0.3.44/src/parsing/parsable.rs
Line
Count
Source
1
//! A trait that can be used to parse an item from an input.
2
3
use core::ops::Deref;
4
5
use num_conv::prelude::*;
6
7
use crate::error::TryFromParsed;
8
use crate::format_description::well_known::iso8601::EncodedConfig;
9
use crate::format_description::well_known::{Iso8601, Rfc2822, Rfc3339};
10
use crate::format_description::BorrowedFormatItem;
11
#[cfg(feature = "alloc")]
12
use crate::format_description::OwnedFormatItem;
13
use crate::internal_macros::bug;
14
use crate::parsing::{Parsed, ParsedItem};
15
use crate::{error, Date, Month, OffsetDateTime, Time, UtcOffset, Weekday};
16
17
/// A type that can be parsed.
18
#[cfg_attr(docsrs, doc(notable_trait))]
19
#[doc(alias = "Parseable")]
20
pub trait Parsable: sealed::Sealed {}
21
impl Parsable for BorrowedFormatItem<'_> {}
22
impl Parsable for [BorrowedFormatItem<'_>] {}
23
#[cfg(feature = "alloc")]
24
impl Parsable for OwnedFormatItem {}
25
#[cfg(feature = "alloc")]
26
impl Parsable for [OwnedFormatItem] {}
27
impl Parsable for Rfc2822 {}
28
impl Parsable for Rfc3339 {}
29
impl<const CONFIG: EncodedConfig> Parsable for Iso8601<CONFIG> {}
30
impl<T: Deref> Parsable for T where T::Target: Parsable {}
31
32
/// Seal the trait to prevent downstream users from implementing it, while still allowing it to
33
/// exist in generic bounds.
34
mod sealed {
35
    use super::*;
36
    use crate::{PrimitiveDateTime, UtcDateTime};
37
38
    /// Parse the item using a format description and an input.
39
    pub trait Sealed {
40
        /// Parse the item into the provided [`Parsed`] struct.
41
        ///
42
        /// This method can be used to parse a single component without parsing the full value.
43
        fn parse_into<'a>(
44
            &self,
45
            input: &'a [u8],
46
            parsed: &mut Parsed,
47
        ) -> Result<&'a [u8], error::Parse>;
48
49
        /// Parse the item into a new [`Parsed`] struct.
50
        ///
51
        /// This method can only be used to parse a complete value of a type. If any characters
52
        /// remain after parsing, an error will be returned.
53
        #[inline]
54
0
        fn parse(&self, input: &[u8]) -> Result<Parsed, error::Parse> {
55
0
            let mut parsed = Parsed::new();
56
0
            if self.parse_into(input, &mut parsed)?.is_empty() {
57
0
                Ok(parsed)
58
            } else {
59
0
                Err(error::Parse::ParseFromDescription(
60
0
                    error::ParseFromDescription::UnexpectedTrailingCharacters,
61
0
                ))
62
            }
63
0
        }
64
65
        /// Parse a [`Date`] from the format description.
66
        #[inline]
67
0
        fn parse_date(&self, input: &[u8]) -> Result<Date, error::Parse> {
68
0
            Ok(self.parse(input)?.try_into()?)
69
0
        }
70
71
        /// Parse a [`Time`] from the format description.
72
        #[inline]
73
0
        fn parse_time(&self, input: &[u8]) -> Result<Time, error::Parse> {
74
0
            Ok(self.parse(input)?.try_into()?)
75
0
        }
76
77
        /// Parse a [`UtcOffset`] from the format description.
78
        #[inline]
79
0
        fn parse_offset(&self, input: &[u8]) -> Result<UtcOffset, error::Parse> {
80
0
            Ok(self.parse(input)?.try_into()?)
81
0
        }
82
83
        /// Parse a [`PrimitiveDateTime`] from the format description.
84
        #[inline]
85
0
        fn parse_primitive_date_time(
86
0
            &self,
87
0
            input: &[u8],
88
0
        ) -> Result<PrimitiveDateTime, error::Parse> {
89
0
            Ok(self.parse(input)?.try_into()?)
90
0
        }
91
92
        /// Parse a [`UtcDateTime`] from the format description.
93
        #[inline]
94
0
        fn parse_utc_date_time(&self, input: &[u8]) -> Result<UtcDateTime, error::Parse> {
95
0
            Ok(self.parse(input)?.try_into()?)
96
0
        }
97
98
        /// Parse a [`OffsetDateTime`] from the format description.
99
        #[inline]
100
0
        fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
101
0
            Ok(self.parse(input)?.try_into()?)
102
0
        }
103
    }
104
}
105
106
impl sealed::Sealed for BorrowedFormatItem<'_> {
107
    #[inline]
108
0
    fn parse_into<'a>(
109
0
        &self,
110
0
        input: &'a [u8],
111
0
        parsed: &mut Parsed,
112
0
    ) -> Result<&'a [u8], error::Parse> {
113
0
        Ok(parsed.parse_item(input, self)?)
114
0
    }
115
}
116
117
impl sealed::Sealed for [BorrowedFormatItem<'_>] {
118
    #[inline]
119
0
    fn parse_into<'a>(
120
0
        &self,
121
0
        input: &'a [u8],
122
0
        parsed: &mut Parsed,
123
0
    ) -> Result<&'a [u8], error::Parse> {
124
0
        Ok(parsed.parse_items(input, self)?)
125
0
    }
126
}
127
128
#[cfg(feature = "alloc")]
129
impl sealed::Sealed for OwnedFormatItem {
130
    #[inline]
131
0
    fn parse_into<'a>(
132
0
        &self,
133
0
        input: &'a [u8],
134
0
        parsed: &mut Parsed,
135
0
    ) -> Result<&'a [u8], error::Parse> {
136
0
        Ok(parsed.parse_item(input, self)?)
137
0
    }
138
}
139
140
#[cfg(feature = "alloc")]
141
impl sealed::Sealed for [OwnedFormatItem] {
142
    #[inline]
143
0
    fn parse_into<'a>(
144
0
        &self,
145
0
        input: &'a [u8],
146
0
        parsed: &mut Parsed,
147
0
    ) -> Result<&'a [u8], error::Parse> {
148
0
        Ok(parsed.parse_items(input, self)?)
149
0
    }
150
}
151
152
impl<T> sealed::Sealed for T
153
where
154
    T: Deref<Target: sealed::Sealed>,
155
{
156
    #[inline]
157
0
    fn parse_into<'a>(
158
0
        &self,
159
0
        input: &'a [u8],
160
0
        parsed: &mut Parsed,
161
0
    ) -> Result<&'a [u8], error::Parse> {
162
0
        self.deref().parse_into(input, parsed)
163
0
    }
164
}
165
166
impl sealed::Sealed for Rfc2822 {
167
0
    fn parse_into<'a>(
168
0
        &self,
169
0
        input: &'a [u8],
170
0
        parsed: &mut Parsed,
171
0
    ) -> Result<&'a [u8], error::Parse> {
172
        use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
173
        use crate::parsing::combinator::rfc::rfc2822::{cfws, fws};
174
        use crate::parsing::combinator::{
175
            ascii_char, exactly_n_digits, first_match, n_to_m_digits, opt, sign,
176
        };
177
178
0
        let colon = ascii_char::<b':'>;
179
0
        let comma = ascii_char::<b','>;
180
181
0
        let input = opt(cfws)(input).into_inner();
182
0
        let weekday = first_match(
183
0
            [
184
0
                (b"Mon".as_slice(), Weekday::Monday),
185
0
                (b"Tue".as_slice(), Weekday::Tuesday),
186
0
                (b"Wed".as_slice(), Weekday::Wednesday),
187
0
                (b"Thu".as_slice(), Weekday::Thursday),
188
0
                (b"Fri".as_slice(), Weekday::Friday),
189
0
                (b"Sat".as_slice(), Weekday::Saturday),
190
0
                (b"Sun".as_slice(), Weekday::Sunday),
191
0
            ],
192
0
            false,
193
0
        )(input);
194
0
        let input = if let Some(item) = weekday {
195
0
            let input = item
196
0
                .consume_value(|value| parsed.set_weekday(value))
197
0
                .ok_or(InvalidComponent("weekday"))?;
198
0
            let input = comma(input).ok_or(InvalidLiteral)?.into_inner();
199
0
            opt(cfws)(input).into_inner()
200
        } else {
201
0
            input
202
        };
203
0
        let input = n_to_m_digits::<1, 2, _>(input)
204
0
            .and_then(|item| item.consume_value(|value| parsed.set_day(value)))
205
0
            .ok_or(InvalidComponent("day"))?;
206
0
        let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
207
0
        let input = first_match(
208
0
            [
209
0
                (b"Jan".as_slice(), Month::January),
210
0
                (b"Feb".as_slice(), Month::February),
211
0
                (b"Mar".as_slice(), Month::March),
212
0
                (b"Apr".as_slice(), Month::April),
213
0
                (b"May".as_slice(), Month::May),
214
0
                (b"Jun".as_slice(), Month::June),
215
0
                (b"Jul".as_slice(), Month::July),
216
0
                (b"Aug".as_slice(), Month::August),
217
0
                (b"Sep".as_slice(), Month::September),
218
0
                (b"Oct".as_slice(), Month::October),
219
0
                (b"Nov".as_slice(), Month::November),
220
0
                (b"Dec".as_slice(), Month::December),
221
0
            ],
222
0
            false,
223
0
        )(input)
224
0
        .and_then(|item| item.consume_value(|value| parsed.set_month(value)))
225
0
        .ok_or(InvalidComponent("month"))?;
226
0
        let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
227
0
        let input = match exactly_n_digits::<4, u32>(input) {
228
0
            Some(item) => {
229
0
                let input = item
230
0
                    .flat_map(|year| if year >= 1900 { Some(year) } else { None })
231
0
                    .and_then(|item| {
232
0
                        item.consume_value(|value| parsed.set_year(value.cast_signed()))
233
0
                    })
234
0
                    .ok_or(InvalidComponent("year"))?;
235
0
                fws(input).ok_or(InvalidLiteral)?.into_inner()
236
            }
237
            None => {
238
0
                let input = exactly_n_digits::<2, u32>(input)
239
0
                    .and_then(|item| {
240
0
                        item.map(|year| if year < 50 { year + 2000 } else { year + 1900 })
241
0
                            .map(|year| year.cast_signed())
242
0
                            .consume_value(|value| parsed.set_year(value))
243
0
                    })
244
0
                    .ok_or(InvalidComponent("year"))?;
245
0
                cfws(input).ok_or(InvalidLiteral)?.into_inner()
246
            }
247
        };
248
249
0
        let input = exactly_n_digits::<2, _>(input)
250
0
            .and_then(|item| item.consume_value(|value| parsed.set_hour_24(value)))
251
0
            .ok_or(InvalidComponent("hour"))?;
252
0
        let input = opt(cfws)(input).into_inner();
253
0
        let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
254
0
        let input = opt(cfws)(input).into_inner();
255
0
        let input = exactly_n_digits::<2, _>(input)
256
0
            .and_then(|item| item.consume_value(|value| parsed.set_minute(value)))
257
0
            .ok_or(InvalidComponent("minute"))?;
258
259
0
        let input = if let Some(input) = colon(opt(cfws)(input).into_inner()) {
260
0
            let input = input.into_inner(); // discard the colon
261
0
            let input = opt(cfws)(input).into_inner();
262
0
            let input = exactly_n_digits::<2, _>(input)
263
0
                .and_then(|item| item.consume_value(|value| parsed.set_second(value)))
264
0
                .ok_or(InvalidComponent("second"))?;
265
0
            cfws(input).ok_or(InvalidLiteral)?.into_inner()
266
        } else {
267
0
            cfws(input).ok_or(InvalidLiteral)?.into_inner()
268
        };
269
270
        // The RFC explicitly allows leap seconds.
271
0
        parsed.leap_second_allowed = true;
272
273
        #[expect(
274
            clippy::unnecessary_lazy_evaluations,
275
            reason = "rust-lang/rust-clippy#8522"
276
        )]
277
0
        let zone_literal = first_match(
278
0
            [
279
0
                (b"UT".as_slice(), 0),
280
0
                (b"GMT".as_slice(), 0),
281
0
                (b"EST".as_slice(), -5),
282
0
                (b"EDT".as_slice(), -4),
283
0
                (b"CST".as_slice(), -6),
284
0
                (b"CDT".as_slice(), -5),
285
0
                (b"MST".as_slice(), -7),
286
0
                (b"MDT".as_slice(), -6),
287
0
                (b"PST".as_slice(), -8),
288
0
                (b"PDT".as_slice(), -7),
289
0
            ],
290
0
            false,
291
0
        )(input)
292
0
        .or_else(|| match input {
293
0
            [b'a'..=b'i' | b'k'..=b'z' | b'A'..=b'I' | b'K'..=b'Z', rest @ ..] => {
294
0
                Some(ParsedItem(rest, 0))
295
            }
296
0
            _ => None,
297
0
        });
298
0
        if let Some(zone_literal) = zone_literal {
299
0
            let input = zone_literal
300
0
                .consume_value(|value| parsed.set_offset_hour(value))
301
0
                .ok_or(InvalidComponent("offset hour"))?;
302
0
            parsed
303
0
                .set_offset_minute_signed(0)
304
0
                .ok_or(InvalidComponent("offset minute"))?;
305
0
            parsed
306
0
                .set_offset_second_signed(0)
307
0
                .ok_or(InvalidComponent("offset second"))?;
308
0
            return Ok(input);
309
0
        }
310
311
0
        let ParsedItem(input, offset_sign) = sign(input).ok_or(InvalidComponent("offset hour"))?;
312
0
        let input = exactly_n_digits::<2, u8>(input)
313
0
            .and_then(|item| {
314
0
                item.map(|offset_hour| {
315
0
                    if offset_sign == b'-' {
316
0
                        -offset_hour.cast_signed()
317
                    } else {
318
0
                        offset_hour.cast_signed()
319
                    }
320
0
                })
321
0
                .consume_value(|value| parsed.set_offset_hour(value))
322
0
            })
323
0
            .ok_or(InvalidComponent("offset hour"))?;
324
0
        let input = exactly_n_digits::<2, u8>(input)
325
0
            .and_then(|item| {
326
0
                item.consume_value(|value| parsed.set_offset_minute_signed(value.cast_signed()))
327
0
            })
328
0
            .ok_or(InvalidComponent("offset minute"))?;
329
330
0
        let input = opt(cfws)(input).into_inner();
331
332
0
        Ok(input)
333
0
    }
334
335
0
    fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
336
        use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
337
        use crate::parsing::combinator::rfc::rfc2822::{cfws, fws};
338
        use crate::parsing::combinator::{
339
            ascii_char, exactly_n_digits, first_match, n_to_m_digits, opt, sign,
340
        };
341
342
0
        let colon = ascii_char::<b':'>;
343
0
        let comma = ascii_char::<b','>;
344
345
0
        let input = opt(cfws)(input).into_inner();
346
        // This parses the weekday, but we don't actually use the value anywhere. Because of this,
347
        // just return `()` to avoid unnecessary generated code.
348
0
        let weekday = first_match(
349
0
            [
350
0
                (b"Mon".as_slice(), ()),
351
0
                (b"Tue".as_slice(), ()),
352
0
                (b"Wed".as_slice(), ()),
353
0
                (b"Thu".as_slice(), ()),
354
0
                (b"Fri".as_slice(), ()),
355
0
                (b"Sat".as_slice(), ()),
356
0
                (b"Sun".as_slice(), ()),
357
0
            ],
358
0
            false,
359
0
        )(input);
360
0
        let input = if let Some(item) = weekday {
361
0
            let input = item.into_inner();
362
0
            let input = comma(input).ok_or(InvalidLiteral)?.into_inner();
363
0
            opt(cfws)(input).into_inner()
364
        } else {
365
0
            input
366
        };
367
0
        let ParsedItem(input, day) =
368
0
            n_to_m_digits::<1, 2, _>(input).ok_or(InvalidComponent("day"))?;
369
0
        let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
370
0
        let ParsedItem(input, month) = first_match(
371
0
            [
372
0
                (b"Jan".as_slice(), Month::January),
373
0
                (b"Feb".as_slice(), Month::February),
374
0
                (b"Mar".as_slice(), Month::March),
375
0
                (b"Apr".as_slice(), Month::April),
376
0
                (b"May".as_slice(), Month::May),
377
0
                (b"Jun".as_slice(), Month::June),
378
0
                (b"Jul".as_slice(), Month::July),
379
0
                (b"Aug".as_slice(), Month::August),
380
0
                (b"Sep".as_slice(), Month::September),
381
0
                (b"Oct".as_slice(), Month::October),
382
0
                (b"Nov".as_slice(), Month::November),
383
0
                (b"Dec".as_slice(), Month::December),
384
0
            ],
385
0
            false,
386
0
        )(input)
387
0
        .ok_or(InvalidComponent("month"))?;
388
0
        let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
389
0
        let (input, year) = match exactly_n_digits::<4, u32>(input) {
390
0
            Some(item) => {
391
0
                let ParsedItem(input, year) = item
392
0
                    .flat_map(|year| if year >= 1900 { Some(year) } else { None })
393
0
                    .ok_or(InvalidComponent("year"))?;
394
0
                let input = fws(input).ok_or(InvalidLiteral)?.into_inner();
395
0
                (input, year)
396
            }
397
            None => {
398
0
                let ParsedItem(input, year) = exactly_n_digits::<2, u32>(input)
399
0
                    .map(|item| item.map(|year| if year < 50 { year + 2000 } else { year + 1900 }))
400
0
                    .ok_or(InvalidComponent("year"))?;
401
0
                let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
402
0
                (input, year)
403
            }
404
        };
405
406
0
        let ParsedItem(input, hour) =
407
0
            exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("hour"))?;
408
0
        let input = opt(cfws)(input).into_inner();
409
0
        let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
410
0
        let input = opt(cfws)(input).into_inner();
411
0
        let ParsedItem(input, minute) =
412
0
            exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("minute"))?;
413
414
0
        let (input, mut second) = if let Some(input) = colon(opt(cfws)(input).into_inner()) {
415
0
            let input = input.into_inner(); // discard the colon
416
0
            let input = opt(cfws)(input).into_inner();
417
0
            let ParsedItem(input, second) =
418
0
                exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("second"))?;
419
0
            let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
420
0
            (input, second)
421
        } else {
422
0
            (cfws(input).ok_or(InvalidLiteral)?.into_inner(), 0)
423
        };
424
425
        #[expect(
426
            clippy::unnecessary_lazy_evaluations,
427
            reason = "rust-lang/rust-clippy#8522"
428
        )]
429
0
        let zone_literal = first_match(
430
0
            [
431
0
                (b"UT".as_slice(), 0),
432
0
                (b"GMT".as_slice(), 0),
433
0
                (b"EST".as_slice(), -5),
434
0
                (b"EDT".as_slice(), -4),
435
0
                (b"CST".as_slice(), -6),
436
0
                (b"CDT".as_slice(), -5),
437
0
                (b"MST".as_slice(), -7),
438
0
                (b"MDT".as_slice(), -6),
439
0
                (b"PST".as_slice(), -8),
440
0
                (b"PDT".as_slice(), -7),
441
0
            ],
442
0
            false,
443
0
        )(input)
444
0
        .or_else(|| match input {
445
0
            [b'a'..=b'i' | b'k'..=b'z' | b'A'..=b'I' | b'K'..=b'Z', rest @ ..] => {
446
0
                Some(ParsedItem(rest, 0))
447
            }
448
0
            _ => None,
449
0
        });
450
451
0
        let (input, offset_hour, offset_minute) = if let Some(zone_literal) = zone_literal {
452
0
            let ParsedItem(input, offset_hour) = zone_literal;
453
0
            (input, offset_hour, 0)
454
        } else {
455
0
            let ParsedItem(input, offset_sign) =
456
0
                sign(input).ok_or(InvalidComponent("offset hour"))?;
457
0
            let ParsedItem(input, offset_hour) = exactly_n_digits::<2, u8>(input)
458
0
                .map(|item| {
459
0
                    item.map(|offset_hour| {
460
0
                        if offset_sign == b'-' {
461
0
                            -offset_hour.cast_signed()
462
                        } else {
463
0
                            offset_hour.cast_signed()
464
                        }
465
0
                    })
466
0
                })
467
0
                .ok_or(InvalidComponent("offset hour"))?;
468
0
            let ParsedItem(input, offset_minute) =
469
0
                exactly_n_digits::<2, u8>(input).ok_or(InvalidComponent("offset minute"))?;
470
0
            (input, offset_hour, offset_minute.cast_signed())
471
        };
472
473
0
        let input = opt(cfws)(input).into_inner();
474
475
0
        if !input.is_empty() {
476
0
            return Err(error::Parse::ParseFromDescription(
477
0
                error::ParseFromDescription::UnexpectedTrailingCharacters,
478
0
            ));
479
0
        }
480
481
0
        let mut nanosecond = 0;
482
0
        let leap_second_input = if second == 60 {
483
0
            second = 59;
484
0
            nanosecond = 999_999_999;
485
0
            true
486
        } else {
487
0
            false
488
        };
489
490
0
        let dt = (|| {
491
0
            let date = Date::from_calendar_date(year.cast_signed(), month, day)?;
492
0
            let time = Time::from_hms_nano(hour, minute, second, nanosecond)?;
493
0
            let offset = UtcOffset::from_hms(offset_hour, offset_minute, 0)?;
494
0
            Ok(OffsetDateTime::new_in_offset(date, time, offset))
495
        })()
496
0
        .map_err(TryFromParsed::ComponentRange)?;
497
498
0
        if leap_second_input && !dt.is_valid_leap_second_stand_in() {
499
0
            return Err(error::Parse::TryFromParsed(TryFromParsed::ComponentRange(
500
0
                error::ComponentRange {
501
0
                    name: "second",
502
0
                    minimum: 0,
503
0
                    maximum: 59,
504
0
                    value: 60,
505
0
                    conditional_message: Some("because leap seconds are not supported"),
506
0
                },
507
0
            )));
508
0
        }
509
510
0
        Ok(dt)
511
0
    }
512
}
513
514
impl sealed::Sealed for Rfc3339 {
515
0
    fn parse_into<'a>(
516
0
        &self,
517
0
        input: &'a [u8],
518
0
        parsed: &mut Parsed,
519
0
    ) -> Result<&'a [u8], error::Parse> {
520
        use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
521
        use crate::parsing::combinator::{
522
            any_digit, ascii_char, ascii_char_ignore_case, exactly_n_digits, sign,
523
        };
524
525
0
        let dash = ascii_char::<b'-'>;
526
0
        let colon = ascii_char::<b':'>;
527
528
0
        let input = exactly_n_digits::<4, u32>(input)
529
0
            .and_then(|item| item.consume_value(|value| parsed.set_year(value.cast_signed())))
530
0
            .ok_or(InvalidComponent("year"))?;
531
0
        let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
532
0
        let input = exactly_n_digits::<2, _>(input)
533
0
            .and_then(|item| item.flat_map(|value| Month::from_number(value).ok()))
534
0
            .and_then(|item| item.consume_value(|value| parsed.set_month(value)))
535
0
            .ok_or(InvalidComponent("month"))?;
536
0
        let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
537
0
        let input = exactly_n_digits::<2, _>(input)
538
0
            .and_then(|item| item.consume_value(|value| parsed.set_day(value)))
539
0
            .ok_or(InvalidComponent("day"))?;
540
541
        // RFC3339 allows any separator, not just `T`, not just `space`.
542
        // cf. Section 5.6: Internet Date/Time Format:
543
        //   NOTE: ISO 8601 defines date and time separated by "T".
544
        //   Applications using this syntax may choose, for the sake of
545
        //   readability, to specify a full-date and full-time separated by
546
        //   (say) a space character.
547
        // Specifically, rusqlite uses space separators.
548
0
        let input = input.get(1..).ok_or(InvalidComponent("separator"))?;
549
550
0
        let input = exactly_n_digits::<2, _>(input)
551
0
            .and_then(|item| item.consume_value(|value| parsed.set_hour_24(value)))
552
0
            .ok_or(InvalidComponent("hour"))?;
553
0
        let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
554
0
        let input = exactly_n_digits::<2, _>(input)
555
0
            .and_then(|item| item.consume_value(|value| parsed.set_minute(value)))
556
0
            .ok_or(InvalidComponent("minute"))?;
557
0
        let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
558
0
        let input = exactly_n_digits::<2, _>(input)
559
0
            .and_then(|item| item.consume_value(|value| parsed.set_second(value)))
560
0
            .ok_or(InvalidComponent("second"))?;
561
0
        let input = if let Some(ParsedItem(input, ())) = ascii_char::<b'.'>(input) {
562
0
            let ParsedItem(mut input, mut value) = any_digit(input)
563
0
                .ok_or(InvalidComponent("subsecond"))?
564
0
                .map(|v| (v - b'0').extend::<u32>() * 100_000_000);
565
566
0
            let mut multiplier = 10_000_000;
567
0
            while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
568
0
                value += (digit - b'0').extend::<u32>() * multiplier;
569
0
                input = new_input;
570
0
                multiplier /= 10;
571
0
            }
572
573
0
            parsed
574
0
                .set_subsecond(value)
575
0
                .ok_or(InvalidComponent("subsecond"))?;
576
0
            input
577
        } else {
578
0
            input
579
        };
580
581
        // The RFC explicitly allows leap seconds.
582
0
        parsed.leap_second_allowed = true;
583
584
0
        if let Some(ParsedItem(input, ())) = ascii_char_ignore_case::<b'Z'>(input) {
585
0
            parsed
586
0
                .set_offset_hour(0)
587
0
                .ok_or(InvalidComponent("offset hour"))?;
588
0
            parsed
589
0
                .set_offset_minute_signed(0)
590
0
                .ok_or(InvalidComponent("offset minute"))?;
591
0
            parsed
592
0
                .set_offset_second_signed(0)
593
0
                .ok_or(InvalidComponent("offset second"))?;
594
0
            return Ok(input);
595
0
        }
596
597
0
        let ParsedItem(input, offset_sign) = sign(input).ok_or(InvalidComponent("offset hour"))?;
598
0
        let input = exactly_n_digits::<2, u8>(input)
599
0
            .and_then(|item| {
600
0
                item.filter(|&offset_hour| offset_hour <= 23)?
601
0
                    .map(|offset_hour| {
602
0
                        if offset_sign == b'-' {
603
0
                            -offset_hour.cast_signed()
604
                        } else {
605
0
                            offset_hour.cast_signed()
606
                        }
607
0
                    })
608
0
                    .consume_value(|value| parsed.set_offset_hour(value))
609
0
            })
610
0
            .ok_or(InvalidComponent("offset hour"))?;
611
0
        let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
612
0
        let input = exactly_n_digits::<2, u8>(input)
613
0
            .and_then(|item| {
614
0
                item.map(|offset_minute| {
615
0
                    if offset_sign == b'-' {
616
0
                        -offset_minute.cast_signed()
617
                    } else {
618
0
                        offset_minute.cast_signed()
619
                    }
620
0
                })
621
0
                .consume_value(|value| parsed.set_offset_minute_signed(value))
622
0
            })
623
0
            .ok_or(InvalidComponent("offset minute"))?;
624
625
0
        Ok(input)
626
0
    }
627
628
23.3k
    fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
629
        use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
630
        use crate::parsing::combinator::{
631
            any_digit, ascii_char, ascii_char_ignore_case, exactly_n_digits, sign,
632
        };
633
634
23.3k
        let dash = ascii_char::<b'-'>;
635
23.3k
        let colon = ascii_char::<b':'>;
636
637
23.1k
        let ParsedItem(input, year) =
638
23.3k
            exactly_n_digits::<4, u32>(input).ok_or(InvalidComponent("year"))?;
639
23.1k
        let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
640
23.1k
        let ParsedItem(input, month) =
641
23.1k
            exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("month"))?;
642
23.1k
        let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
643
23.0k
        let ParsedItem(input, day) =
644
23.1k
            exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("day"))?;
645
646
        // RFC3339 allows any separator, not just `T`, not just `space`.
647
        // cf. Section 5.6: Internet Date/Time Format:
648
        //   NOTE: ISO 8601 defines date and time separated by "T".
649
        //   Applications using this syntax may choose, for the sake of
650
        //   readability, to specify a full-date and full-time separated by
651
        //   (say) a space character.
652
        // Specifically, rusqlite uses space separators.
653
23.0k
        let input = input.get(1..).ok_or(InvalidComponent("separator"))?;
654
655
23.0k
        let ParsedItem(input, hour) =
656
23.0k
            exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("hour"))?;
657
23.0k
        let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
658
22.9k
        let ParsedItem(input, minute) =
659
22.9k
            exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("minute"))?;
660
22.9k
        let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
661
22.9k
        let ParsedItem(input, mut second) =
662
22.9k
            exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("second"))?;
663
22.9k
        let ParsedItem(input, mut nanosecond) =
664
22.9k
            if let Some(ParsedItem(input, ())) = ascii_char::<b'.'>(input) {
665
1.83k
                let ParsedItem(mut input, mut value) = any_digit(input)
666
1.83k
                    .ok_or(InvalidComponent("subsecond"))?
667
1.80k
                    .map(|v| (v - b'0').extend::<u32>() * 100_000_000);
668
669
1.80k
                let mut multiplier = 10_000_000;
670
7.47k
                while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
671
5.67k
                    value += (digit - b'0').extend::<u32>() * multiplier;
672
5.67k
                    input = new_input;
673
5.67k
                    multiplier /= 10;
674
5.67k
                }
675
676
1.80k
                ParsedItem(input, value)
677
            } else {
678
21.1k
                ParsedItem(input, 0)
679
            };
680
22.8k
        let ParsedItem(input, offset) = {
681
22.9k
            if let Some(ParsedItem(input, ())) = ascii_char_ignore_case::<b'Z'>(input) {
682
2.11k
                ParsedItem(input, UtcOffset::UTC)
683
            } else {
684
20.7k
                let ParsedItem(input, offset_sign) =
685
20.7k
                    sign(input).ok_or(InvalidComponent("offset hour"))?;
686
20.7k
                let ParsedItem(input, offset_hour) = exactly_n_digits::<2, u8>(input)
687
20.7k
                    .and_then(|parsed| parsed.filter(|&offset_hour| offset_hour <= 23))
688
20.7k
                    .ok_or(InvalidComponent("offset hour"))?;
689
20.7k
                let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
690
20.6k
                let ParsedItem(input, offset_minute) =
691
20.7k
                    exactly_n_digits::<2, u8>(input).ok_or(InvalidComponent("offset minute"))?;
692
20.6k
                UtcOffset::from_hms(
693
20.6k
                    if offset_sign == b'-' {
694
3.38k
                        -offset_hour.cast_signed()
695
                    } else {
696
17.3k
                        offset_hour.cast_signed()
697
                    },
698
20.6k
                    if offset_sign == b'-' {
699
3.38k
                        -offset_minute.cast_signed()
700
                    } else {
701
17.3k
                        offset_minute.cast_signed()
702
                    },
703
                    0,
704
                )
705
20.6k
                .map(|offset| ParsedItem(input, offset))
706
20.6k
                .map_err(|mut err| {
707
                    // Provide the user a more accurate error.
708
6
                    if err.name == "hours" {
709
0
                        err.name = "offset hour";
710
6
                    } else if err.name == "minutes" {
711
6
                        err.name = "offset minute";
712
6
                    }
713
6
                    err
714
6
                })
715
20.6k
                .map_err(TryFromParsed::ComponentRange)?
716
            }
717
        };
718
719
22.8k
        if !input.is_empty() {
720
19
            return Err(error::Parse::ParseFromDescription(
721
19
                error::ParseFromDescription::UnexpectedTrailingCharacters,
722
19
            ));
723
22.7k
        }
724
725
        // The RFC explicitly permits leap seconds. We don't currently support them, so treat it as
726
        // the preceding nanosecond. However, leap seconds can only occur as the last second of the
727
        // month UTC.
728
22.7k
        let leap_second_input = if second == 60 {
729
11.5k
            second = 59;
730
11.5k
            nanosecond = 999_999_999;
731
11.5k
            true
732
        } else {
733
11.2k
            false
734
        };
735
736
22.7k
        let date = Month::from_number(month)
737
22.7k
            .and_then(|month| Date::from_calendar_date(year.cast_signed(), month, day))
738
22.7k
            .map_err(TryFromParsed::ComponentRange)?;
739
22.7k
        let time = Time::from_hms_nano(hour, minute, second, nanosecond)
740
22.7k
            .map_err(TryFromParsed::ComponentRange)?;
741
22.7k
        let dt = OffsetDateTime::new_in_offset(date, time, offset);
742
743
22.7k
        if leap_second_input && !dt.is_valid_leap_second_stand_in() {
744
104
            return Err(error::Parse::TryFromParsed(TryFromParsed::ComponentRange(
745
104
                error::ComponentRange {
746
104
                    name: "second",
747
104
                    minimum: 0,
748
104
                    maximum: 59,
749
104
                    value: 60,
750
104
                    conditional_message: Some("because leap seconds are not supported"),
751
104
                },
752
104
            )));
753
22.6k
        }
754
755
22.6k
        Ok(dt)
756
23.3k
    }
757
}
758
759
impl<const CONFIG: EncodedConfig> sealed::Sealed for Iso8601<CONFIG> {
760
    #[inline]
761
0
    fn parse_into<'a>(
762
0
        &self,
763
0
        mut input: &'a [u8],
764
0
        parsed: &mut Parsed,
765
0
    ) -> Result<&'a [u8], error::Parse> {
766
        use crate::parsing::combinator::rfc::iso8601::ExtendedKind;
767
768
0
        let mut extended_kind = ExtendedKind::Unknown;
769
0
        let mut date_is_present = false;
770
0
        let mut time_is_present = false;
771
0
        let mut offset_is_present = false;
772
0
        let mut first_error = None;
773
774
0
        parsed.leap_second_allowed = true;
775
776
0
        match Self::parse_date(parsed, &mut extended_kind)(input) {
777
0
            Ok(new_input) => {
778
0
                input = new_input;
779
0
                date_is_present = true;
780
0
            }
781
0
            Err(err) => {
782
0
                first_error.get_or_insert(err);
783
0
            }
784
        }
785
786
0
        match Self::parse_time(parsed, &mut extended_kind, date_is_present)(input) {
787
0
            Ok(new_input) => {
788
0
                input = new_input;
789
0
                time_is_present = true;
790
0
            }
791
0
            Err(err) => {
792
0
                first_error.get_or_insert(err);
793
0
            }
794
        }
795
796
        // If a date and offset are present, a time must be as well.
797
0
        if !date_is_present || time_is_present {
798
0
            match Self::parse_offset(parsed, &mut extended_kind)(input) {
799
0
                Ok(new_input) => {
800
0
                    input = new_input;
801
0
                    offset_is_present = true;
802
0
                }
803
0
                Err(err) => {
804
0
                    first_error.get_or_insert(err);
805
0
                }
806
            }
807
0
        }
808
809
0
        if !date_is_present && !time_is_present && !offset_is_present {
810
0
            match first_error {
811
0
                Some(err) => return Err(err),
812
0
                None => bug!("an error should be present if no components were parsed"),
813
            }
814
0
        }
815
816
0
        Ok(input)
817
0
    }
818
}