1 use crate::datetime
::*;
2 use crate::parser
::errors
::CustomError
;
3 use crate::parser
::trivia
::from_utf8_unchecked
;
4 use combine
::parser
::byte
::byte
;
5 use combine
::parser
::range
::{recognize, take_while1}
;
6 use combine
::stream
::RangeStream
;
9 // ;; Date and Time (as defined in RFC 3339)
11 // date-time = offset-date-time / local-date-time / local-date / local-time
12 // offset-date-time = full-date "T" full-time
13 // local-date-time = full-date "T" partial-time
14 // local-date = full-date
15 // local-time = partial-time
16 // full-time = partial-time time-offset
17 parse
!(date_time() -> Datetime
, {
23 satisfy(is_time_delim
),
24 look_ahead(time_hour())
27 optional(time_offset()),
33 Some((_
, time
, offset
)) => {
34 Datetime { date: Some(date), time: Some(time), offset }
38 Datetime { date: Some(date), time: None, offset: None}
44 .message("While parsing a Time")
49 .message("While parsing a Date-Time")
52 // full-date = date-fullyear "-" date-month "-" date-mday
53 parse
!(full_date() -> Date
, {
55 attempt((date_fullyear(), byte(b'
-'
))),
59 ).map(|((year
, _
), month
, _
, day
)| {
60 Date { year, month, day }
64 // partial-time = time-hour ":" time-minute ":" time-second [time-secfrac]
65 parse
!(partial_time() -> Time
, {
74 optional(attempt(time_secfrac())),
75 ).map(|((hour
, _
), minute
, _
, second
, nanosecond
)| {
76 Time { hour, minute, second, nanosecond: nanosecond.unwrap_or_default() }
80 // time-offset = "Z" / time-numoffset
81 // time-numoffset = ( "+" / "-" ) time-hour ":" time-minute
82 parse
!(time_offset() -> Offset
, {
83 attempt(satisfy(|c
| c
== b'Z'
|| c
== b'z'
)).map(|_
| Offset
::Z
)
86 attempt(choice([byte(b'
+'
), byte(b'
-'
)])),
90 ).map(|(sign
, hours
, _
, minutes
)| {
91 let hours
= hours
as i8;
92 let hours
= match sign
{
95 _
=> unreachable
!("Parser prevents this"),
97 Offset
::Custom { hours, minutes }
99 ).message("While parsing a Time Offset")
102 // date-fullyear = 4DIGIT
103 parse
!(date_fullyear() -> u16, {
104 signed_digits(4).map(|d
| d
as u16)
107 // date-month = 2DIGIT ; 01-12
108 parse
!(date_month() -> u8, {
109 unsigned_digits(2).map(|d
| d
as u8).and_then(|v
| {
110 if (1..=12).contains(&v
) {
113 Err(CustomError
::OutOfRange
)
118 // date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year
119 parse
!(date_mday() -> u8, {
120 unsigned_digits(2).map(|d
| d
as u8).and_then(|v
| {
121 if (1..=31).contains(&v
) {
124 Err(CustomError
::OutOfRange
)
129 // time-delim = "T" / %x20 ; T, t, or space
130 fn is_time_delim(c
: u8) -> bool
{
131 matches
!(c
, b'T'
| b't'
| b' '
)
134 // time-hour = 2DIGIT ; 00-23
135 parse
!(time_hour() -> u8, {
136 unsigned_digits(2).map(|d
| d
as u8).and_then(|v
| {
137 if (0..=23).contains(&v
) {
140 Err(CustomError
::OutOfRange
)
145 // time-minute = 2DIGIT ; 00-59
146 parse
!(time_minute() -> u8, {
147 unsigned_digits(2).map(|d
| d
as u8).and_then(|v
| {
148 if (0..=59).contains(&v
) {
151 Err(CustomError
::OutOfRange
)
156 // time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second rules
157 parse
!(time_second() -> u8, {
158 unsigned_digits(2).map(|d
| d
as u8).and_then(|v
| {
159 if (0..=60).contains(&v
) {
162 Err(CustomError
::OutOfRange
)
167 // time-secfrac = "." 1*DIGIT
168 parse
!(time_secfrac() -> u32, {
169 static SCALE
: [u32; 10] =
170 [0, 100_000_000, 10_000_000, 1_000_000, 100_000, 10_000, 1_000, 100, 10, 1];
171 byte(b'
.'
).and(take_while1(|c
: u8| c
.is_ascii_digit())).and_then
::<_
, _
, CustomError
>(|(_
, repr
): (u8, &[u8])| {
172 let mut repr
= unsafe { from_utf8_unchecked(repr, "`is_ascii_digit` filters out on-ASCII") }
;
173 let max_digits
= SCALE
.len() - 1;
174 if max_digits
< repr
.len() {
175 // Millisecond precision is required. Further precision of fractional seconds is
176 // implementation-specific. If the value contains greater precision than the
177 // implementation can support, the additional precision must be truncated, not rounded.
178 repr
= &repr
[0..max_digits
];
181 let v
= repr
.parse
::<u32>().map_err(|_
| CustomError
::OutOfRange
)?
;
182 let num_digits
= repr
.len();
184 // scale the number accordingly.
185 let scale
= SCALE
.get(num_digits
).ok_or(CustomError
::OutOfRange
)?
;
186 let v
= v
.checked_mul(*scale
).ok_or(CustomError
::OutOfRange
)?
;
191 parse
!(signed_digits(count
: usize) -> i32, {
192 recognize(skip_count_min_max(
194 satisfy(|c
: u8| c
.is_ascii_digit()),
195 )).and_then(|b
: &[u8]| {
196 let s
= unsafe { from_utf8_unchecked(b, "`is_ascii_digit` filters out on-ASCII") }
;
201 parse
!(unsigned_digits(count
: usize) -> u32, {
202 recognize(skip_count_min_max(
204 satisfy(|c
: u8| c
.is_ascii_digit()),
205 )).and_then(|b
: &[u8]| {
206 let s
= unsafe { from_utf8_unchecked(b, "`is_ascii_digit` filters out on-ASCII") }
;