rc_zip/parse/
date_time.rs1use chrono::{
2 offset::{LocalResult, TimeZone, Utc},
3 DateTime, Timelike,
4};
5use ownable::{IntoOwned, ToOwned};
6use std::fmt;
7use winnow::{
8 binary::{le_u16, le_u64},
9 seq, PResult, Parser, Partial,
10};
11
12#[derive(Clone, Copy, Eq, PartialEq, IntoOwned, ToOwned)]
16pub struct MsdosTimestamp {
17 pub time: u16,
19
20 pub date: u16,
22}
23
24impl fmt::Debug for MsdosTimestamp {
25 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
26 match self.to_datetime() {
27 Some(dt) => write!(f, "MsdosTimestamp({})", dt),
28 None => write!(f, "MsdosTimestamp(?)"),
29 }
30 }
31}
32
33impl MsdosTimestamp {
34 pub fn parser(i: &mut Partial<&'_ [u8]>) -> PResult<Self> {
36 seq! {Self {
37 time: le_u16,
38 date: le_u16,
39 }}
40 .parse_next(i)
41 }
42
43 pub fn to_datetime(&self) -> Option<DateTime<Utc>> {
45 let res = {
47 let d = (self.date & 0b1_1111) as u32;
49 let m = ((self.date >> 5) & 0b1111) as u32;
51 let y = ((self.date >> 9) + 1980) as i32;
53 Utc.with_ymd_and_hms(y, m, d, 0, 0, 0)
54 };
55
56 let date = match res {
57 LocalResult::Single(date) => date,
58 _ => return None,
59 };
60
61 let s = (self.time & 0b1_1111) as u32 * 2;
63 let m = (self.time >> 5 & 0b11_1111) as u32;
65 let h = (self.time >> 11) as u32;
67 date.with_hour(h)?.with_minute(m)?.with_second(s)
68 }
69}
70
71#[derive(Clone, Copy, Eq, PartialEq)]
73pub struct NtfsTimestamp {
74 pub timestamp: u64,
76}
77
78impl fmt::Debug for NtfsTimestamp {
79 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
80 match self.to_datetime() {
81 Some(dt) => write!(f, "NtfsTimestamp({})", dt),
82 None => write!(f, "NtfsTimestamp(?)"),
83 }
84 }
85}
86
87impl NtfsTimestamp {
88 pub fn parser(i: &mut Partial<&'_ [u8]>) -> PResult<Self> {
90 le_u64.map(|timestamp| Self { timestamp }).parse_next(i)
91 }
92
93 pub fn to_datetime(&self) -> Option<DateTime<Utc>> {
95 let ticks_per_second = 10_000_000;
97 let secs = (self.timestamp / ticks_per_second) as i64;
98 let nsecs = ((self.timestamp % ticks_per_second) * 100) as u32;
99 let epoch = Utc.with_ymd_and_hms(1601, 1, 1, 0, 0, 0).single()?;
100 match Utc.timestamp_opt(epoch.timestamp() + secs, nsecs) {
101 LocalResult::Single(date) => Some(date),
102 _ => None,
103 }
104 }
105}
106
107pub(crate) fn zero_datetime() -> chrono::DateTime<chrono::offset::Utc> {
108 chrono::DateTime::<Utc>::from_timestamp_millis(0).unwrap()
109}