pub use chrono::Utc;
use serde::{de, Serialize};
use std::{fmt, ops::Deref, str::FromStr};
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct DateTime<Tz: chrono::offset::TimeZone>(chrono::DateTime<Tz>);
impl<Tz: chrono::offset::TimeZone> Deref for DateTime<Tz> {
type Target = chrono::DateTime<Tz>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<Tz: chrono::offset::TimeZone> fmt::Display for DateTime<Tz>
where
Tz::Offset: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl<Tz: chrono::offset::TimeZone> fmt::Debug for DateTime<Tz>
where
Tz::Offset: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
struct DateTimeVisitor;
impl<'de> de::Visitor<'de> for DateTimeVisitor {
type Value = DateTime<Utc>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a formatted date or date and time string")
}
fn visit_str<E>(self, value: &str) -> Result<DateTime<Utc>, E>
where
E: de::Error,
{
parse_date(value).map_err(|err| E::custom(format!("{}", err)))
}
}
impl<'de> de::Deserialize<'de> for DateTime<Utc> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
deserializer.deserialize_str(DateTimeVisitor)
}
}
impl Serialize for DateTime<Utc> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.0.serialize(serializer)
}
}
fn parse_date(s: &str) -> Result<DateTime<Utc>, chrono::ParseError> {
let dt: chrono::DateTime<Utc> = if s.len() == 10 {
let nd = chrono::NaiveDate::parse_from_str(s, "%Y-%m-%d")?;
chrono::DateTime::from_utc(nd.and_hms(0, 0, 0), Utc)
} else {
s.parse::<chrono::DateTime<chrono::FixedOffset>>()
.map(|dt| dt.with_timezone(&Utc))?
};
Ok(DateTime(dt))
}
impl FromStr for DateTime<Utc> {
type Err = chrono::ParseError;
fn from_str(s: &str) -> chrono::ParseResult<DateTime<Utc>> {
parse_date(s)
}
}
impl From<chrono::DateTime<Utc>> for DateTime<Utc> {
fn from(d: chrono::DateTime<Utc>) -> Self {
DateTime(d)
}
}
#[test]
fn test_datetime_ser_deser() {
#[derive(Debug, Serialize, serde::Deserialize)]
struct DateStruct {
date: DateTime<Utc>,
}
let val: DateStruct = serde_json::from_str(r#"{ "date": "2020-01-01" }"#).unwrap();
assert_eq!(
format!("date: {:?}", val.date),
"date: 2020-01-01 00:00:00 UTC"
);
let json = serde_json::to_string(&val).unwrap();
assert_eq!(json, r#"{"date":"2020-01-01T00:00:00Z"}"#);
}
#[test]
fn test_datetime_debug() {
let dt = "2020-01-01T01:02:03Z".parse::<DateTime<Utc>>().unwrap();
assert_eq!(format!("{:?}", dt), "2020-01-01 01:02:03 UTC");
}
#[test]
fn test_datetime_display() {
let dt = "2020-01-01T01:02:03Z".parse::<DateTime<Utc>>().unwrap();
assert_eq!(format!("{}", dt), "2020-01-01 01:02:03 UTC");
}
#[test]
fn test_datetime_parse_iso8601() {
let dt = "2020-01-01T01:02:03Z".parse::<DateTime<Utc>>().unwrap();
assert_eq!(format!("{}", dt), "2020-01-01 01:02:03 UTC");
assert_eq!(format!("{}", dt.0), "2020-01-01 01:02:03 UTC");
}
#[test]
fn test_datetime_parse_short() {
let dt = "2020-01-01".parse::<DateTime<Utc>>().unwrap();
assert_eq!(format!("{}", dt), "2020-01-01 00:00:00 UTC");
assert_eq!(format!("{}", dt.0), "2020-01-01 00:00:00 UTC");
}