use std::ops::Add;
use std::ops::Sub;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct Timestamp {
pub(crate) seconds: i64,
pub(crate) nanos: u32,
}
impl Timestamp {
pub const fn new(mut seconds: i64, mut nanos: u32) -> Timestamp {
while nanos >= 1_000_000_000 {
seconds += 1;
nanos -= 1_000_000_000;
}
Timestamp { seconds, nanos }
}
pub const fn from_nanos(nanos: i128) -> Timestamp {
let seconds: i64 = (nanos / 1_000_000_000) as i64;
let nanos = if seconds >= 0 {
(nanos % 1_000_000_000) as u32
}
else {
(1_000_000_000 - (nanos % 1_000_000_000).abs()) as u32
};
Timestamp { seconds, nanos }
}
pub const fn from_micros(micros: i64) -> Timestamp {
Timestamp::from_nanos(micros as i128 * 1_000)
}
pub const fn from_millis(millis: i64) -> Timestamp {
Timestamp::from_nanos(millis as i128 * 1_000_000)
}
pub const fn seconds(&self) -> i64 {
self.seconds
}
pub const fn at_precision(&self, e: u8) -> i128 {
(self.seconds as i128) * 10i128.pow(e as u32)
+ (self.nanos as i128) / 10i128.pow(9 - (e as u32))
}
pub fn subsec(&self, e: u8) -> u32 {
self.nanos / 10u32.pow(9 - u32::from(e))
}
}
impl Add for Timestamp {
type Output = Self;
fn add(self, other: Timestamp) -> Timestamp {
Timestamp::new(self.seconds + other.seconds, self.nanos + other.nanos)
}
}
impl Sub for Timestamp {
type Output = Self;
fn sub(self, other: Timestamp) -> Timestamp {
if other.nanos > self.nanos {
return Timestamp::new(
self.seconds - other.seconds - 1,
self.nanos + 1_000_000_000 - other.nanos,
);
}
Timestamp::new(self.seconds - other.seconds, self.nanos - other.nanos)
}
}
#[cfg(test)]
#[allow(clippy::inconsistent_digit_grouping)]
mod tests {
use super::*;
use assert2::check;
#[test]
fn test_cmp() {
check!(Timestamp::from(1335020400) < Timestamp::from(1335024000));
check!(Timestamp::from(1335020400) == Timestamp::from(1335020400));
check!(
Timestamp::new(1335020400, 500_000_000)
< Timestamp::new(1335020400, 750_000_000)
);
check!(Timestamp::new(1, 999_999_999) < Timestamp::from(2));
}
#[test]
fn test_from_nanos() {
check!(
Timestamp::from_nanos(1335020400_000_000_000i128)
== Timestamp::new(1335020400, 0)
);
check!(
Timestamp::from_nanos(1335020400_500_000_000i128)
== Timestamp::new(1335020400, 500_000_000)
);
check!(
Timestamp::from_nanos(-1_750_000_000) == Timestamp::new(-1, 250_000_000)
);
}
#[test]
fn test_from_micros() {
check!(
Timestamp::from_micros(1335020400_000_000i64)
== Timestamp::new(1335020400, 0)
);
check!(
Timestamp::from_micros(1335020400_500_000i64)
== Timestamp::new(1335020400, 500_000_000)
);
check!(
Timestamp::from_micros(-1_750_000) == Timestamp::new(-1, 250_000_000)
);
}
#[test]
fn test_from_millis() {
check!(
Timestamp::from_millis(1335020400_000i64)
== Timestamp::new(1335020400, 0)
);
check!(
Timestamp::from_millis(1335020400_500i64)
== Timestamp::new(1335020400, 500_000_000)
);
check!(Timestamp::from_millis(-1_750) == Timestamp::new(-1, 250_000_000));
}
#[test]
fn test_seconds() {
assert_eq!(Timestamp::from(1335020400).seconds, 1335020400);
}
#[test]
fn test_at_precision() {
let ts = Timestamp::new(1335020400, 123456789);
assert_eq!(ts.at_precision(3), 1335020400123);
assert_eq!(ts.at_precision(6), 1335020400123456);
assert_eq!(ts.at_precision(9), 1335020400123456789);
}
#[test]
fn test_subsec() {
let ts = Timestamp::new(1335020400, 123456789);
assert_eq!(ts.subsec(3), 123);
assert_eq!(ts.subsec(6), 123456);
assert_eq!(ts.subsec(9), 123456789);
}
#[test]
fn test_add() {
let ts = Timestamp::from(1335020400) + Timestamp::new(86400, 1_000_000);
assert_eq!(ts.seconds(), 1335020400 + 86400);
assert_eq!(ts.subsec(3), 1);
}
#[test]
fn test_sub() {
let ts = Timestamp::from(1335020400) - Timestamp::new(86400, 0);
assert_eq!(ts.seconds(), 1335020400 - 86400);
assert_eq!(ts.nanos, 0);
}
#[test]
fn test_sub_nano_overflow() {
let ts = Timestamp::from(1335020400) - Timestamp::new(0, 500_000_000);
assert_eq!(ts.seconds(), 1335020399);
assert_eq!(ts.subsec(1), 5);
}
}