1#[cfg(feature = "chrono")]
2use chrono::{Datelike, NaiveDate, Timelike, Weekday};
3
4#[derive(Clone, Debug, PartialEq, Eq)]
6#[cfg_attr(feature = "defmt", derive(defmt::Format))]
7pub enum Error {
8 InvalidYear,
10 InvalidMonth,
12 InvalidDay,
14 InvalidDayOfWeek(
16 u8,
18 ),
19 InvalidHour,
21 InvalidMinute,
23 InvalidSecond,
25 InvalidMicrosecond,
27}
28
29#[cfg_attr(feature = "defmt", derive(defmt::Format))]
31pub struct DateTime {
32 year: u16,
34 month: u8,
36 day: u8,
38 day_of_week: DayOfWeek,
40 hour: u8,
42 minute: u8,
44 second: u8,
46 usecond: u32,
48}
49
50impl DateTime {
51 pub const fn year(&self) -> u16 {
53 self.year
54 }
55
56 pub const fn month(&self) -> u8 {
58 self.month
59 }
60
61 pub const fn day(&self) -> u8 {
63 self.day
64 }
65
66 pub const fn day_of_week(&self) -> DayOfWeek {
68 self.day_of_week
69 }
70
71 pub const fn hour(&self) -> u8 {
73 self.hour
74 }
75
76 pub const fn minute(&self) -> u8 {
78 self.minute
79 }
80
81 pub const fn second(&self) -> u8 {
83 self.second
84 }
85
86 pub const fn microsecond(&self) -> u32 {
88 self.usecond
89 }
90
91 pub fn from(
93 year: u16,
94 month: u8,
95 day: u8,
96 day_of_week: DayOfWeek,
97 hour: u8,
98 minute: u8,
99 second: u8,
100 usecond: u32,
101 ) -> Result<Self, Error> {
102 if year > 4095 {
103 Err(Error::InvalidYear)
104 } else if !(1..=12).contains(&month) {
105 Err(Error::InvalidMonth)
106 } else if !(1..=31).contains(&day) {
107 Err(Error::InvalidDay)
108 } else if hour > 23 {
109 Err(Error::InvalidHour)
110 } else if minute > 59 {
111 Err(Error::InvalidMinute)
112 } else if second > 59 {
113 Err(Error::InvalidSecond)
114 } else if usecond > 999_999 {
115 Err(Error::InvalidMicrosecond)
116 } else {
117 Ok(Self {
118 year,
119 month,
120 day,
121 day_of_week,
122 hour,
123 minute,
124 second,
125 usecond,
126 })
127 }
128 }
129}
130
131#[cfg(feature = "chrono")]
132impl From<chrono::NaiveDateTime> for DateTime {
133 fn from(date_time: chrono::NaiveDateTime) -> Self {
134 Self {
135 year: date_time.year() as u16,
136 month: date_time.month() as u8,
137 day: date_time.day() as u8,
138 day_of_week: date_time.weekday().into(),
139 hour: date_time.hour() as u8,
140 minute: date_time.minute() as u8,
141 second: date_time.second() as u8,
142 usecond: date_time.and_utc().timestamp_subsec_micros(),
143 }
144 }
145}
146
147#[cfg(feature = "chrono")]
148impl From<DateTime> for chrono::NaiveDateTime {
149 fn from(date_time: DateTime) -> Self {
150 NaiveDate::from_ymd_opt(date_time.year as i32, date_time.month as u32, date_time.day as u32)
151 .unwrap()
152 .and_hms_micro_opt(
153 date_time.hour as u32,
154 date_time.minute as u32,
155 date_time.second as u32,
156 date_time.usecond,
157 )
158 .unwrap()
159 }
160}
161
162#[repr(u8)]
164#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
165#[cfg_attr(feature = "defmt", derive(defmt::Format))]
166#[allow(missing_docs)]
167pub enum DayOfWeek {
168 Monday = 1,
169 Tuesday = 2,
170 Wednesday = 3,
171 Thursday = 4,
172 Friday = 5,
173 Saturday = 6,
174 Sunday = 7,
175}
176
177#[cfg(feature = "chrono")]
178impl From<chrono::Weekday> for DayOfWeek {
179 fn from(weekday: Weekday) -> Self {
180 day_of_week_from_u8(weekday.number_from_monday() as u8).unwrap()
181 }
182}
183
184#[cfg(feature = "chrono")]
185impl From<DayOfWeek> for chrono::Weekday {
186 fn from(weekday: DayOfWeek) -> Self {
187 match weekday {
188 DayOfWeek::Monday => Weekday::Mon,
189 DayOfWeek::Tuesday => Weekday::Tue,
190 DayOfWeek::Wednesday => Weekday::Wed,
191 DayOfWeek::Thursday => Weekday::Thu,
192 DayOfWeek::Friday => Weekday::Fri,
193 DayOfWeek::Saturday => Weekday::Sat,
194 DayOfWeek::Sunday => Weekday::Sun,
195 }
196 }
197}
198
199pub(super) const fn day_of_week_from_u8(v: u8) -> Result<DayOfWeek, Error> {
200 Ok(match v {
201 1 => DayOfWeek::Monday,
202 2 => DayOfWeek::Tuesday,
203 3 => DayOfWeek::Wednesday,
204 4 => DayOfWeek::Thursday,
205 5 => DayOfWeek::Friday,
206 6 => DayOfWeek::Saturday,
207 7 => DayOfWeek::Sunday,
208 x => return Err(Error::InvalidDayOfWeek(x)),
209 })
210}
211
212pub(super) const fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 {
213 dotw as u8
214}