embassy_stm32/rtc/
datetime.rs

1#[cfg(feature = "chrono")]
2use chrono::{Datelike, NaiveDate, Timelike, Weekday};
3
4/// Errors regarding the [`DateTime`] struct.
5#[derive(Clone, Debug, PartialEq, Eq)]
6#[cfg_attr(feature = "defmt", derive(defmt::Format))]
7pub enum Error {
8    /// The [DateTime] contains an invalid year value. Must be between `0..=4095`.
9    InvalidYear,
10    /// The [DateTime] contains an invalid month value. Must be between `1..=12`.
11    InvalidMonth,
12    /// The [DateTime] contains an invalid day value. Must be between `1..=31`.
13    InvalidDay,
14    /// The [DateTime] contains an invalid day of week. Must be between `0..=6` where 0 is Sunday.
15    InvalidDayOfWeek(
16        /// The value of the DayOfWeek that was given.
17        u8,
18    ),
19    /// The [DateTime] contains an invalid hour value. Must be between `0..=23`.
20    InvalidHour,
21    /// The [DateTime] contains an invalid minute value. Must be between `0..=59`.
22    InvalidMinute,
23    /// The [DateTime] contains an invalid second value. Must be between `0..=59`.
24    InvalidSecond,
25    /// The [DateTime] contains an invalid microsecond value. Must be between `0..=999_999`.
26    InvalidMicrosecond,
27}
28
29/// Structure containing date and time information
30#[cfg_attr(feature = "defmt", derive(defmt::Format))]
31pub struct DateTime {
32    /// 0..4095
33    year: u16,
34    /// 1..12, 1 is January
35    month: u8,
36    /// 1..28,29,30,31 depending on month
37    day: u8,
38    ///
39    day_of_week: DayOfWeek,
40    /// 0..23
41    hour: u8,
42    /// 0..59
43    minute: u8,
44    /// 0..59
45    second: u8,
46    /// 0..999_999
47    usecond: u32,
48}
49
50impl DateTime {
51    /// Get the year (0..=4095)
52    pub const fn year(&self) -> u16 {
53        self.year
54    }
55
56    /// Get the month (1..=12, 1 is January)
57    pub const fn month(&self) -> u8 {
58        self.month
59    }
60
61    /// Get the day (1..=31)
62    pub const fn day(&self) -> u8 {
63        self.day
64    }
65
66    /// Get the day of week
67    pub const fn day_of_week(&self) -> DayOfWeek {
68        self.day_of_week
69    }
70
71    /// Get the hour (0..=23)
72    pub const fn hour(&self) -> u8 {
73        self.hour
74    }
75
76    /// Get the minute (0..=59)
77    pub const fn minute(&self) -> u8 {
78        self.minute
79    }
80
81    /// Get the second (0..=59)
82    pub const fn second(&self) -> u8 {
83        self.second
84    }
85
86    /// Get the microsecond (0..=999_999)
87    pub const fn microsecond(&self) -> u32 {
88        self.usecond
89    }
90
91    /// Create a new DateTime with the given information.
92    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/// A day of the week
163#[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}