#[cfg(feature = "chrono")]
use chrono::{DateTime, Utc};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
const NANOS_PER_SECOND: i128 = 1_000_000_000;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct DurationWireValue {
pub(crate) seconds: u64,
pub(crate) nanos: u32,
}
impl From<Duration> for DurationWireValue {
fn from(duration: Duration) -> Self {
Self {
seconds: duration.as_secs(),
nanos: duration.subsec_nanos(),
}
}
}
impl DurationWireValue {
pub(crate) fn is_valid(self) -> bool {
self.nanos < NANOS_PER_SECOND as u32
}
pub(crate) fn into_duration(self) -> Option<Duration> {
self.is_valid()
.then(|| Duration::new(self.seconds, self.nanos))
}
pub(crate) fn write_to(self, buffer: &mut [u8]) -> usize {
buffer[..8].copy_from_slice(&self.seconds.to_le_bytes());
buffer[8..12].copy_from_slice(&self.nanos.to_le_bytes());
12
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct EpochTimestampWireValue {
pub(crate) seconds: i64,
pub(crate) nanos: u32,
}
impl From<SystemTime> for EpochTimestampWireValue {
fn from(system_time: SystemTime) -> Self {
let total_nanos = match system_time.duration_since(UNIX_EPOCH) {
Ok(duration) => {
(duration.as_secs() as i128) * NANOS_PER_SECOND + duration.subsec_nanos() as i128
}
Err(error) => {
let duration = error.duration();
-((duration.as_secs() as i128) * NANOS_PER_SECOND + duration.subsec_nanos() as i128)
}
};
Self {
seconds: total_nanos.div_euclid(NANOS_PER_SECOND) as i64,
nanos: total_nanos.rem_euclid(NANOS_PER_SECOND) as u32,
}
}
}
#[cfg(feature = "chrono")]
impl From<DateTime<Utc>> for EpochTimestampWireValue {
fn from(date_time: DateTime<Utc>) -> Self {
Self {
seconds: date_time.timestamp(),
nanos: date_time.timestamp_subsec_nanos(),
}
}
}
impl EpochTimestampWireValue {
pub(crate) fn is_valid(self) -> bool {
self.nanos < NANOS_PER_SECOND as u32
}
pub(crate) fn into_system_time(self) -> Option<SystemTime> {
self.is_valid().then(|| {
let total_nanos = (self.seconds as i128) * NANOS_PER_SECOND + self.nanos as i128;
if total_nanos >= 0 {
let duration = Duration::new(
(total_nanos / NANOS_PER_SECOND) as u64,
(total_nanos % NANOS_PER_SECOND) as u32,
);
UNIX_EPOCH + duration
} else {
let absolute_nanos = (-total_nanos) as u128;
let absolute_seconds = (absolute_nanos / NANOS_PER_SECOND as u128) as u64;
let absolute_fraction = (absolute_nanos % NANOS_PER_SECOND as u128) as u32;
UNIX_EPOCH - Duration::new(absolute_seconds, absolute_fraction)
}
})
}
#[cfg(feature = "chrono")]
pub(crate) fn into_date_time_utc(self) -> Option<DateTime<Utc>> {
self.is_valid()
.then(|| DateTime::from_timestamp(self.seconds, self.nanos))
.flatten()
}
pub(crate) fn write_to(self, buffer: &mut [u8]) -> usize {
buffer[..8].copy_from_slice(&self.seconds.to_le_bytes());
buffer[8..12].copy_from_slice(&self.nanos.to_le_bytes());
12
}
}