aboutsummaryrefslogtreecommitdiff
path: root/src/zip/datetime.rs
blob: a4b1b55bebd3c5ecf1e006473fb5611015054f18 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
use crate::zip::{ZipError, ZipResult};
use chrono::{DateTime, Datelike, NaiveDate, TimeZone, Timelike};

pub trait DosDateTime<Tz>: Sized {
    fn from_dos_date_time(date: u16, time: u16, tz: Tz) -> ZipResult<Self>;

    #[allow(dead_code)]
    fn to_dos_date(&self) -> u16;

    fn to_dos_time(&self) -> u16;
}

impl<Tz: TimeZone> DosDateTime<Tz> for DateTime<Tz> {
    #[inline]
    fn from_dos_date_time(date: u16, time: u16, tz: Tz) -> ZipResult<Self> {
        Ok(NaiveDate::from_ymd_opt(
            (date as i32 >> 9 & 0x7F) + 1980,
            date as u32 >> 5 & 0xF,
            date as u32 & 0x1F,
        )
        .ok_or(ZipError::InvalidDate)?
        .and_hms_opt(
            (time as u32 >> 11) & 0x1F,
            (time as u32 >> 5) & 0x3F,
            (time as u32 & 0x1F) * 2,
        )
        .ok_or(ZipError::InvalidTime)?
        .and_local_timezone(tz)
        .unwrap())
    }

    #[inline]
    fn to_dos_date(&self) -> u16 {
        (self.year() as u16 - 1980 & 0x7F) << 9
            | (self.month() as u16 & 0xF) << 5
            | self.day() as u16 & 0x1F
    }

    #[inline]
    fn to_dos_time(&self) -> u16 {
        (self.hour() as u16 & 0x1F) << 11
            | (self.minute() as u16 & 0x3F) << 5
            | self.second() as u16 / 2 & 0x1F
    }
}