1use jiff::civil::DateTime;
10use nom::{
11 IResult, Parser,
12 branch::alt,
13 bytes::complete::take_while_m_n,
14 character::complete::char,
15 combinator::{map, map_res},
16};
17
18pub fn parse_date(input: &str) -> IResult<&str, DateTime> {
27 alt((
28 parse_full_datetime_with_timezone,
29 parse_full_datetime,
30 parse_date_only,
31 ))
32 .parse(input)
33}
34
35fn parse_full_datetime_with_timezone(input: &str) -> IResult<&str, DateTime> {
37 map_res(
38 (
39 parse_date_components,
40 alt((char('T'), char(' '))),
41 parse_time_components,
42 parse_optional_nanosecond,
43 char('Z'),
44 ),
45 |((year, month, day), _, (hour, minute, second), nanos, _)| {
46 DateTime::new(year, month, day, hour, minute, second, nanos)
48 },
49 )
50 .parse(input)
51}
52
53fn parse_full_datetime(input: &str) -> IResult<&str, DateTime> {
55 map_res(
56 (
57 parse_date_components,
58 alt((char('T'), char(' '))),
59 parse_time_components,
60 parse_optional_nanosecond,
61 ),
62 |((year, month, day), _, (hour, minute, second), nanosecond)| {
63 DateTime::new(year, month, day, hour, minute, second, nanosecond)
64 },
65 )
66 .parse(input)
67}
68
69fn parse_date_only(input: &str) -> IResult<&str, DateTime> {
71 map_res(parse_date_components, |(year, month, day)| {
72 DateTime::new(year, month, day, 0, 0, 0, 0)
74 })
75 .parse(input)
76}
77
78fn parse_date_components(input: &str) -> IResult<&str, (i16, i8, i8)> {
80 map(
81 (
82 parse_four_digit_number,
83 char('-'),
84 parse_two_digit_number,
85 char('-'),
86 parse_two_digit_number,
87 ),
88 |(year, _, month, _, day)| (year, month, day),
89 )
90 .parse(input)
91}
92
93fn parse_time_components(input: &str) -> IResult<&str, (i8, i8, i8)> {
95 map(
96 (
97 parse_two_digit_number,
98 char(':'),
99 parse_two_digit_number,
100 char(':'),
101 parse_two_digit_number,
102 ),
103 |(hour, _, minute, _, second)| (hour, minute, second),
104 )
105 .parse(input)
106}
107
108fn parse_optional_nanosecond(input: &str) -> IResult<&str, i32> {
110 alt((
111 map(
112 (
113 char('.'),
114 take_while_m_n(1, 3, |c: char| c.is_ascii_digit()),
115 ),
116 |(_, digits): (char, &str)| {
117 let nanosecond: i32 = digits.parse().unwrap_or(0) * 1000000;
119 match digits.len() {
121 1 => nanosecond * 100,
122 2 => nanosecond * 10,
123 _ => nanosecond,
124 }
125 },
126 ),
127 nom::combinator::success(0i32),
128 ))
129 .parse(input)
130}
131
132fn parse_four_digit_number(input: &str) -> IResult<&str, i16> {
134 map_res(
135 take_while_m_n(4, 4, |c: char| c.is_ascii_digit()),
136 |s: &str| s.parse::<i16>(),
137 )
138 .parse(input)
139}
140
141fn parse_two_digit_number(input: &str) -> IResult<&str, i8> {
143 map_res(
144 take_while_m_n(2, 2, |c: char| c.is_ascii_digit()),
145 |s: &str| s.parse::<i8>(),
146 )
147 .parse(input)
148}