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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
use crate::{Time, TimeDiff, OFFSET_1601};
use chrono::{DateTime, NaiveDateTime, Local};
use core::fmt::Display;

/// System time, as grabbed from the system (obviously). Its timezone is dependent on the system's timezone as configured in the BIOS
///
/// `inner_secs` is the time as seconds since `1601-01-01 00:00:00`, from `chrono::Local`
/// `inner_milliseconds` is the subsec milliseconds
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct System {
    inner_secs: u64,
    inner_milliseconds: u64,
    pub utc_offset: i32,
}

impl Display for System {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "{}", self.strftime("%Y-%m-%d %H:%M:%S"))
    }
}

impl TimeDiff for System {}

impl Time for System {
    fn now() -> Self {
        let now: DateTime<Local> = Local::now();
        System {
            inner_secs: (now.timestamp() + OFFSET_1601 as i64) as u64,
            inner_milliseconds: (now.timestamp_subsec_millis()) as u64,
            utc_offset: now.offset().local_minus_utc(),
        }
    }

    fn utc_offset(&self) -> i32 {
        self.utc_offset
    }

    fn strptime<T: ToString, G: ToString>(s: T, format: G) -> Self {
        let s = s.to_string();
        let format = format.to_string();
        let temp = DateTime::parse_from_str(&s, &format);
        let x = match temp {
            Err(_) => {
                if !format.contains("%z") {
                    return Self::strptime(s + " +0000", format + "%z");
                }
                panic!("Bad format string");
            }
            Ok(dt) => dt,
        };
        System {
            inner_secs: (x.timestamp() + (OFFSET_1601 as i64)) as u64,
            inner_milliseconds: x.timestamp_subsec_millis() as u64,
            utc_offset: x.offset().local_minus_utc() as i32,
        }
    }

    fn unix(&self) -> i64 {
        (self.inner_secs as i64) - (OFFSET_1601 as i64)
    }
    fn unix_ms(&self) -> i64 {
        ((self.inner_secs as i64 * 1000i64) + self.inner_milliseconds as i64) - (OFFSET_1601 as i64 * 1000i64)
    }

    fn strftime(&self, format: &str) -> String {
        let timestamp = if self.inner_secs >= OFFSET_1601 {
            (self.inner_secs - OFFSET_1601) as i64
        } else {
            -((OFFSET_1601 as i64) - (self.inner_secs as i64))
        };
        NaiveDateTime::from_timestamp_opt(timestamp, 0)
            .unwrap()
            .format(format)
            .to_string()
    }

    fn from_epoch(timestamp: u64) -> Self {
        System {
            inner_secs: (timestamp / 1000),
            inner_milliseconds: timestamp % 1000,
            utc_offset: 0,
        }
    }

    fn raw(&self) -> u64 {
        (self.inner_secs * 1000) + self.inner_milliseconds
    }

    fn from_epoch_offset(timestamp: u64, offset: i32) -> Self {
        System {
            inner_secs: (timestamp / 1000),
            inner_milliseconds: timestamp % 1000,
            utc_offset: offset,
        }
    }
}