use std::error::Error;
use std::fmt;
pub const NANOS_PER_SECOND: u32 = 1_000_000_000;
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Timestamp {
seconds: i64,
nanoseconds: u32,
}
impl Timestamp {
pub const fn new(seconds: i64, nanoseconds: u32) -> Result<Self, TimestampError> {
if nanoseconds >= NANOS_PER_SECOND {
return Err(TimestampError::NanosecondsOutOfRange { nanoseconds });
}
Ok(Self {
seconds,
nanoseconds,
})
}
#[must_use]
pub const fn seconds(self) -> i64 {
self.seconds
}
#[must_use]
pub const fn nanoseconds(self) -> u32 {
self.nanoseconds
}
#[must_use]
pub const fn nanos(self) -> u32 {
self.nanoseconds
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TimestampError {
NanosecondsOutOfRange {
nanoseconds: u32,
},
}
impl fmt::Display for TimestampError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NanosecondsOutOfRange { nanoseconds } => write!(
formatter,
"timestamp nanoseconds {nanoseconds} are outside 0..1_000_000_000"
),
}
}
}
impl Error for TimestampError {}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Date(i32);
impl Date {
#[must_use]
pub const fn new(days_since_unix_epoch: i32) -> Self {
Self(days_since_unix_epoch)
}
#[must_use]
pub const fn from_days_since_unix_epoch(days: i32) -> Self {
Self::new(days)
}
#[must_use]
pub const fn days_since_unix_epoch(self) -> i32 {
self.0
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct LogicalTime(Timestamp);
impl LogicalTime {
#[must_use]
pub const fn new(timestamp: Timestamp) -> Self {
Self(timestamp)
}
#[must_use]
pub const fn timestamp(self) -> Timestamp {
self.0
}
}
crate::inspect_owned_capacity_copy_v1!(Timestamp);
crate::inspect_owned_capacity_copy_v1!(Date);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn timestamp_accepts_only_canonical_nanoseconds() {
assert!(Timestamp::new(i64::MIN, 0).is_ok());
assert!(Timestamp::new(i64::MAX, NANOS_PER_SECOND - 1).is_ok());
assert_eq!(
Timestamp::new(0, NANOS_PER_SECOND),
Err(TimestampError::NanosecondsOutOfRange {
nanoseconds: NANOS_PER_SECOND
})
);
}
#[test]
fn date_and_logical_time_are_exact_wrappers() {
let date = Date::from_days_since_unix_epoch(-1);
assert_eq!(date.days_since_unix_epoch(), -1);
let timestamp = Timestamp::new(42, 7).expect("valid timestamp");
let logical = LogicalTime::new(timestamp);
assert_eq!(logical.timestamp(), timestamp);
}
}