Skip to main content

winnow_iso8601/
partial_datetime.rs

1use crate::partial_date::{partial_date, partial_end_date};
2use crate::partial_time::{partial_end_base_time, partial_time};
3use core::str;
4use winnow::combinator::{alt, opt, preceded, trace};
5use winnow::error::ParserError;
6use winnow::stream::{AsBStr, AsChar, Compare, Stream, StreamIsPartial};
7use winnow::token::literal;
8use winnow::{seq, Parser, Result};
9use winnow_datetime::types::PartialDateTime;
10
11// partial date time
12pub(crate) fn partial_datetime<'i, Input, Error>(
13    input: &mut Input,
14) -> Result<PartialDateTime, Error>
15where
16    Input: StreamIsPartial + Stream + Compare<&'i str>,
17    <Input as Stream>::Slice: AsBStr,
18    <Input as Stream>::Token: AsChar + Clone,
19    Error: ParserError<Input>,
20{
21    trace("partial_datetime", move |input: &mut Input| {
22        seq!((
23            opt(partial_date),
24            opt(preceded(alt((literal(" "), literal("T"))), partial_time))
25        ))
26        .verify(|(d, t)| d.is_some() || t.is_some())
27        .map(|(d, t)| PartialDateTime { date: d, time: t })
28        .parse_next(input)
29    })
30    .parse_next(input)
31}
32
33pub(crate) fn partial_end_datetime<'i, Input, Error>(
34    input: &mut Input,
35    start_datetime: &PartialDateTime,
36) -> Result<PartialDateTime, Error>
37where
38    Input: StreamIsPartial + Stream + Compare<&'i str>,
39    <Input as Stream>::Slice: AsBStr,
40    <Input as Stream>::Token: AsChar + Clone,
41    Error: ParserError<Input>,
42{
43    trace(
44        "partial_end_datetime",
45        move |input: &mut Input| match start_datetime {
46            PartialDateTime {
47                date: start_date,
48                time: start_time,
49            } => {
50                let mut end_date = None;
51                let mut end_time = None;
52
53                if start_date.is_none() && start_date.is_none() {
54                    return Err(ParserError::from_input(input));
55                }
56
57                if let Some(d) = start_date {
58                    end_date = partial_end_date(input, d).map(Some)?;
59                }
60
61                if let Some(t) = start_time {
62                    _ = literal(" ").parse_next(input)?;
63
64                    end_time = partial_end_base_time(input, t).map(Some)?;
65                }
66
67                Ok(PartialDateTime {
68                    date: end_date,
69                    time: end_time,
70                })
71            }
72        },
73    )
74    .parse_next(input)
75}
76#[cfg(test)]
77mod parsers {
78    use crate::partial_datetime::{partial_datetime, partial_end_datetime};
79    use winnow::error::InputError;
80
81    use winnow_datetime::types::{PartialDate, PartialDateTime};
82
83    #[test]
84    fn partial_datetime_parsing() {
85        // Year
86        assert_eq!(
87            partial_datetime::<_, InputError<_>>(&mut "2015").unwrap(),
88            PartialDateTime {
89                date: Some(PartialDate::Year { year: Some(2015) }),
90                time: None,
91            }
92        );
93        // YMD
94        assert_eq!(
95            partial_datetime::<_, InputError<_>>(&mut "2015-06-26").unwrap(),
96            PartialDateTime {
97                date: Some(PartialDate::YMD {
98                    year: Some(2015),
99                    month: Some(6),
100                    day: Some(26)
101                }),
102                time: None,
103            }
104        );
105        assert_eq!(
106            partial_datetime::<_, InputError<_>>(&mut "2015-06").unwrap(),
107            PartialDateTime {
108                date: Some(PartialDate::YMD {
109                    year: Some(2015),
110                    month: Some(6),
111                    day: None
112                }),
113                time: None,
114            }
115        );
116        // YWD
117        assert_eq!(
118            partial_datetime::<_, InputError<_>>(&mut "2015-W05-6").unwrap(),
119            PartialDateTime {
120                date: Some(PartialDate::YWD {
121                    year: Some(2015),
122                    week: Some(5),
123                    day: Some(6)
124                }),
125                time: None,
126            }
127        );
128        assert_eq!(
129            partial_datetime::<_, InputError<_>>(&mut "2015-W05-1").unwrap(),
130            PartialDateTime {
131                date: Some(PartialDate::YWD {
132                    year: Some(2015),
133                    week: Some(5),
134                    day: Some(1)
135                }),
136                time: None,
137            }
138        );
139        assert_eq!(
140            partial_datetime::<_, InputError<_>>(&mut "2015-W05").unwrap(),
141            PartialDateTime {
142                date: Some(PartialDate::YWD {
143                    year: Some(2015),
144                    week: Some(5),
145                    day: None
146                }),
147                time: None,
148            }
149        );
150        //Ordinal
151        assert_eq!(
152            partial_datetime::<_, InputError<_>>(&mut "2015-156").unwrap(),
153            PartialDateTime {
154                date: Some(PartialDate::YDDD {
155                    year: Some(2015),
156                    day: Some(156)
157                }),
158                time: None,
159            }
160        );
161        assert_eq!(
162            partial_datetime::<_, InputError<_>>(&mut "2015-156").unwrap(),
163            PartialDateTime {
164                date: Some(PartialDate::YDDD {
165                    year: Some(2015),
166                    day: Some(156)
167                }),
168                time: None,
169            }
170        );
171    }
172
173    #[test]
174    fn partial_ymd() {
175        assert_eq!(
176            partial_end_datetime::<_, InputError<_>>(
177                &mut "2015-06-26",
178                &PartialDateTime {
179                    date: Some(PartialDate::YMD {
180                        year: Some(2015),
181                        month: Some(6),
182                        day: Some(25)
183                    }),
184                    time: None,
185                }
186            )
187            .unwrap(),
188            PartialDateTime {
189                date: Some(PartialDate::YMD {
190                    year: Some(2015),
191                    month: Some(6),
192                    day: Some(26)
193                }),
194                time: None,
195            }
196        );
197        assert_eq!(
198            partial_end_datetime::<_, InputError<_>>(
199                &mut "06-26",
200                &PartialDateTime {
201                    date: Some(PartialDate::YMD {
202                        year: Some(2015),
203                        month: Some(6),
204                        day: Some(25)
205                    }),
206                    time: None,
207                }
208            )
209            .unwrap(),
210            PartialDateTime {
211                date: Some(PartialDate::YMD {
212                    year: Some(2015),
213                    month: Some(6),
214                    day: Some(26)
215                }),
216                time: None,
217            }
218        );
219        assert_eq!(
220            partial_end_datetime::<_, InputError<_>>(
221                &mut "26",
222                &PartialDateTime {
223                    date: Some(PartialDate::YMD {
224                        year: Some(2015),
225                        month: Some(6),
226                        day: Some(25)
227                    }),
228                    time: None,
229                }
230            )
231            .unwrap(),
232            PartialDateTime {
233                date: Some(PartialDate::YMD {
234                    year: Some(2015),
235                    month: Some(6),
236                    day: Some(26)
237                }),
238                time: None,
239            }
240        );
241    }
242
243    #[test]
244    fn partial_ywd() {
245        assert_eq!(
246            partial_end_datetime::<_, InputError<_>>(
247                &mut "2024-W51-4",
248                &PartialDateTime {
249                    date: Some(PartialDate::YWD {
250                        year: Some(2024),
251                        week: Some(51),
252                        day: Some(3)
253                    }),
254                    time: None,
255                }
256            )
257            .unwrap(),
258            PartialDateTime {
259                date: Some(PartialDate::YWD {
260                    year: Some(2024),
261                    week: Some(51),
262                    day: Some(4)
263                }),
264                time: None,
265            }
266        );
267        assert_eq!(
268            partial_end_datetime::<_, InputError<_>>(
269                &mut "W51-4",
270                &PartialDateTime {
271                    date: Some(PartialDate::YWD {
272                        year: Some(2024),
273                        week: Some(51),
274                        day: Some(3)
275                    }),
276                    time: None,
277                }
278            )
279            .unwrap(),
280            PartialDateTime {
281                date: Some(PartialDate::YWD {
282                    year: Some(2024),
283                    week: Some(51),
284                    day: Some(4)
285                }),
286                time: None,
287            }
288        );
289        assert_eq!(
290            partial_end_datetime::<_, InputError<_>>(
291                &mut "4",
292                &PartialDateTime {
293                    date: Some(PartialDate::YWD {
294                        year: Some(2024),
295                        week: Some(51),
296                        day: Some(3)
297                    }),
298                    time: None,
299                }
300            )
301            .unwrap(),
302            PartialDateTime {
303                date: Some(PartialDate::YWD {
304                    year: Some(2024),
305                    week: Some(51),
306                    day: Some(4)
307                }),
308                time: None,
309            }
310        );
311    }
312
313    #[test]
314    fn partial_yddd() {
315        assert_eq!(
316            partial_end_datetime::<_, InputError<_>>(
317                &mut "083",
318                &PartialDateTime {
319                    date: Some(PartialDate::YDDD {
320                        year: Some(2025),
321                        day: Some(82)
322                    }),
323                    time: None,
324                }
325            )
326            .unwrap(),
327            PartialDateTime {
328                date: Some(PartialDate::YDDD {
329                    year: Some(2025),
330                    day: Some(83)
331                }),
332                time: None,
333            }
334        );
335    }
336}