1use std::fmt;
2use std::time::{Duration, SystemTime, SystemTimeError};
3
4#[derive(Debug, Clone, Copy)]
6pub enum Day {
7 Sunday,
8 Monday,
9 Tuesday,
10 Wednesday,
11 Thursday,
12 Friday,
13 Saturday,
14}
15
16pub fn day_string(day: Day) -> &'static str {
18 match day {
19 Day::Sunday => "Sunday",
20 Day::Monday => "Monday",
21 Day::Tuesday => "Tuesday",
22 Day::Wednesday => "Wednesday",
23 Day::Thursday => "Thursday",
24 Day::Friday => "Friday",
25 Day::Saturday => "Saturday",
26 }
27}
28
29pub fn day_abbrev_string(day: Day) -> &'static str {
31 &day_string(day)[0..3]
32}
33
34impl fmt::Display for Day {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 write!(f, "{}", day_string(*self))
37 }
38}
39
40#[derive(Debug, Clone, Copy)]
42pub enum Month {
43 January,
44 February,
45 March,
46 April,
47 May,
48 June,
49 July,
50 August,
51 September,
52 October,
53 November,
54 December,
55}
56
57pub fn month_string(month: Month) -> &'static str {
59 match month {
60 Month::January => "January",
61 Month::February => "February",
62 Month::March => "March",
63 Month::April => "April",
64 Month::May => "May",
65 Month::June => "June",
66 Month::July => "July",
67 Month::August => "August",
68 Month::September => "September",
69 Month::October => "October",
70 Month::November => "November",
71 Month::December => "December",
72 }
73}
74
75pub fn month_abbrev_string(month: Month) -> &'static str {
77 &month_string(month)[0..3]
78}
79
80impl fmt::Display for Month {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 write!(f, "{}", month_string(*self))
83 }
84}
85
86pub fn days_in_year(year: u64) -> u64 {
88 if year % 400 == 0 {
89 366
90 } else if year % 100 == 0 {
91 365
92 } else if year % 4 == 0 {
93 366
94 } else {
95 365
96 }
97}
98
99pub fn days_in_month(year: u64, month: Month) -> u64 {
101 match month {
102 Month::January => 31,
103 Month::February if days_in_year(year) == 366 => 29,
104 Month::February => 28,
105 Month::March => 31,
106 Month::April => 30,
107 Month::May => 31,
108 Month::June => 30,
109 Month::July => 31,
110 Month::August => 31,
111 Month::September => 30,
112 Month::October => 31,
113 Month::November => 30,
114 Month::December => 31,
115 }
116}
117
118pub fn index_from_month(month: Month) -> u64 {
120 match month {
121 Month::January => 1,
122 Month::February => 2,
123 Month::March => 3,
124 Month::April => 4,
125 Month::May => 5,
126 Month::June => 6,
127 Month::July => 7,
128 Month::August => 8,
129 Month::September => 9,
130 Month::October => 10,
131 Month::November => 11,
132 Month::December => 12,
133 }
134}
135
136pub fn month_from_index(index: u64) -> Option<Month> {
139 match index {
140 1 => Some(Month::January),
141 2 => Some(Month::February),
142 3 => Some(Month::March),
143 4 => Some(Month::April),
144 5 => Some(Month::May),
145 6 => Some(Month::June),
146 7 => Some(Month::July),
147 8 => Some(Month::August),
148 9 => Some(Month::September),
149 10 => Some(Month::October),
150 11 => Some(Month::November),
151 12 => Some(Month::December),
152 _ => None,
153 }
154}
155
156pub fn seconds_in_day() -> u64 {
158 24 * 60 * 60
159}
160
161pub fn seconds_in_hour() -> u64 {
163 60 * 60
164}
165
166pub fn seconds_in_minute() -> u64 {
168 60
169}
170
171pub struct PostEpochTime {
178 delta: Duration,
179}
180
181impl PostEpochTime {
182 pub fn from(st: &SystemTime) -> Result<Self, SystemTimeError> {
185 Ok(PostEpochTime {
186 delta: st.duration_since(SystemTime::UNIX_EPOCH)?
187 })
188 }
189
190 pub fn now() -> Result<Self, SystemTimeError> {
193 Self::from(&SystemTime::now())
194 }
195
196 pub fn milliseconds_since_epoch(&self) -> u128 {
198 self.delta.as_millis()
199 }
200
201 pub fn microseconds_since_epoch(&self) -> u128 {
203 self.delta.as_micros()
204 }
205
206 pub fn nanoseconds_since_epoch(&self) -> u128 {
208 self.delta.as_nanos()
209 }
210
211 pub fn seconds_since_epoch(&self) -> u64 {
213 self.delta.as_secs()
214 }
215
216 pub fn days_since_epoch(&self) -> u64 {
218 self.delta.as_secs() / seconds_in_day()
219 }
220
221 pub fn day_of_week(&self) -> Day {
223 match self.days_since_epoch() % 7 {
224 0 => Day::Thursday,
225 1 => Day::Friday,
226 2 => Day::Saturday,
227 3 => Day::Sunday,
228 4 => Day::Monday,
229 5 => Day::Tuesday,
230 6 => Day::Wednesday,
231 _ => panic!("Modulo operator is broken"),
232 }
233 }
234
235 fn year_split(&self) -> (u64, u64) {
236 let mut days = self.days_since_epoch();
237 let mut year = 1970;
238 loop {
239 let in_year = days_in_year(year);
240 if days < in_year {
241 break;
242 }
243 days -= in_year;
244 year += 1;
245 }
246 (year, days)
247 }
248
249 pub fn year(&self) -> u64 {
251 self.year_split().0
252 }
253
254 pub fn day_of_year(&self) -> u64 {
259 self.year_split().1 + 1
260 }
261
262 fn month_split(&self) -> (Month, u64) {
263 let (year, mut days) = self.year_split();
264 let mut month = Month::January;
265 loop {
266 let in_month = days_in_month(year, month);
267 if days < in_month {
268 break;
269 }
270 days -= in_month;
271 month = month_from_index(index_from_month(month) + 1).expect("Month should never overflow");
272 }
273 (month, days)
274 }
275
276 pub fn month(&self) -> Month {
278 self.month_split().0
279 }
280
281 pub fn day_of_month(&self) -> u64 {
285 self.month_split().1 + 1
286 }
287
288 pub fn second_in_day(&self) -> u64 {
291 self.delta.as_secs() % seconds_in_day()
292 }
293
294 pub fn hour(&self) -> u64 {
297 self.second_in_day() / seconds_in_hour()
298 }
299
300 pub fn second_in_hour(&self) -> u64 {
303 self.second_in_day() % seconds_in_hour()
304 }
305
306 pub fn minute(&self) -> u64 {
309 self.second_in_hour() / seconds_in_minute()
310 }
311
312 pub fn second(&self) -> u64 {
315 self.delta.as_secs() % seconds_in_minute()
316 }
317}
318
319impl fmt::Display for PostEpochTime {
320 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
321 write!(f, "{}, {} {} {} {:02}:{:02}:{:02}",
322 day_abbrev_string(self.day_of_week()),
323 self.day_of_month(),
324 month_abbrev_string(self.month()),
325 self.year(),
326 self.hour(),
327 self.minute(),
328 self.second())
329 }
330}
331
332#[cfg(test)]
333mod tests {
334 use super::*;
335
336 #[test]
337 fn smoke_test() {
338 let timestamp = SystemTime::UNIX_EPOCH + Duration::new(1580610340, 123);
339 let pet = PostEpochTime::from(×tamp).unwrap();
340 assert_eq!(format!("{}", pet), "Sun, 2 Feb 2020 02:25:40".to_string());
341 }
342}