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}