zencan_common/
time_types.rs1use chrono::{Datelike, NaiveDate, NaiveTime, TimeDelta, Timelike};
4use core::time::Duration;
5use snafu::Snafu;
6
7const MILLIS_PER_DAY: u64 = 86_400_000;
8
9#[derive(Clone, Copy, Debug, Snafu)]
10pub enum TimeCreateError {
11 PreEpoch,
13 OutOfRange,
15 InvalidDate,
20}
21
22#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
24pub struct TimeOfDay(TimeDifference);
25
26impl TimeOfDay {
27 pub const SIZE: usize = 6;
29
30 pub const EPOCH: TimeOfDay = TimeOfDay(TimeDifference { ms: 0, days: 0 });
32 const CHRONO_EPOCH: NaiveDate = NaiveDate::from_ymd_opt(1984, 1, 1).unwrap();
34
35 pub fn new(days: u16, ms: u32) -> Self {
41 Self(TimeDifference::new(days, ms))
42 }
43
44 pub fn from_ymd_hms_ms(
46 year: u32,
47 month: u32,
48 day: u32,
49 hour: u32,
50 min: u32,
51 sec: u32,
52 milli: u32,
53 ) -> Result<Self, TimeCreateError> {
54 let chrono_date = NaiveDate::from_ymd_opt(year as i32, month, day)
55 .ok_or(InvalidDateSnafu.build())?
56 .and_hms_milli_opt(hour, min, sec, milli)
57 .ok_or(InvalidDateSnafu.build())?;
58
59 let delta = chrono_date - const { Self::CHRONO_EPOCH.and_hms_opt(0, 0, 0).unwrap() };
60 let days = delta.num_days();
61 let ms = (delta - TimeDelta::days(days)).num_milliseconds();
62 if days < 0 {
63 PreEpochSnafu.fail()
64 } else if days > u16::MAX as i64 {
65 OutOfRangeSnafu.fail()
66 } else {
67 Ok(Self::new(days as u16, ms as u32))
68 }
69 }
70
71 pub fn from_le_bytes(bytes: [u8; 6]) -> Self {
73 Self(TimeDifference::from_le_bytes(bytes))
74 }
75
76 pub fn to_le_bytes(&self) -> [u8; 6] {
78 self.0.to_le_bytes()
79 }
80
81 pub fn date_ymd(&self) -> (u32, u32, u32) {
85 let date = Self::CHRONO_EPOCH + self.0.as_chrono_delta();
86 (date.year() as u32, date.month(), date.day())
87 }
88
89 pub fn days(&self) -> u16 {
91 self.0.days
92 }
93
94 pub fn time_hmsm(&self) -> (u32, u32, u32, u32) {
96 let sec = self.0.ms / 1000;
97 let nanos = (self.0.ms % 1000) * 1000;
98 let t = NaiveTime::from_num_seconds_from_midnight_opt(sec, nanos).unwrap();
99 (t.hour(), t.minute(), t.second(), t.nanosecond() / 1000)
100 }
101
102 pub fn time_millis(&self) -> u32 {
104 self.0.ms
105 }
106
107 pub fn time_duration(&self) -> Duration {
109 Duration::from_millis(self.time_millis() as u64)
110 }
111
112 pub fn total_millis(&self) -> u64 {
114 self.0.total_millis()
115 }
116
117 #[cfg(feature = "std")]
119 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
120 pub fn as_system_time(&self) -> std::time::SystemTime {
121 use std::time::SystemTime;
122
123 const UNIX_EPOCH: NaiveDate = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap();
125 let epoch_delta_millis = (Self::CHRONO_EPOCH - UNIX_EPOCH).num_milliseconds() as u64;
126 SystemTime::UNIX_EPOCH + self.0.as_duration() + Duration::from_millis(epoch_delta_millis)
127 }
128}
129
130#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
132pub struct TimeDifference {
133 ms: u32,
134 days: u16,
135}
136
137impl TimeDifference {
138 pub const SIZE: usize = 6;
140
141 pub const ZERO: TimeDifference = TimeDifference { ms: 0, days: 0 };
143
144 pub const fn new(days: u16, ms: u32) -> Self {
146 Self { ms, days }
147 }
148
149 pub fn from_le_bytes(bytes: [u8; 6]) -> Self {
151 let ms = u32::from_le_bytes(bytes[0..4].try_into().unwrap());
152 let days = u16::from_le_bytes(bytes[4..6].try_into().unwrap());
153 Self::new(days, ms)
154 }
155
156 pub fn to_le_bytes(&self) -> [u8; 6] {
158 let mut bytes = [0; 6];
159 bytes[0..4].copy_from_slice(&self.ms.to_le_bytes());
160 bytes[4..6].copy_from_slice(&self.days.to_le_bytes());
161 bytes
162 }
163
164 pub fn total_millis(&self) -> u64 {
166 self.days as u64 * MILLIS_PER_DAY + self.ms as u64
167 }
168
169 pub fn as_duration(&self) -> Duration {
171 Duration::from_millis(self.days as u64 * MILLIS_PER_DAY + self.ms as u64)
172 }
173
174 pub(crate) fn as_chrono_delta(&self) -> chrono::TimeDelta {
175 chrono::TimeDelta::milliseconds(self.days as i64 * MILLIS_PER_DAY as i64 + self.ms as i64)
176 }
177}