embedded_sdmmc/filesystem/
timestamp.rs

1/// Things that impl this can tell you the current time.
2pub trait TimeSource {
3    /// Returns the current time
4    fn get_timestamp(&self) -> Timestamp;
5}
6
7/// A Gregorian Calendar date/time, in the local time zone.
8///
9/// TODO: Consider replacing this with POSIX time as a `u32`, which would save
10/// two bytes at the expense of some maths.
11#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
12#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
13pub struct Timestamp {
14    /// Add 1970 to this file to get the calendar year
15    pub year_since_1970: u8,
16    /// Add one to this value to get the calendar month
17    pub zero_indexed_month: u8,
18    /// Add one to this value to get the calendar day
19    pub zero_indexed_day: u8,
20    /// The number of hours past midnight
21    pub hours: u8,
22    /// The number of minutes past the hour
23    pub minutes: u8,
24    /// The number of seconds past the minute
25    pub seconds: u8,
26}
27
28impl Timestamp {
29    /// Create a `Timestamp` from the 16-bit FAT date and time fields.
30    pub fn from_fat(date: u16, time: u16) -> Timestamp {
31        let year = 1980 + (date >> 9);
32        let month = ((date >> 5) & 0x000F) as u8;
33        let day = (date & 0x001F) as u8;
34        let hours = ((time >> 11) & 0x001F) as u8;
35        let minutes = ((time >> 5) & 0x0003F) as u8;
36        let seconds = ((time << 1) & 0x0003F) as u8;
37        // Volume labels have a zero for month/day, so tolerate that...
38        Timestamp {
39            year_since_1970: (year - 1970) as u8,
40            zero_indexed_month: if month == 0 { 0 } else { month - 1 },
41            zero_indexed_day: if day == 0 { 0 } else { day - 1 },
42            hours,
43            minutes,
44            seconds,
45        }
46    }
47
48    // TODO add tests for the method
49    /// Serialize a `Timestamp` to FAT format
50    pub fn serialize_to_fat(self) -> [u8; 4] {
51        let mut data = [0u8; 4];
52
53        let hours = (u16::from(self.hours) << 11) & 0xF800;
54        let minutes = (u16::from(self.minutes) << 5) & 0x07E0;
55        let seconds = (u16::from(self.seconds / 2)) & 0x001F;
56        data[..2].copy_from_slice(&(hours | minutes | seconds).to_le_bytes()[..]);
57
58        let year = if self.year_since_1970 < 10 {
59            0
60        } else {
61            (u16::from(self.year_since_1970 - 10) << 9) & 0xFE00
62        };
63        let month = (u16::from(self.zero_indexed_month + 1) << 5) & 0x01E0;
64        let day = u16::from(self.zero_indexed_day + 1) & 0x001F;
65        data[2..].copy_from_slice(&(year | month | day).to_le_bytes()[..]);
66        data
67    }
68
69    /// Create a `Timestamp` from year/month/day/hour/minute/second.
70    ///
71    /// Values should be given as you'd write then (i.e. 1980, 01, 01, 13, 30,
72    /// 05) is 1980-Jan-01, 1:30:05pm.
73    pub fn from_calendar(
74        year: u16,
75        month: u8,
76        day: u8,
77        hours: u8,
78        minutes: u8,
79        seconds: u8,
80    ) -> Result<Timestamp, &'static str> {
81        Ok(Timestamp {
82            year_since_1970: if (1970..=(1970 + 255)).contains(&year) {
83                (year - 1970) as u8
84            } else {
85                return Err("Bad year");
86            },
87            zero_indexed_month: if (1..=12).contains(&month) {
88                month - 1
89            } else {
90                return Err("Bad month");
91            },
92            zero_indexed_day: if (1..=31).contains(&day) {
93                day - 1
94            } else {
95                return Err("Bad day");
96            },
97            hours: if hours <= 23 {
98                hours
99            } else {
100                return Err("Bad hours");
101            },
102            minutes: if minutes <= 59 {
103                minutes
104            } else {
105                return Err("Bad minutes");
106            },
107            seconds: if seconds <= 59 {
108                seconds
109            } else {
110                return Err("Bad seconds");
111            },
112        })
113    }
114}
115
116impl core::fmt::Debug for Timestamp {
117    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
118        write!(f, "Timestamp({})", self)
119    }
120}
121
122impl core::fmt::Display for Timestamp {
123    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
124        write!(
125            f,
126            "{}-{:02}-{:02} {:02}:{:02}:{:02}",
127            u16::from(self.year_since_1970) + 1970,
128            self.zero_indexed_month + 1,
129            self.zero_indexed_day + 1,
130            self.hours,
131            self.minutes,
132            self.seconds
133        )
134    }
135}
136
137// ****************************************************************************
138//
139// End Of File
140//
141// ****************************************************************************