chrome_cache_parser/
time.rs1use chrono::{DateTime, Local, Utc};
2use zerocopy::{FromBytes, FromZeroes};
3
4use crate::{CCPError, CCPResult};
5
6const MICROSEC_PER_SEC: u64 = 1_000_000;
7const NANOSEC_PER_MICROSEC: u64 = 1_000;
8const WIN_TO_UNIX_EPOCH_DELTA_SEC: u64 = 11_644_473_600;
9const WIN_TO_UNIX_EPOCH_DIFF_MICROSEC: u64 = WIN_TO_UNIX_EPOCH_DELTA_SEC * MICROSEC_PER_SEC;
10
11#[derive(Debug, FromZeroes, FromBytes, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
14pub struct WindowsEpochMicroseconds(u64);
15
16impl WindowsEpochMicroseconds {
17 pub fn into_datetime_utc(self) -> CCPResult<DateTime<Utc>> {
18 let windows_micro_seconds: u64 = self.0;
19
20 let unix_micro_seconds = windows_micro_seconds
21 .checked_sub(WIN_TO_UNIX_EPOCH_DIFF_MICROSEC)
22 .ok_or(CCPError::InvalidTimestamp(windows_micro_seconds))?;
23 let unix_seconds = unix_micro_seconds / MICROSEC_PER_SEC;
24 let unix_nanoseconds = (unix_micro_seconds % MICROSEC_PER_SEC) * NANOSEC_PER_MICROSEC;
25
26 DateTime::from_timestamp(unix_seconds as i64, unix_nanoseconds as u32)
27 .ok_or(CCPError::InvalidTimestamp(windows_micro_seconds))
28 }
29
30 pub fn into_datetime_local(self) -> CCPResult<DateTime<Local>> {
31 let utc: CCPResult<DateTime<Utc>> = self.into_datetime_utc();
32 Ok(utc?.with_timezone(&Local))
33 }
34}
35
36#[cfg(test)]
37#[test]
38fn test_windows_epoch_microseconds() {
39 use chrono::{Datelike, Timelike};
40 let timestamp = WindowsEpochMicroseconds(13_360_111_021_811_283);
41 let date: CCPResult<DateTime<Utc>> = timestamp.into_datetime_utc();
42 let date = date.unwrap();
43
44 assert_eq!(date.year(), 2024);
45 assert_eq!(date.month(), 5);
46 assert_eq!(date.day(), 13);
47 assert_eq!(date.hour(), 21);
48 assert_eq!(date.second(), 1);
49}
50
51#[test]
52fn test_windows_epoch_from_0_input_returns_error() {
53 let timestamp = WindowsEpochMicroseconds(0);
54 let date: CCPResult<DateTime<Utc>> = timestamp.into_datetime_utc();
55 assert!(date.is_err());
56}