1 | #[cfg (feature = "chrono" )] |
2 | use chrono::{Datelike, NaiveDate, Timelike, Weekday}; |
3 | |
4 | /// Errors regarding the [`DateTime`] struct. |
5 | #[derive (Clone, Debug, PartialEq, Eq)] |
6 | pub enum Error { |
7 | /// The [DateTime] contains an invalid year value. Must be between `0..=4095`. |
8 | InvalidYear, |
9 | /// The [DateTime] contains an invalid month value. Must be between `1..=12`. |
10 | InvalidMonth, |
11 | /// The [DateTime] contains an invalid day value. Must be between `1..=31`. |
12 | InvalidDay, |
13 | /// The [DateTime] contains an invalid day of week. Must be between `0..=6` where 0 is Sunday. |
14 | InvalidDayOfWeek( |
15 | /// The value of the DayOfWeek that was given. |
16 | u8, |
17 | ), |
18 | /// The [DateTime] contains an invalid hour value. Must be between `0..=23`. |
19 | InvalidHour, |
20 | /// The [DateTime] contains an invalid minute value. Must be between `0..=59`. |
21 | InvalidMinute, |
22 | /// The [DateTime] contains an invalid second value. Must be between `0..=59`. |
23 | InvalidSecond, |
24 | } |
25 | |
26 | /// Structure containing date and time information |
27 | pub struct DateTime { |
28 | /// 0..4095 |
29 | year: u16, |
30 | /// 1..12, 1 is January |
31 | month: u8, |
32 | /// 1..28,29,30,31 depending on month |
33 | day: u8, |
34 | /// |
35 | day_of_week: DayOfWeek, |
36 | /// 0..23 |
37 | hour: u8, |
38 | /// 0..59 |
39 | minute: u8, |
40 | /// 0..59 |
41 | second: u8, |
42 | } |
43 | |
44 | impl DateTime { |
45 | /// Get the year (0..=4095) |
46 | pub const fn year(&self) -> u16 { |
47 | self.year |
48 | } |
49 | |
50 | /// Get the month (1..=12, 1 is January) |
51 | pub const fn month(&self) -> u8 { |
52 | self.month |
53 | } |
54 | |
55 | /// Get the day (1..=31) |
56 | pub const fn day(&self) -> u8 { |
57 | self.day |
58 | } |
59 | |
60 | /// Get the day of week |
61 | pub const fn day_of_week(&self) -> DayOfWeek { |
62 | self.day_of_week |
63 | } |
64 | |
65 | /// Get the hour (0..=23) |
66 | pub const fn hour(&self) -> u8 { |
67 | self.hour |
68 | } |
69 | |
70 | /// Get the minute (0..=59) |
71 | pub const fn minute(&self) -> u8 { |
72 | self.minute |
73 | } |
74 | |
75 | /// Get the second (0..=59) |
76 | pub const fn second(&self) -> u8 { |
77 | self.second |
78 | } |
79 | |
80 | /// Create a new DateTime with the given information. |
81 | pub fn from( |
82 | year: u16, |
83 | month: u8, |
84 | day: u8, |
85 | day_of_week: DayOfWeek, |
86 | hour: u8, |
87 | minute: u8, |
88 | second: u8, |
89 | ) -> Result<Self, Error> { |
90 | if year > 4095 { |
91 | Err(Error::InvalidYear) |
92 | } else if !(1..=12).contains(&month) { |
93 | Err(Error::InvalidMonth) |
94 | } else if !(1..=31).contains(&day) { |
95 | Err(Error::InvalidDay) |
96 | } else if hour > 23 { |
97 | Err(Error::InvalidHour) |
98 | } else if minute > 59 { |
99 | Err(Error::InvalidMinute) |
100 | } else if second > 59 { |
101 | Err(Error::InvalidSecond) |
102 | } else { |
103 | Ok(Self { |
104 | year, |
105 | month, |
106 | day, |
107 | day_of_week, |
108 | hour, |
109 | minute, |
110 | second, |
111 | }) |
112 | } |
113 | } |
114 | } |
115 | |
116 | #[cfg (feature = "chrono" )] |
117 | impl From<chrono::NaiveDateTime> for DateTime { |
118 | fn from(date_time: chrono::NaiveDateTime) -> Self { |
119 | Self { |
120 | year: date_time.year() as u16, |
121 | month: date_time.month() as u8, |
122 | day: date_time.day() as u8, |
123 | day_of_week: date_time.weekday().into(), |
124 | hour: date_time.hour() as u8, |
125 | minute: date_time.minute() as u8, |
126 | second: date_time.second() as u8, |
127 | } |
128 | } |
129 | } |
130 | |
131 | #[cfg (feature = "chrono" )] |
132 | impl From<DateTime> for chrono::NaiveDateTime { |
133 | fn from(date_time: DateTime) -> Self { |
134 | NaiveDateOption::from_ymd_opt(date_time.year as i32, date_time.month as u32, date_time.day as u32) |
135 | .unwrap() |
136 | .and_hms_opt(date_time.hour as u32, min:date_time.minute as u32, sec:date_time.second as u32) |
137 | .unwrap() |
138 | } |
139 | } |
140 | |
141 | /// A day of the week |
142 | #[repr (u8)] |
143 | #[derive (Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] |
144 | #[allow (missing_docs)] |
145 | pub enum DayOfWeek { |
146 | Monday = 1, |
147 | Tuesday = 2, |
148 | Wednesday = 3, |
149 | Thursday = 4, |
150 | Friday = 5, |
151 | Saturday = 6, |
152 | Sunday = 7, |
153 | } |
154 | |
155 | #[cfg (feature = "chrono" )] |
156 | impl From<chrono::Weekday> for DayOfWeek { |
157 | fn from(weekday: Weekday) -> Self { |
158 | day_of_week_from_u8(weekday.number_from_monday() as u8).unwrap() |
159 | } |
160 | } |
161 | |
162 | #[cfg (feature = "chrono" )] |
163 | impl From<DayOfWeek> for chrono::Weekday { |
164 | fn from(weekday: DayOfWeek) -> Self { |
165 | match weekday { |
166 | DayOfWeek::Monday => Weekday::Mon, |
167 | DayOfWeek::Tuesday => Weekday::Tue, |
168 | DayOfWeek::Wednesday => Weekday::Wed, |
169 | DayOfWeek::Thursday => Weekday::Thu, |
170 | DayOfWeek::Friday => Weekday::Fri, |
171 | DayOfWeek::Saturday => Weekday::Sat, |
172 | DayOfWeek::Sunday => Weekday::Sun, |
173 | } |
174 | } |
175 | } |
176 | |
177 | pub(super) const fn day_of_week_from_u8(v: u8) -> Result<DayOfWeek, Error> { |
178 | Ok(match v { |
179 | 1 => DayOfWeek::Monday, |
180 | 2 => DayOfWeek::Tuesday, |
181 | 3 => DayOfWeek::Wednesday, |
182 | 4 => DayOfWeek::Thursday, |
183 | 5 => DayOfWeek::Friday, |
184 | 6 => DayOfWeek::Saturday, |
185 | 7 => DayOfWeek::Sunday, |
186 | x: u8 => return Err(Error::InvalidDayOfWeek(x)), |
187 | }) |
188 | } |
189 | |
190 | pub(super) const fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 { |
191 | dotw as u8 |
192 | } |
193 | |