Skip to main content

flexible_time/timestamp/
start.rs

1use crate::{
2    error::{ConversionError, Error},
3    timestamp::ParsedTimestamp,
4};
5use std::str::FromStr;
6use std::time::SystemTime;
7use time::{Date, Month, OffsetDateTime};
8
9/// A timestamp which will be align with the start of a [`ParsedTimestamp`]. Meaning, it will
10/// take the minimum value of all omitted components.
11#[derive(Copy, Clone, Debug, PartialEq, Eq)]
12pub struct StartTimestamp(pub OffsetDateTime);
13
14impl From<StartTimestamp> for OffsetDateTime {
15    fn from(value: StartTimestamp) -> Self {
16        value.0
17    }
18}
19
20impl From<StartTimestamp> for SystemTime {
21    fn from(value: StartTimestamp) -> Self {
22        value.0.into()
23    }
24}
25
26impl TryFrom<ParsedTimestamp> for StartTimestamp {
27    type Error = ConversionError;
28
29    fn try_from(value: ParsedTimestamp) -> Result<Self, Self::Error> {
30        Ok(StartTimestamp(match value {
31            ParsedTimestamp::Year(year) => Date::from_calendar_date(year, Month::January, 1)?
32                .midnight()
33                .assume_utc(),
34            ParsedTimestamp::YearMonth(year, month) => Date::from_calendar_date(year, month, 1)?
35                .midnight()
36                .assume_utc(),
37            ParsedTimestamp::YearMonthDay(year, month, day) => {
38                Date::from_calendar_date(year, month, day.get())?
39                    .midnight()
40                    .assume_utc()
41            }
42
43            ParsedTimestamp::YearMonthDayHour(year, month, day, hour) => {
44                Date::from_calendar_date(year, month, day.get())?
45                    .with_hms(hour, 0, 0)?
46                    .assume_utc()
47            }
48            ParsedTimestamp::YearMonthDayHourMinute(year, month, day, hour, minute) => {
49                Date::from_calendar_date(year, month, day.get())?
50                    .with_hms(hour, minute, 0)?
51                    .assume_utc()
52            }
53            ParsedTimestamp::YearMonthDayHourMinuteSecond(
54                year,
55                month,
56                day,
57                hour,
58                minute,
59                second,
60            ) => Date::from_calendar_date(year, month, day.get())?
61                .with_hms(hour, minute, second)?
62                .assume_utc(),
63        }))
64    }
65}
66
67impl FromStr for StartTimestamp {
68    type Err = Error;
69
70    fn from_str(s: &str) -> Result<Self, Self::Err> {
71        Ok(StartTimestamp::try_from(ParsedTimestamp::from_str(s)?)?)
72    }
73}
74
75#[cfg(test)]
76mod test {
77    use super::*;
78    use time::macros::datetime;
79    use time::OffsetDateTime;
80
81    macro_rules! assert_start {
82        ($name:ident, $s:literal, $dt:expr) => {
83            #[test]
84            fn $name() {
85                assert_eq!(
86                    $dt,
87                    OffsetDateTime::from(StartTimestamp::from_str($s).unwrap())
88                )
89            }
90        };
91    }
92
93    assert_start!(start_year, "2023", datetime!(2023-01-01 0:00 UTC));
94    assert_start!(start_year_month, "2023-02", datetime!(2023-02-01 0:00 UTC));
95    assert_start!(
96        start_year_month_day,
97        "2023-02-03",
98        datetime!(2023-02-03 0:00 UTC)
99    );
100
101    assert_start!(
102        start_year_month_day_hour_single,
103        "2023-02-03 4",
104        datetime!(2023-02-03 4:00 UTC)
105    );
106    assert_start!(
107        start_year_month_day_hour,
108        "2023-02-03 04",
109        datetime!(2023-02-03 4:00 UTC)
110    );
111
112    assert_start!(
113        start_year_month_day_hour_single_minute,
114        "2023-02-03 4:05",
115        datetime!(2023-02-03 4:05 UTC)
116    );
117    assert_start!(
118        start_year_month_day_hour_minute,
119        "2023-02-03 04:05",
120        datetime!(2023-02-03 4:05 UTC)
121    );
122
123    assert_start!(
124        start_year_month_day_hour_minute_second,
125        "2023-02-03 04:05:06",
126        datetime!(2023-02-03 4:05:06 UTC)
127    );
128    assert_start!(
129        start_year_month_day_hour_single_minute_second,
130        "2023-02-03 4:05:06",
131        datetime!(2023-02-03 4:05:06 UTC)
132    );
133
134    assert_start!(
135        start_year_month_day_hour_24h_minute_second,
136        "2023-02-03 16:05:06",
137        datetime!(2023-02-03 16:05:06 UTC)
138    );
139}