pub(crate) mod builder;
use std::{
convert::TryInto,
fmt::{self, Display},
time::{Duration, SystemTime},
};
#[cfg(feature = "chrono-0_4")]
use chrono::{LocalResult, TimeZone, Utc};
use time::format_description::well_known::Rfc3339;
pub use crate::datetime::builder::DateTimeBuilder;
use crate::error::{Error, Result};
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Copy, Clone)]
pub struct DateTime(i64);
impl crate::DateTime {
pub const MAX: Self = Self::from_millis(i64::MAX);
pub const MIN: Self = Self::from_millis(i64::MIN);
pub const fn from_millis(date: i64) -> Self {
Self(date)
}
pub fn now() -> DateTime {
Self::from_system_time(SystemTime::now())
}
#[cfg(feature = "chrono-0_4")]
pub fn from_chrono<T: chrono::TimeZone>(dt: chrono::DateTime<T>) -> Self {
Self::from_millis(dt.timestamp_millis())
}
#[cfg(feature = "jiff-0_2")]
pub fn from_jiff(ts: jiff::Timestamp) -> Self {
Self::from_millis(ts.as_millisecond())
}
pub fn builder() -> DateTimeBuilder {
DateTimeBuilder::default()
}
#[cfg(feature = "chrono-0_4")]
pub fn to_chrono(self) -> chrono::DateTime<Utc> {
match Utc.timestamp_millis_opt(self.0) {
LocalResult::Single(dt) => dt,
_ => {
if self.0 < 0 {
chrono::DateTime::<Utc>::MIN_UTC
} else {
chrono::DateTime::<Utc>::MAX_UTC
}
}
}
}
#[cfg(feature = "jiff-0_2")]
pub fn to_jiff(self) -> jiff::Timestamp {
jiff::Timestamp::from_millisecond(self.0).unwrap_or({
if self.0 < 0 {
jiff::Timestamp::MIN
} else {
jiff::Timestamp::MAX
}
})
}
fn from_time_private(dt: time::OffsetDateTime) -> Self {
let millis = dt.unix_timestamp_nanos() / 1_000_000;
match millis.try_into() {
Ok(ts) => Self::from_millis(ts),
_ => {
if millis > 0 {
Self::MAX
} else {
Self::MIN
}
}
}
}
#[cfg(not(feature = "time-0_3"))]
#[allow(unused)]
pub(crate) fn from_time_0_3(dt: time::OffsetDateTime) -> Self {
Self::from_time_private(dt)
}
#[cfg(feature = "time-0_3")]
pub fn from_time_0_3(dt: time::OffsetDateTime) -> Self {
Self::from_time_private(dt)
}
fn to_time_private(self) -> time::OffsetDateTime {
match self.to_time_opt() {
Some(dt) => dt,
None => if self.0 < 0 {
time::PrimitiveDateTime::MIN
} else {
time::PrimitiveDateTime::MAX
}
.assume_utc(),
}
}
pub(crate) fn to_time_opt(self) -> Option<time::OffsetDateTime> {
time::OffsetDateTime::UNIX_EPOCH.checked_add(time::Duration::milliseconds(self.0))
}
#[cfg(not(feature = "time-0_3"))]
#[allow(unused)]
pub(crate) fn to_time_0_3(self) -> time::OffsetDateTime {
self.to_time_private()
}
#[cfg(feature = "time-0_3")]
pub fn to_time_0_3(self) -> time::OffsetDateTime {
self.to_time_private()
}
pub fn from_system_time(st: SystemTime) -> Self {
match st.duration_since(SystemTime::UNIX_EPOCH) {
Ok(d) => {
if d.as_millis() <= i64::MAX as u128 {
Self::from_millis(d.as_millis() as i64)
} else {
Self::MAX
}
}
Err(e) => {
let millis = e.duration().as_millis();
if millis > i64::MAX as u128 {
Self::MIN
} else {
Self::from_millis(-(millis as i64))
}
}
}
}
pub fn to_system_time(self) -> SystemTime {
if self.0 >= 0 {
SystemTime::UNIX_EPOCH + Duration::from_millis(self.0 as u64)
} else {
SystemTime::UNIX_EPOCH - Duration::from_millis((self.0 as i128).unsigned_abs() as u64)
}
}
pub const fn timestamp_millis(self) -> i64 {
self.0
}
pub const fn saturating_add_millis(self, millis: i64) -> Self {
Self::from_millis(self.0.saturating_add(millis))
}
pub const fn saturating_add_duration(self, duration: Duration) -> Self {
let millis = duration.as_millis();
if millis > i64::MAX as u128 {
Self::from_millis(i64::MAX)
} else {
self.saturating_add_millis(millis as i64)
}
}
pub fn try_to_rfc3339_string(self) -> Result<String> {
self.to_time_0_3().format(&Rfc3339).map_err(Error::datetime)
}
pub fn parse_rfc3339_str(s: impl AsRef<str>) -> Result<Self> {
let odt = time::OffsetDateTime::parse(s.as_ref(), &Rfc3339).map_err(Error::datetime)?;
Ok(Self::from_time_0_3(odt))
}
pub fn checked_duration_since(self, earlier: Self) -> Option<Duration> {
if earlier.0 > self.0 {
return None;
}
Some(Duration::from_millis((self.0 - earlier.0) as u64))
}
pub fn saturating_duration_since(self, earlier: Self) -> Duration {
self.checked_duration_since(earlier)
.unwrap_or(Duration::ZERO)
}
}
impl fmt::Debug for crate::DateTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut tup = f.debug_tuple("DateTime");
match self.to_time_opt() {
Some(dt) => tup.field(&dt),
_ => tup.field(&self.0),
};
tup.finish()
}
}
impl Display for crate::DateTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.to_time_opt() {
Some(dt) => Display::fmt(&dt, f),
_ => Display::fmt(&self.0, f),
}
}
}
impl From<SystemTime> for crate::DateTime {
fn from(st: SystemTime) -> Self {
Self::from_system_time(st)
}
}
impl From<crate::DateTime> for SystemTime {
fn from(dt: crate::DateTime) -> Self {
dt.to_system_time()
}
}
#[cfg(feature = "chrono-0_4")]
impl From<crate::DateTime> for chrono::DateTime<Utc> {
fn from(bson_dt: DateTime) -> Self {
bson_dt.to_chrono()
}
}
#[cfg(feature = "chrono-0_4")]
impl<T: chrono::TimeZone> From<chrono::DateTime<T>> for crate::DateTime {
fn from(x: chrono::DateTime<T>) -> Self {
Self::from_chrono(x)
}
}
#[cfg(feature = "jiff-0_2")]
impl From<crate::DateTime> for jiff::Timestamp {
fn from(bson_dt: DateTime) -> Self {
bson_dt.to_jiff()
}
}
#[cfg(feature = "jiff-0_2")]
impl From<jiff::Timestamp> for crate::DateTime {
fn from(x: jiff::Timestamp) -> Self {
Self::from_jiff(x)
}
}
#[cfg(feature = "time-0_3")]
impl From<crate::DateTime> for time::OffsetDateTime {
fn from(bson_dt: DateTime) -> Self {
bson_dt.to_time_0_3()
}
}
#[cfg(feature = "time-0_3")]
impl From<time::OffsetDateTime> for crate::DateTime {
fn from(x: time::OffsetDateTime) -> Self {
Self::from_time_0_3(x)
}
}