winnow_datetime/
types.rs

1use core::fmt;
2#[cfg(feature = "serde")]
3use serde::{Deserialize, Serialize};
4
5/// Compound struct, holds Date and Time.
6/// ```
7/// # use std::str::FromStr;
8/// use winnow_datetime::DateTime;
9/// /*
10/// assert_eq!(
11///     winnow_datetime::DateTime::from_str("2023-02-18T17:08:08.793Z"),
12///     Ok(winnow_datetime::DateTime {
13///         date: winnow_datetime::Date::YMD{ year: 2023, month: 2, day: 18},
14///         time: winnow_datetime::Time{ hour: 17, minute: 8, second: 8, millisecond: 793, offset: Offset { offset_hours: 0, offset_minutes: 00 }}
15///     })
16/// )
17/// */
18/// ```
19#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
20#[derive(Eq, PartialEq, Debug, Copy, Clone, Default)]
21pub struct DateTime {
22    /// The date part
23    pub date: Date,
24    /// The time part
25    pub time: Time,
26}
27
28#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
29#[derive(Eq, PartialEq, Debug, Copy, Clone, Default)]
30pub struct PartialDateTime {
31    // optional date part
32    pub date: Option<PartialDate>,
33    // optional time part
34    pub time: Option<PartialTime>,
35}
36
37pub trait OffsetFormat: Sized {
38    type Err;
39
40    // Format the date
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
42
43    // Parse a date from a string
44    fn parse(s: &str) -> Result<Self, Self::Err>;
45}
46
47/// A date object.
48#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
49#[derive(Eq, PartialEq, Debug, Copy, Clone)]
50pub enum Date {
51    /// consists of year, month and day of month
52    YMD { year: i32, month: u32, day: u32 },
53    /// consists of year, week and day of week
54    Week { year: i32, week: u32, day: u32 },
55    /// consists of year and day of year
56    Ordinal { year: i32, day: u32 },
57}
58
59impl Default for Date {
60    fn default() -> Date {
61        Date::YMD {
62            year: 0,
63            month: 0,
64            day: 0,
65        }
66    }
67}
68
69#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
70#[derive(Eq, PartialEq, Debug, Copy, Clone)]
71pub enum PartialDate {
72    /// a standalone year
73    Year { year: Option<i32> },
74    /// consists of year, month and day of month
75    YMD {
76        year: Option<i32>,
77        month: Option<u32>,
78        day: Option<u32>,
79    },
80    /// consists of year, week and day of week
81    YWD {
82        year: Option<i32>,
83        week: Option<u32>,
84        day: Option<u32>,
85    },
86    /// consists of year and day of year
87    YDDD { year: Option<i32>, day: Option<u32> },
88}
89
90/// A time object.
91#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
92#[derive(Eq, PartialEq, Debug, Copy, Clone, Default)]
93pub struct Time {
94    /// a 24th of a day
95    pub hour: u32,
96    /// 60 discrete parts of an hour
97    pub minute: u32,
98    /// a minute are 60 of these
99    pub second: u32,
100    /// everything after a `.`
101    pub millisecond: u32,
102    /// Note, offset can't be partial, so a regular Offset is used
103    pub offset: Option<Offset>,
104}
105
106impl Time {
107    /// Change this time's offset.
108    ///
109    /// # Arguments
110    ///
111    /// * `tzo` - A tuple of `(hours, minutes)` specifying the offset offset from UTC.
112    pub fn set_tz(&self, tzo: Option<(i32, i32)>) -> Time {
113        let mut t = *self;
114
115        if let Some(tzo) = tzo {
116            t.offset = Some(Offset {
117                offset_hours: tzo.0,
118                offset_minutes: tzo.1,
119            });
120        } else {
121            t.offset = None;
122        }
123
124        t
125    }
126}
127
128#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
129#[derive(Eq, PartialEq, Debug, Copy, Clone)]
130pub struct PartialTime {
131    pub hour: Option<u32>,
132    pub minute: Option<u32>,
133    pub second: Option<u32>,
134    pub millisecond: Option<u32>,
135    pub offset: Option<Offset>,
136}
137
138/// struct holding an optional number of repetitions and an `IntervalRange`
139#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
140#[derive(Eq, PartialEq, Debug, Copy, Clone)]
141pub struct Interval {
142    pub repetitions: Option<Option<u32>>,
143    pub range: IntervalRange,
144}
145
146///     Closed - Start date and end date, such as "2007-03-01T13:00:00Z/2008-05-11T15:30:00Z"
147///     Closed Start - Start and time period, such as "2007-03-01T13:00:00Z/P1Y2M10DT2H30M"
148///     Closed End - and end, such as "P1Y2M10DT2H30M/2008-05-11T15:30:00Z"
149///     Open -  such as "P1Y2M10DT2H30M", with additional context information
150#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
151#[derive(Eq, PartialEq, Debug, Copy, Clone)]
152pub enum IntervalRange {
153    Closed {
154        start: PartialDateTime,
155        end: PartialDateTime,
156    },
157    ClosedStart {
158        start: PartialDateTime,
159        duration: Duration,
160    },
161    ClosedEnd {
162        duration: Duration,
163        end: PartialDateTime,
164    },
165    Open {
166        duration: Duration,
167    },
168}
169
170/// Struct holding offset offsets
171#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
172#[derive(Eq, PartialEq, Debug, Copy, Clone, Default)]
173pub struct Offset {
174    /// hour offset offset
175    pub offset_hours: i32,
176    /// minute offset offset
177    pub offset_minutes: i32,
178}
179
180/// A time duration.
181///
182/// ## Duration Grammar
183///
184/// | Duration     | ABNF Description                                     |
185/// | ------------ | ---------------------------------------------------- |
186/// | `dur-second` | 1*DIGIT "S"                                          |
187/// | `dur-minute` | 1*DIGIT "M" [`dur-second`]                           |
188/// | `dur-hour`   | 1*DIGIT "H" [`dur-minute`]                           |
189/// | `dur-time`   | "T" (`dur-hour` / `dur-minute` / `dur-second`)       |
190/// | `dur-day`    | 1*DIGIT "D"                                          |
191/// | `dur-week`   | 1*DIGIT "W"                                          |
192/// | `dur-month`  | 1*DIGIT "M" [`dur-day`]                              |
193/// | `dur-year`   | 1*DIGIT "Y" [`dur-month`]                            |
194/// | `dur-date`   | (`dur-day` / `dur-month` / `dur-year`) [`dur-time`]  |
195/// | `duration`   | "P" (`dur-date` / `dur-time` / `dur-week`)           |
196///
197/// ## Examples
198/// ```
199/// // 1 year, 11 months, 16 days, 23 hours, 26 minutes, 59 seconds, 123 milliseconds
200/// use winnow_datetime::types::Duration;
201///
202/// let d = Duration {
203///      years: 1,
204///      months: 11,
205///      weeks: 0,
206///      days: 16,
207///      hours: 23,
208///      minutes: 26,
209///      seconds: 59,
210///      milliseconds: Some(0.123)
211/// };
212/// ```
213#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(default))]
214#[derive(Debug, Default, Copy, Clone)]
215pub struct Duration {
216    /// Number of calendar years
217    pub years: u32,
218    /// Number of months
219    pub months: u32,
220    /// Number of weeks
221    pub weeks: u32,
222    /// Number of days
223    pub days: u32,
224    /// Number of hours
225    pub hours: u32,
226    /// Number of minutes
227    pub minutes: u32,
228    /// Number of seconds
229    pub seconds: u32,
230    /// Number of milliseconds
231    pub milliseconds: Option<f32>,
232}
233
234impl PartialEq for Duration {
235    fn eq(&self, other: &Self) -> bool {
236        self.years == other.years
237            && self.months == other.months
238            && self.weeks == other.weeks
239            && self.days == other.days
240            && self.hours == other.hours
241            && self.minutes == other.minutes
242            && self.seconds == other.seconds
243            && self.milliseconds.map(|v| v.to_bits()) == other.milliseconds.map(|v| v.to_bits())
244    }
245}
246
247impl Eq for Duration {}
248
249impl Duration {
250    /// Whether this duration represents a zero duration.
251    pub fn is_zero(&self) -> bool {
252        let Duration {
253            years,
254            months,
255            weeks,
256            days,
257            hours,
258            minutes,
259            seconds,
260            milliseconds,
261        } = self;
262
263        [*years, *months, *weeks, *days, *hours, *minutes, *seconds]
264            .iter()
265            .all(|&x| x == 0)
266            && milliseconds.unwrap_or(0.0) == 0.0
267    }
268
269    /// Whether this duration has a time component.
270    pub fn has_time(&self) -> bool {
271        let Duration {
272            days,
273            hours,
274            minutes,
275            seconds,
276            milliseconds,
277            ..
278        } = self;
279
280        [*days, *hours, *minutes, *seconds].iter().all(|&x| x > 0)
281            || milliseconds.unwrap_or(0.0) > 0.0
282    }
283}
284
285impl From<Duration> for ::core::time::Duration {
286    fn from(duration: Duration) -> Self {
287        let Duration {
288            years,
289            months,
290            weeks,
291            days,
292            hours,
293            minutes,
294            seconds,
295            milliseconds,
296        } = duration;
297
298        let secs = years * 365 * 86_400
299            + months * 30 * 86_400
300            + weeks * 7 * 86_400
301            + days * 86_400
302            + hours * 3600
303            + minutes * 60
304            + seconds;
305        let nanos = (milliseconds.unwrap_or(0.0) * 1_000_000_000.0).floor();
306        Self::new(secs as u64, nanos as u32)
307    }
308}
309
310#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
311#[derive(Debug, Default, Copy, Clone)]
312pub struct DurationPart {
313    pub whole: u32,
314    pub frac: Option<f32>,
315}
316
317impl PartialEq for DurationPart {
318    fn eq(&self, other: &Self) -> bool {
319        self.whole == other.whole
320            && self.frac.map(|f| f.to_bits()) == other.frac.map(|f| f.to_bits())
321    }
322}
323
324impl Eq for DurationPart {}
325
326/// A time duration with fractional precision.
327///
328/// ## FractionalDuration Grammar
329///
330/// | FractionalDuration     | ABNF Description                                     |
331/// | ------------ | ---------------------------------------------------- |
332/// | `dur-second` | 1*DIGIT "S"                                          |
333/// | `dur-minute` | 1*DIGIT "M" [`dur-second`]                           |
334/// | `dur-hour`   | 1*DIGIT "H" [`dur-minute`]                           |
335/// | `dur-time`   | "T" (`dur-hour` / `dur-minute` / `dur-second`)       |
336/// | `dur-day`    | 1*DIGIT "D"                                          |
337/// | `dur-week`   | 1*DIGIT "W"                                          |
338/// | `dur-month`  | 1*DIGIT "M" [`dur-day`]                              |
339/// | `dur-year`   | 1*DIGIT "Y" [`dur-month`]                            |
340/// | `dur-date`   | (`dur-day` / `dur-month` / `dur-year`) [`dur-time`]  |
341/// | `duration`   | "P" (`dur-date` / `dur-time` / `dur-week`)           |
342///
343/// ## Examples
344/// ```
345/// // 1 year, 11 months, 16 days, 23 hours, 26 minutes, 59 seconds, 123 milliseconds
346/// use winnow_datetime::types::FractionalDuration;
347///
348/// let d = FractionalDuration {
349///      years: (1, None),
350///      months: (11, None),
351///      weeks: (0, None),
352///      days: (16, None),
353///      hours: (23, None),
354///      minutes: (26, None),
355///      seconds: (59, Some(0.123)),
356/// };
357/// ```
358#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(default))]
359#[derive(Debug, Default, Copy, Clone)]
360pub struct FractionalDuration {
361    /// Number of calendar years
362    pub years: (u32, Option<f32>),
363    /// Number of months
364    pub months: (u32, Option<f32>),
365    /// Number of weeks
366    pub weeks: (u32, Option<f32>),
367    /// Number of days
368    pub days: (u32, Option<f32>),
369    /// Number of hours
370    pub hours: (u32, Option<f32>),
371    /// Number of minutes
372    pub minutes: (u32, Option<f32>),
373    /// Number of seconds
374    pub seconds: (u32, Option<f32>),
375}
376
377impl PartialEq for FractionalDuration {
378    fn eq(&self, other: &Self) -> bool {
379        [
380            self.years,
381            self.months,
382            self.weeks,
383            self.days,
384            self.hours,
385            self.minutes,
386            self.seconds,
387        ]
388        .iter()
389        .zip([
390            other.years,
391            other.months,
392            other.weeks,
393            other.days,
394            other.hours,
395            other.minutes,
396            other.seconds,
397        ])
398        .all(|(part, other_part)| {
399            part.0 == other_part.0
400                && part.1.map(|v| v.to_bits()) == other_part.1.map(|v| v.to_bits())
401        })
402    }
403}
404
405impl Eq for FractionalDuration {}
406
407impl FractionalDuration {
408    /// Whether this duration represents a zero duration.
409    pub fn is_zero(&self) -> bool {
410        let FractionalDuration {
411            years,
412            months,
413            weeks,
414            days,
415            hours,
416            minutes,
417            seconds,
418        } = self;
419        [*years, *months, *weeks, *days, *hours, *minutes, *seconds]
420            .iter()
421            .all(|&x| x.0 == 0 && x.1.unwrap_or(0.0) == 0.0)
422    }
423
424    /// Whether this duration has a time component.
425    pub fn has_time(&self) -> bool {
426        let FractionalDuration {
427            days,
428            hours,
429            minutes,
430            seconds,
431            ..
432        } = self;
433        [*days, *hours, *minutes, *seconds]
434            .iter()
435            .all(|&x| x.0 > 0 || x.1.unwrap_or(0.0) > 0.0)
436    }
437}