embedded_sdmmc/filesystem/
timestamp.rs1pub trait TimeSource {
5 fn get_timestamp(&self) -> Timestamp;
7}
8
9#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
14#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
15pub struct Timestamp {
16 pub year_since_1970: u8,
18 pub zero_indexed_month: u8,
20 pub zero_indexed_day: u8,
22 pub hours: u8,
24 pub minutes: u8,
26 pub seconds: u8,
28}
29
30impl Timestamp {
31 pub fn from_fat(date: u16, time: u16) -> Timestamp {
33 let year = 1980 + (date >> 9);
34 let month = ((date >> 5) & 0x000F) as u8;
35 let day = (date & 0x001F) as u8;
36 let hours = ((time >> 11) & 0x001F) as u8;
37 let minutes = ((time >> 5) & 0x0003F) as u8;
38 let seconds = ((time << 1) & 0x0003F) as u8;
39 Timestamp {
41 year_since_1970: (year - 1970) as u8,
42 zero_indexed_month: if month == 0 { 0 } else { month - 1 },
43 zero_indexed_day: if day == 0 { 0 } else { day - 1 },
44 hours,
45 minutes,
46 seconds,
47 }
48 }
49
50 pub fn serialize_to_fat(self) -> [u8; 4] {
52 let mut data = [0u8; 4];
53
54 let hours = (u16::from(self.hours) << 11) & (0x1F << 11);
55 let minutes = (u16::from(self.minutes) << 5) & (0x3F << 5);
56 let seconds = (u16::from(self.seconds / 2)) & 0x1F;
57 data[..2].copy_from_slice(&(hours | minutes | seconds).to_le_bytes()[..]);
58
59 let year = if self.year_since_1970 < 10 {
60 0
61 } else {
62 (u16::from(self.year_since_1970 - 10) << 9) & 0xFE00
63 };
64 let month = (u16::from(self.zero_indexed_month + 1) << 5) & 0x01E0;
65 let day = u16::from(self.zero_indexed_day + 1) & 0x001F;
66 data[2..].copy_from_slice(&(year | month | day).to_le_bytes()[..]);
67 data
68 }
69
70 pub fn from_calendar(
75 year: u16,
76 month: u8,
77 day: u8,
78 hours: u8,
79 minutes: u8,
80 seconds: u8,
81 ) -> Result<Timestamp, &'static str> {
82 Ok(Timestamp {
83 year_since_1970: if (1970..=2097).contains(&year) {
84 (year - 1970) as u8
85 } else {
86 return Err("Bad year");
87 },
88 zero_indexed_month: if (1..=12).contains(&month) {
89 month - 1
90 } else {
91 return Err("Bad month");
92 },
93 zero_indexed_day: if (1..=31).contains(&day) {
94 day - 1
95 } else {
96 return Err("Bad day");
97 },
98 hours: if hours <= 23 {
99 hours
100 } else {
101 return Err("Bad hours");
102 },
103 minutes: if minutes <= 59 {
104 minutes
105 } else {
106 return Err("Bad minutes");
107 },
108 seconds: if seconds <= 59 {
109 seconds
110 } else {
111 return Err("Bad seconds");
112 },
113 })
114 }
115}
116
117impl core::fmt::Debug for Timestamp {
118 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
119 write!(f, "Timestamp({})", self)
120 }
121}
122
123impl core::fmt::Display for Timestamp {
124 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
125 write!(
126 f,
127 "{}-{:02}-{:02} {:02}:{:02}:{:02}",
128 u16::from(self.year_since_1970) + 1970,
129 self.zero_indexed_month + 1,
130 self.zero_indexed_day + 1,
131 self.hours,
132 self.minutes,
133 self.seconds
134 )
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[test]
143 fn date_encode1() {
144 let ts = Timestamp::from_calendar(2018, 12, 9, 19, 21, 38).unwrap();
146
147 let bytes = ts.serialize_to_fat();
148
149 assert_eq!(bytes, [0xb3, 0x9a, 0x89, 0x4d], "{:02x?} is wrong", &bytes);
151 }
152
153 #[test]
154 fn date_decode1() {
155 let ts = Timestamp::from_calendar(2018, 12, 9, 19, 21, 38).unwrap();
157
158 let actual_ts = Timestamp::from_fat(0x4d89, 0x9ab3);
160
161 assert_eq!(ts, actual_ts);
162 }
163
164 #[test]
165 fn date_encode2() {
166 let ts = Timestamp::from_calendar(2024, 10, 25, 16, 31, 14).unwrap();
168
169 let bytes = ts.serialize_to_fat();
170
171 assert_eq!(bytes, [0xe7, 0x83, 0x59, 0x59], "{:02x?} is wrong", &bytes);
173 }
174
175 #[test]
176 fn date_decode2() {
177 let ts = Timestamp::from_calendar(2024, 10, 25, 16, 31, 14).unwrap();
179
180 let actual_ts = Timestamp::from_fat(0x5959, 0x83e7);
182 assert_eq!(ts, actual_ts);
183 }
184 }
186
187