fatfs/
time.rs

1use crate::core::fmt::Debug;
2
3#[cfg(feature = "chrono")]
4use chrono;
5#[cfg(feature = "chrono")]
6use chrono::{Datelike, Local, TimeZone, Timelike};
7
8/// A DOS compatible date.
9///
10/// Used by `DirEntry` time-related methods.
11#[derive(Copy, Clone, Eq, PartialEq, Debug)]
12pub struct Date {
13    /// Full year - [1980, 2107]
14    pub year: u16,
15    /// Month of the year - [1, 12]
16    pub month: u16,
17    /// Day of the month - [1, 31]
18    pub day: u16,
19}
20
21impl Date {
22    pub(crate) fn decode(dos_date: u16) -> Self {
23        let (year, month, day) = ((dos_date >> 9) + 1980, (dos_date >> 5) & 0xF, dos_date & 0x1F);
24        Date { year, month, day }
25    }
26
27    pub(crate) fn epoch() -> Self {
28        Date { year: 1980, month: 1, day: 1 }
29    }
30
31    pub(crate) fn encode(&self) -> u16 {
32        ((self.year - 1980) << 9) | (self.month << 5) | self.day
33    }
34}
35
36/// A DOS compatible time.
37///
38/// Used by `DirEntry` time-related methods.
39#[derive(Copy, Clone, Eq, PartialEq, Debug)]
40pub struct Time {
41    /// Hours after midnight - [0, 23]
42    pub hour: u16,
43    /// Minutes after the hour - [0, 59]
44    pub min: u16,
45    /// Seconds after the minute - [0, 59]
46    pub sec: u16,
47    /// Milliseconds after the second - [0, 999]
48    pub millis: u16,
49}
50
51impl Time {
52    pub(crate) fn decode(dos_time: u16, dos_time_hi_res: u8) -> Self {
53        let hour = dos_time >> 11;
54        let min = (dos_time >> 5) & 0x3F;
55        let sec = (dos_time & 0x1F) * 2 + (dos_time_hi_res as u16) / 100;
56        let millis = (dos_time_hi_res as u16 % 100) * 10;
57        Time { hour, min, sec, millis }
58    }
59
60    pub(crate) fn encode(&self) -> (u16, u8) {
61        let dos_time = (self.hour << 11) | (self.min << 5) | (self.sec / 2);
62        let dos_time_hi_res = ((self.millis / 10) + (self.sec % 2) * 100) as u8;
63        (dos_time, dos_time_hi_res)
64    }
65}
66
67/// A DOS compatible date and time.
68///
69/// Used by `DirEntry` time-related methods.
70#[derive(Copy, Clone, Eq, PartialEq, Debug)]
71pub struct DateTime {
72    /// A date part
73    pub date: Date,
74    // A time part
75    pub time: Time,
76}
77
78impl DateTime {
79    pub(crate) fn decode(dos_date: u16, dos_time: u16, dos_time_hi_res: u8) -> Self {
80        DateTime { date: Date::decode(dos_date), time: Time::decode(dos_time, dos_time_hi_res) }
81    }
82
83    pub(crate) fn epoch() -> Self {
84        DateTime { date: Date::epoch(), time: Time::decode(0, 0) }
85    }
86}
87
88#[cfg(feature = "chrono")]
89impl From<Date> for chrono::Date<Local> {
90    fn from(date: Date) -> Self {
91        Local.ymd(date.year as i32, date.month as u32, date.day as u32)
92    }
93}
94
95#[cfg(feature = "chrono")]
96impl From<DateTime> for chrono::DateTime<Local> {
97    fn from(date_time: DateTime) -> Self {
98        chrono::Date::<Local>::from(date_time.date).and_hms_milli(
99            date_time.time.hour as u32,
100            date_time.time.min as u32,
101            date_time.time.sec as u32,
102            date_time.time.millis as u32,
103        )
104    }
105}
106
107#[cfg(feature = "chrono")]
108impl From<chrono::Date<Local>> for Date {
109    fn from(date: chrono::Date<Local>) -> Self {
110        Date { year: date.year() as u16, month: date.month() as u16, day: date.day() as u16 }
111    }
112}
113
114#[cfg(feature = "chrono")]
115impl From<chrono::DateTime<Local>> for DateTime {
116    fn from(date_time: chrono::DateTime<Local>) -> Self {
117        DateTime {
118            date: Date::from(date_time.date()),
119            time: Time {
120                hour: date_time.hour() as u16,
121                min: date_time.minute() as u16,
122                sec: date_time.second() as u16,
123                millis: (date_time.nanosecond() / 1_000_000) as u16,
124            },
125        }
126    }
127}
128
129/// A current time and date provider.
130///
131/// Provides a custom implementation for a time resolution used when updating directory entry time fields.
132/// `TimeProvider` is specified by the `time_provider` property in `FsOptions` struct.
133pub trait TimeProvider: Debug {
134    fn get_current_date(&self) -> Date;
135    fn get_current_date_time(&self) -> DateTime;
136}
137
138/// `TimeProvider` implementation that returns current local time retrieved from `chrono` crate.
139#[cfg(feature = "chrono")]
140#[derive(Debug, Clone, Copy)]
141pub struct ChronoTimeProvider {
142    _dummy: (),
143}
144
145#[cfg(feature = "chrono")]
146impl ChronoTimeProvider {
147    pub fn new() -> Self {
148        Self { _dummy: () }
149    }
150}
151
152#[cfg(feature = "chrono")]
153impl TimeProvider for ChronoTimeProvider {
154    fn get_current_date(&self) -> Date {
155        Date::from(chrono::Local::now().date())
156    }
157
158    fn get_current_date_time(&self) -> DateTime {
159        DateTime::from(chrono::Local::now())
160    }
161}
162
163/// `TimeProvider` implementation that always returns DOS minimal date-time (1980-01-01 00:00:00).
164#[derive(Debug, Clone, Copy)]
165pub struct NullTimeProvider {
166    _dummy: (),
167}
168
169impl NullTimeProvider {
170    pub fn new() -> Self {
171        Self { _dummy: () }
172    }
173}
174
175impl TimeProvider for NullTimeProvider {
176    fn get_current_date(&self) -> Date {
177        Date::epoch()
178    }
179
180    fn get_current_date_time(&self) -> DateTime {
181        DateTime::epoch()
182    }
183}
184
185/// Default time provider implementation.
186///
187/// Defined as `ChronoTimeProvider` if `chrono` feature is enabled. Otherwise defined as `NullTimeProvider`.
188#[cfg(feature = "chrono")]
189pub type DefaultTimeProvider = ChronoTimeProvider;
190#[cfg(not(feature = "chrono"))]
191pub type DefaultTimeProvider = NullTimeProvider;
192
193#[cfg(test)]
194mod tests {
195    use super::{Date, Time};
196
197    #[test]
198    fn date_encode_decode() {
199        let d = Date { year: 2055, month: 7, day: 23 };
200        let x = d.encode();
201        assert_eq!(x, 38647);
202        assert_eq!(d, Date::decode(x));
203    }
204
205    #[test]
206    fn time_encode_decode() {
207        let t1 = Time { hour: 15, min: 3, sec: 29, millis: 990 };
208        let t2 = Time { sec: 18, ..t1 };
209        let t3 = Time { millis: 40, ..t1 };
210        let (x1, y1) = t1.encode();
211        let (x2, y2) = t2.encode();
212        let (x3, y3) = t3.encode();
213        assert_eq!((x1, y1), (30830, 199));
214        assert_eq!((x2, y2), (30825, 99));
215        assert_eq!((x3, y3), (30830, 104));
216        assert_eq!(t1, Time::decode(x1, y1));
217        assert_eq!(t2, Time::decode(x2, y2));
218        assert_eq!(t3, Time::decode(x3, y3));
219    }
220}