use std::{
convert::TryInto,
error,
fmt::{self, Display},
result,
time::{Duration, SystemTime},
};
pub(crate) mod builder;
pub use crate::datetime::builder::DateTimeBuilder;
use time::format_description::well_known::Rfc3339;
#[cfg(feature = "chrono-0_4")]
use chrono::{LocalResult, TimeZone, Utc};
#[cfg(all(
feature = "serde_with",
any(feature = "chrono-0_4", feature = "time-0_3")
))]
use serde::{Deserialize, Deserializer, Serialize};
#[cfg(all(
feature = "serde_with",
any(feature = "chrono-0_4", feature = "time-0_3")
))]
use serde_with::{DeserializeAs, SerializeAs};
#[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")]
#[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())
}
pub fn builder() -> DateTimeBuilder {
DateTimeBuilder::default()
}
#[cfg(feature = "chrono-0_4")]
#[cfg_attr(docsrs, doc(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
}
}
}
}
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")]
#[cfg_attr(docsrs, doc(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
}
#[deprecated(since = "2.3.0", note = "Use try_to_rfc3339_string instead.")]
pub fn to_rfc3339_string(self) -> String {
self.try_to_rfc3339_string().unwrap()
}
pub fn try_to_rfc3339_string(self) -> Result<String> {
self.to_time_0_3()
.format(&Rfc3339)
.map_err(|e| Error::CannotFormat {
message: e.to_string(),
})
}
pub fn parse_rfc3339_str(s: impl AsRef<str>) -> Result<Self> {
let odt = time::OffsetDateTime::parse(s.as_ref(), &Rfc3339).map_err(|e| {
Error::InvalidTimestamp {
message: e.to_string(),
}
})?;
Ok(Self::from_time_0_3(odt))
}
}
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")]
#[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)
}
}
#[cfg(feature = "time-0_3")]
#[cfg_attr(docsrs, doc(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")]
#[cfg_attr(docsrs, doc(cfg(feature = "time-0_3")))]
impl From<time::OffsetDateTime> for crate::DateTime {
fn from(x: time::OffsetDateTime) -> Self {
Self::from_time_0_3(x)
}
}
#[cfg(all(feature = "time-0_3", feature = "serde_with"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "time-0_3", feature = "serde_with"))))]
impl<'de> DeserializeAs<'de, time::OffsetDateTime> for crate::DateTime {
fn deserialize_as<D>(deserializer: D) -> std::result::Result<time::OffsetDateTime, D::Error>
where
D: Deserializer<'de>,
{
let dt = DateTime::deserialize(deserializer)?;
Ok(dt.to_time_0_3())
}
}
#[cfg(all(feature = "time-0_3", feature = "serde_with"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "time-0_3", feature = "chrono-0_4"))))]
impl SerializeAs<time::OffsetDateTime> for crate::DateTime {
fn serialize_as<S>(
source: &time::OffsetDateTime,
serializer: S,
) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let dt = DateTime::from_time_0_3(*source);
dt.serialize(serializer)
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum Error {
#[non_exhaustive]
InvalidTimestamp { message: String },
#[non_exhaustive]
CannotFormat { 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 } | Error::CannotFormat { message } => {
write!(fmt, "{}", message)
}
}
}
}
impl error::Error for Error {}