use std::{
error,
fmt::{self, Display},
result,
time::{Duration, SystemTime},
};
#[cfg(all(feature = "serde_with", feature = "chrono-0_4"))]
use serde::{Deserialize, Deserializer, Serialize};
#[cfg(all(feature = "serde_with", feature = "chrono-0_4"))]
use serde_with::{DeserializeAs, SerializeAs};
use chrono::{LocalResult, TimeZone, Utc};
#[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(not(feature = "chrono-0_4"))]
pub(crate) fn from_chrono<T: chrono::TimeZone>(dt: chrono::DateTime<T>) -> Self {
Self::from_millis(dt.timestamp_millis())
}
#[cfg(feature = "chrono-0_4")]
#[cfg_attr(docsrs, doc(cfg(feature = "chrono-0_4")))]
pub fn from_chrono<T: chrono::TimeZone>(dt: chrono::DateTime<T>) -> Self {
Self::from_millis(dt.timestamp_millis())
}
fn to_chrono_private(self) -> chrono::DateTime<Utc> {
match Utc.timestamp_millis_opt(self.0) {
LocalResult::Single(dt) => dt,
_ => {
if self.0 < 0 {
chrono::MIN_DATETIME
} else {
chrono::MAX_DATETIME
}
}
}
}
#[cfg(not(feature = "chrono-0_4"))]
#[allow(unused)]
pub(crate) fn to_chrono(self) -> chrono::DateTime<Utc> {
self.to_chrono_private()
}
#[cfg(feature = "chrono-0_4")]
#[cfg_attr(docsrs, doc(cfg(feature = "chrono-0_4")))]
pub fn to_chrono(self) -> chrono::DateTime<Utc> {
self.to_chrono_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).abs() as u64)
}
}
pub const fn timestamp_millis(self) -> i64 {
self.0
}
pub fn to_rfc3339_string(self) -> String {
self.to_chrono()
.to_rfc3339_opts(chrono::SecondsFormat::AutoSi, true)
}
pub fn parse_rfc3339_str(s: impl AsRef<str>) -> Result<Self> {
let date = chrono::DateTime::<chrono::FixedOffset>::parse_from_rfc3339(s.as_ref())
.map_err(|e| Error::InvalidTimestamp {
message: e.to_string(),
})?;
Ok(Self::from_chrono(date))
}
}
impl fmt::Debug for crate::DateTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut tup = f.debug_tuple("DateTime");
match Utc.timestamp_millis_opt(self.0) {
LocalResult::Single(ref 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 Utc.timestamp_millis_opt(self.0) {
LocalResult::Single(ref 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")]
#[cfg_attr(docsrs, doc(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")]
#[cfg_attr(docsrs, doc(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(all(feature = "chrono-0_4", feature = "serde_with"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "chrono-0_4", feature = "serde_with"))))]
impl<'de> DeserializeAs<'de, chrono::DateTime<Utc>> for crate::DateTime {
fn deserialize_as<D>(deserializer: D) -> std::result::Result<chrono::DateTime<Utc>, D::Error>
where
D: Deserializer<'de>,
{
let dt = DateTime::deserialize(deserializer)?;
Ok(dt.to_chrono())
}
}
#[cfg(all(feature = "chrono-0_4", feature = "serde_with"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "chrono-0_4", feature = "chrono-0_4"))))]
impl SerializeAs<chrono::DateTime<Utc>> for crate::DateTime {
fn serialize_as<S>(
source: &chrono::DateTime<Utc>,
serializer: S,
) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let dt = DateTime::from_chrono(*source);
dt.serialize(serializer)
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum Error {
#[non_exhaustive]
InvalidTimestamp { message: String },
}
pub type Result<T> = result::Result<T, Error>;
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::InvalidTimestamp { message } => {
write!(fmt, "{}", message)
}
}
}
}
impl error::Error for Error {}