strftime/format/
check.rs

1//! Checks for a time implementation.
2
3use crate::{Error, Time};
4
5/// Wrapper trait for validating a time implementation.
6pub(crate) trait CheckedTime {
7    /// No checks.
8    fn year(&self) -> i32;
9    /// Checks if the month is in `1..=12`.
10    fn month(&self) -> Result<u8, Error>;
11    /// Checks if the day of the month is in `1..=31`.
12    fn day(&self) -> Result<u8, Error>;
13    /// Checks if the hour of the day is in `0..=23`.
14    fn hour(&self) -> Result<u8, Error>;
15    /// Checks if the minute of the hour is in `0..=59`.
16    fn minute(&self) -> Result<u8, Error>;
17    /// Checks if the second of the minute is in `0..=60`.
18    fn second(&self) -> Result<u8, Error>;
19    /// Checks if the number of nanoseconds is in `0..=999_999_999`.
20    fn nanoseconds(&self) -> Result<u32, Error>;
21    /// Checks if the day of the week is in `0..=6`.
22    fn day_of_week(&self) -> Result<u8, Error>;
23    /// Checks if the day of the year is in `1..=366`.
24    fn day_of_year(&self) -> Result<u16, Error>;
25    /// No checks.
26    fn to_int(&self) -> i64;
27    /// No checks.
28    fn is_utc(&self) -> bool;
29    /// No checks.
30    fn utc_offset(&self) -> i32;
31    /// Checks if the name of the time zone is valid ASCII.
32    fn time_zone(&self) -> Result<&str, Error>;
33}
34
35impl<T: Time> CheckedTime for T {
36    fn year(&self) -> i32 {
37        self.year()
38    }
39
40    fn month(&self) -> Result<u8, Error> {
41        match self.month() {
42            month @ 1..=12 => Ok(month),
43            _ => Err(Error::InvalidTime),
44        }
45    }
46
47    fn day(&self) -> Result<u8, Error> {
48        match self.day() {
49            day @ 1..=31 => Ok(day),
50            _ => Err(Error::InvalidTime),
51        }
52    }
53
54    fn hour(&self) -> Result<u8, Error> {
55        match self.hour() {
56            hour @ 0..=23 => Ok(hour),
57            _ => Err(Error::InvalidTime),
58        }
59    }
60
61    fn minute(&self) -> Result<u8, Error> {
62        match self.minute() {
63            minute @ 0..=59 => Ok(minute),
64            _ => Err(Error::InvalidTime),
65        }
66    }
67
68    fn second(&self) -> Result<u8, Error> {
69        match self.second() {
70            second @ 0..=60 => Ok(second),
71            _ => Err(Error::InvalidTime),
72        }
73    }
74
75    fn nanoseconds(&self) -> Result<u32, Error> {
76        match self.nanoseconds() {
77            nanoseconds @ 0..=999_999_999 => Ok(nanoseconds),
78            _ => Err(Error::InvalidTime),
79        }
80    }
81
82    fn day_of_week(&self) -> Result<u8, Error> {
83        match self.day_of_week() {
84            day_of_week @ 0..=6 => Ok(day_of_week),
85            _ => Err(Error::InvalidTime),
86        }
87    }
88
89    fn day_of_year(&self) -> Result<u16, Error> {
90        match self.day_of_year() {
91            day_of_year @ 1..=366 => Ok(day_of_year),
92            _ => Err(Error::InvalidTime),
93        }
94    }
95
96    fn to_int(&self) -> i64 {
97        self.to_int()
98    }
99
100    fn is_utc(&self) -> bool {
101        self.is_utc()
102    }
103
104    fn utc_offset(&self) -> i32 {
105        self.utc_offset()
106    }
107
108    fn time_zone(&self) -> Result<&str, Error> {
109        match self.time_zone() {
110            time_zone if time_zone.is_ascii() => Ok(time_zone),
111            _ => Err(Error::InvalidTime),
112        }
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119    use crate::tests::MockTime;
120
121    fn check<T>(ok: bool, result: &Result<T, Error>) {
122        if ok {
123            assert!(result.is_ok());
124        } else {
125            assert!(matches!(result, Err(Error::InvalidTime)));
126        }
127    }
128
129    #[test]
130    fn test_checked_time() {
131        #[rustfmt::skip]
132        let times = [
133            MockTime::new(1970, 1, 1, 0, 0, 0, 0, 4, 1, 0, false, 0, ""),
134            MockTime::new(1970, 0, 0, 99, 99, 99, 1_000_000_000, 9, 999, 0, false, 0, "€"),
135        ];
136
137        check(true, &CheckedTime::month(&times[0]));
138        check(true, &CheckedTime::day(&times[0]));
139        check(true, &CheckedTime::hour(&times[0]));
140        check(true, &CheckedTime::minute(&times[0]));
141        check(true, &CheckedTime::second(&times[0]));
142        check(true, &CheckedTime::nanoseconds(&times[0]));
143        check(true, &CheckedTime::day_of_week(&times[0]));
144        check(true, &CheckedTime::day_of_year(&times[0]));
145        check(true, &CheckedTime::time_zone(&times[0]));
146
147        check(false, &CheckedTime::month(&times[1]));
148        check(false, &CheckedTime::day(&times[1]));
149        check(false, &CheckedTime::hour(&times[1]));
150        check(false, &CheckedTime::minute(&times[1]));
151        check(false, &CheckedTime::second(&times[1]));
152        check(false, &CheckedTime::nanoseconds(&times[1]));
153        check(false, &CheckedTime::day_of_week(&times[1]));
154        check(false, &CheckedTime::day_of_year(&times[1]));
155        check(false, &CheckedTime::time_zone(&times[1]));
156    }
157}