use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::io::{self, Read, Write};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Timestamp(u64);
impl Timestamp {
pub fn from_system_time(system_time: SystemTime) -> Timestamp {
Timestamp(timestamp_from_system_time(system_time))
}
pub fn to_system_time(self) -> SystemTime {
system_time_from_timestamp(self.0)
}
pub fn read_from<R: Read>(reader: &mut R) -> io::Result<Timestamp> {
Ok(Timestamp(reader.read_u64::<LittleEndian>()?))
}
pub fn write_to<W: Write>(self, writer: &mut W) -> io::Result<()> {
writer.write_u64::<LittleEndian>(self.0)
}
}
const UNIX_EPOCH_TIMESTAMP: u64 = 116444736000000000;
fn timestamp_from_system_time(system_time: SystemTime) -> u64 {
match system_time.duration_since(UNIX_EPOCH) {
Ok(duration) => {
let delta = duration_to_timestamp_delta(duration);
UNIX_EPOCH_TIMESTAMP.saturating_add(delta)
}
Err(err) => {
let delta = duration_to_timestamp_delta(err.duration());
UNIX_EPOCH_TIMESTAMP.saturating_sub(delta)
}
}
}
fn system_time_from_timestamp(timestamp: u64) -> SystemTime {
let system_time = if timestamp >= UNIX_EPOCH_TIMESTAMP {
UNIX_EPOCH.checked_add(timestamp_delta_to_duration(
timestamp - UNIX_EPOCH_TIMESTAMP,
))
} else {
UNIX_EPOCH.checked_sub(timestamp_delta_to_duration(
UNIX_EPOCH_TIMESTAMP - timestamp,
))
};
system_time.unwrap_or(UNIX_EPOCH)
}
fn duration_to_timestamp_delta(duration: Duration) -> u64 {
duration
.as_secs()
.saturating_mul(10_000_000)
.saturating_add((duration.subsec_nanos() / 100) as u64)
}
fn timestamp_delta_to_duration(delta: u64) -> Duration {
Duration::new(delta / 10_000_000, (delta % 10_000_000) as u32 * 100)
}
#[cfg(test)]
mod tests {
use super::{
duration_to_timestamp_delta, system_time_from_timestamp,
timestamp_delta_to_duration, timestamp_from_system_time,
UNIX_EPOCH_TIMESTAMP,
};
use std::time::{Duration, UNIX_EPOCH};
#[test]
fn extreme_timestamp_delta() {
let timestamp = u64::MAX;
let duration = timestamp_delta_to_duration(timestamp);
assert_eq!(duration.as_secs(), 1844674407370);
assert_eq!(duration.subsec_nanos(), 955161500);
assert_eq!(duration_to_timestamp_delta(duration), timestamp);
}
#[test]
fn extreme_duration() {
let duration = Duration::new(u64::MAX, 999_999_999);
assert_eq!(duration_to_timestamp_delta(duration), u64::MAX);
}
#[test]
fn unix_epoch() {
assert_eq!(
UNIX_EPOCH_TIMESTAMP,
timestamp_from_system_time(UNIX_EPOCH)
);
assert_eq!(
system_time_from_timestamp(UNIX_EPOCH_TIMESTAMP),
UNIX_EPOCH
);
}
#[test]
fn after_unix_epoch() {
let sat_18_mar_2017_at_18_46_36_utc =
UNIX_EPOCH + Duration::from_secs(1489862796);
assert_eq!(
timestamp_from_system_time(sat_18_mar_2017_at_18_46_36_utc),
131343363960000000,
);
assert_eq!(
system_time_from_timestamp(131343363960000000),
sat_18_mar_2017_at_18_46_36_utc
);
}
#[test]
fn before_unix_epoch() {
let sun_20_jul_1969_at_20_17_00_utc =
UNIX_EPOCH - Duration::from_secs(14182980);
assert_eq!(
timestamp_from_system_time(sun_20_jul_1969_at_20_17_00_utc),
116302906200000000,
);
assert_eq!(
system_time_from_timestamp(116302906200000000),
sun_20_jul_1969_at_20_17_00_utc
);
}
#[test]
fn extreme_timestamps() {
let min_time = system_time_from_timestamp(u64::MIN);
let max_time = system_time_from_timestamp(u64::MAX);
assert!(min_time <= max_time);
}
}