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
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
use chrono::{DateTime, Duration, TimeZone, Utc};

const NS_PER_US: u32 = 1_000;
const NS_PER_MS: u32 = 1_000_000;

#[inline]
fn us_to_ns(us: u32) -> u32 {
    us * NS_PER_US
}

#[inline]
fn ms_to_ns(ms: u32) -> u32 {
    ms * NS_PER_MS
}

#[derive(Debug, Default)]
pub struct Parts {
    pub year: i32,
    pub month: u32,
    pub day: u32,
    pub hour: u32,
    pub minute: u32,
    pub second: u32,
    pub millisecond: u32,
    pub microsecond: u32,
    pub nanosecond: u32,
}

#[derive(Debug)]
pub struct Epoch {
    pub datetime: DateTime<Utc>,
}

impl From<Parts> for Epoch {
    fn from(parts: Parts) -> Self {
        let total_ns = parts.nanosecond + us_to_ns(parts.microsecond) + ms_to_ns(parts.millisecond);
        let datetime = Utc.ymd(parts.year, parts.month, parts.day).and_hms_nano(
            parts.hour,
            parts.minute,
            parts.second,
            total_ns,
        );
        Epoch::new(datetime)
    }
}

impl Epoch {
    pub fn new(datetime: DateTime<Utc>) -> Epoch {
        Epoch { datetime }
    }

    pub fn from_parts(parts: Parts) -> Epoch {
        parts.into()
    }

    pub fn from_epoch_s(epoch_s: i64) -> Epoch {
        let datetime = Utc.timestamp(epoch_s, 0);
        Epoch::new(datetime)
    }

    pub fn from_epoch_ms(epoch_ms: i64) -> Epoch {
        let datetime = Utc.timestamp_millis(epoch_ms);
        Epoch::new(datetime)
    }

    pub fn from_epoch_us(epoch_us: i64) -> Epoch {
        let epoch_ns = epoch_us * 1000;
        Epoch::from_epoch_ns(epoch_ns)
    }

    pub fn from_epoch_ns(epoch_ns: i64) -> Epoch {
        let datetime = Utc.timestamp_nanos(epoch_ns);
        Epoch::new(datetime)
    }

    pub fn epoch_s(&self) -> i64 {
        self.datetime.timestamp()
    }

    pub fn epoch_ms(&self) -> i64 {
        self.datetime.timestamp_millis()
    }

    pub fn epoch_us(&self) -> i64 {
        ((self.epoch_ns() as f64) / 1000.0).round() as i64
    }

    pub fn epoch_ns(&self) -> i64 {
        self.datetime.timestamp_nanos()
    }

    pub fn plus_duration(mut self, duration: Duration) -> Self {
        self.datetime = self.datetime + duration;
        self
    }

    pub fn minus_duration(mut self, duration: Duration) -> Self {
        self.datetime = self.datetime - duration;
        self
    }
}

impl Default for Epoch {
    fn default() -> Self {
        Self {
            datetime: Utc::now(),
        }
    }
}