1use crate::core::fmt::Debug;
2
3#[cfg(feature = "chrono")]
4use chrono;
5#[cfg(feature = "chrono")]
6use chrono::{Datelike, Local, TimeZone, Timelike};
7
8#[derive(Copy, Clone, Eq, PartialEq, Debug)]
12pub struct Date {
13 pub year: u16,
15 pub month: u16,
17 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#[derive(Copy, Clone, Eq, PartialEq, Debug)]
40pub struct Time {
41 pub hour: u16,
43 pub min: u16,
45 pub sec: u16,
47 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#[derive(Copy, Clone, Eq, PartialEq, Debug)]
71pub struct DateTime {
72 pub date: Date,
74 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
129pub trait TimeProvider: Debug {
134 fn get_current_date(&self) -> Date;
135 fn get_current_date_time(&self) -> DateTime;
136}
137
138#[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#[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#[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}