| 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 | |