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 chrono::{DateTime, Local, Utc};
use zerocopy::{FromBytes, FromZeroes};

const MICROSEC_PER_SEC: u64 = 1_000_000;
const NANOSEC_PER_MICROSEC: u64 = 1_000;
const WIN_TO_UNIX_EPOCH_DELTA_SEC: u64 = 11_644_473_600;
const WIN_TO_UNIX_EPOCH_DIFF_MICROSEC: u64 = WIN_TO_UNIX_EPOCH_DELTA_SEC * MICROSEC_PER_SEC;

/// Represents a time in microseconds since the Windows epoch (1601-01-01 00:00:00 UTC) (used in
/// the chrome cache format).
#[derive(Debug, FromZeroes, FromBytes, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct WindowsEpochMicroseconds(u64);

impl From<WindowsEpochMicroseconds> for DateTime<Utc> {
    fn from(micro_seconds: WindowsEpochMicroseconds) -> Self {
        let windows_micro_seconds: u64 = micro_seconds.0;

        let unix_micro_seconds = windows_micro_seconds - WIN_TO_UNIX_EPOCH_DIFF_MICROSEC;
        let unix_seconds = unix_micro_seconds / MICROSEC_PER_SEC;
        let unix_nanoseconds = (unix_micro_seconds % MICROSEC_PER_SEC) * NANOSEC_PER_MICROSEC;

        DateTime::from_timestamp(unix_seconds as i64, unix_nanoseconds as u32).unwrap()
    }
}

impl From<WindowsEpochMicroseconds> for DateTime<Local> {
    fn from(micro_seconds: WindowsEpochMicroseconds) -> Self {
        let utc = DateTime::<Utc>::from(micro_seconds);
        utc.with_timezone(&Local)
    }
}

#[cfg(test)]
#[test]
fn test_windows_epoch_microseconds() {
    use chrono::{Datelike, Timelike};
    let windows_us = WindowsEpochMicroseconds(13_360_111_021_811_283);
    let date = DateTime::<Utc>::from(windows_us);

    assert_eq!(date.year(), 2024);
    assert_eq!(date.month(), 5);
    assert_eq!(date.day(), 13);
    assert_eq!(date.hour(), 21);
    assert_eq!(date.second(), 1);
}