use crate::errors::{EpochError, Result};
use time::{Date, Month, OffsetDateTime, PrimitiveDateTime, Time};
pub mod conversions;
pub mod errors;
#[derive(Debug, Default)]
pub struct DateTimeParts {
pub year: i32,
pub month: u8,
pub day: u8,
pub hour: u8,
pub minute: u8,
pub second: u8,
pub millisecond: u32,
pub microsecond: u32,
pub nanosecond: u32,
}
impl DateTimeParts {
pub fn total_ns(&self) -> Result<u32> {
conversions::ms_to_ns_u32(self.millisecond)?
.checked_add(conversions::us_to_ns_u32(self.microsecond)?)
.ok_or_else(|| EpochError::numeric_precision("When summing total ns"))?
.checked_add(self.nanosecond)
.ok_or_else(|| EpochError::numeric_precision("When summing total ns"))
}
}
#[derive(Debug)]
pub struct Epoch {
datetime: OffsetDateTime,
}
impl TryFrom<DateTimeParts> for Epoch {
type Error = EpochError;
fn try_from(parts: DateTimeParts) -> Result<Self> {
let datetime = PrimitiveDateTime::new(
Date::from_calendar_date(parts.year, parse_month(parts.month)?, parts.day)?,
Time::from_hms_nano(parts.hour, parts.minute, parts.second, parts.total_ns()?)?,
)
.assume_utc();
Ok(Epoch::new(datetime))
}
}
fn parse_month(value: u8) -> Result<Month> {
match value {
1 => Ok(Month::January),
2 => Ok(Month::February),
3 => Ok(Month::March),
4 => Ok(Month::April),
5 => Ok(Month::May),
6 => Ok(Month::June),
7 => Ok(Month::July),
8 => Ok(Month::August),
9 => Ok(Month::September),
10 => Ok(Month::October),
11 => Ok(Month::November),
12 => Ok(Month::December),
i => Err(EpochError {
err: format!("Illegal month integral={}. Valid values are [1-12].", i),
}),
}
}
impl Epoch {
pub const fn new(datetime: OffsetDateTime) -> Epoch {
Epoch { datetime }
}
pub fn from_parts(parts: DateTimeParts) -> Result<Epoch> {
parts.try_into()
}
pub fn from_epoch_s(epoch_s: i64) -> Result<Epoch> {
let datetime = OffsetDateTime::from_unix_timestamp(epoch_s)?;
Ok(Epoch::new(datetime))
}
pub fn from_epoch_ms(epoch_ms: i128) -> Result<Epoch> {
Epoch::from_epoch_ns(conversions::ms_to_ns_i128(epoch_ms)?)
}
pub fn from_epoch_us(epoch_us: i128) -> Result<Epoch> {
Epoch::from_epoch_ns(conversions::us_to_ns_i128(epoch_us)?)
}
pub fn from_epoch_ns(epoch_ns: i128) -> Result<Epoch> {
let datetime = OffsetDateTime::from_unix_timestamp_nanos(epoch_ns)?;
Ok(Epoch::new(datetime))
}
pub fn epoch_s(&self) -> i64 {
self.datetime.unix_timestamp()
}
pub fn epoch_ms(&self) -> Result<i128> {
conversions::ns_to_ms_i128(self.epoch_ns())
}
pub fn epoch_us(&self) -> Result<i128> {
conversions::ns_to_us_i128(self.epoch_ns())
}
pub fn epoch_ns(&self) -> i128 {
self.datetime.unix_timestamp_nanos()
}
pub fn fmt(&self) -> String {
self.datetime.to_string()
}
}
impl Default for Epoch {
fn default() -> Self {
Self {
datetime: OffsetDateTime::now_utc(),
}
}
}