use serde::de;
use std::fmt;
pub type DateTime = chrono::DateTime<chrono::offset::Utc>;
pub fn serialize<S>(dt: &DateTime, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use chrono::{Datelike, Timelike};
let s = format!(
"{:04}{:02}{:02}T{:02}{:02}{:02}Z",
dt.year(),
dt.month(),
dt.day(),
dt.hour(),
dt.minute(),
dt.second()
);
serializer.serialize_str(&s)
}
struct DateTimeVisitor;
impl<'a> de::Visitor<'a> for DateTimeVisitor {
type Value = DateTime;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("An ISO 8601 date in the format YYYYMMDDTHHMMSSZ")
}
fn visit_str<E>(self, raw: &str) -> Result<DateTime, E>
where
E: de::Error,
{
if !raw.is_ascii() {
return Err(E::custom(format!(
"`{}` contains non ascii characters",
raw
)));
}
if raw.len() != 16 {
return Err(E::custom(format!("`{}` has the wrong length", raw)));
}
let (year, rest) = raw.split_at(4);
let (month, rest) = rest.split_at(2);
let (day, rest) = rest.split_at(2);
if !rest.starts_with('T') {
return Err(E::custom(format!("`{}` has the wrong format", raw)));
}
let rest = &rest[1..];
let (hour, rest) = rest.split_at(2);
let (minute, rest) = rest.split_at(2);
let (second, rest) = rest.split_at(2);
if rest != "Z" {
return Err(E::custom(format!("`{}` has the wrong format", raw)));
}
match (
year.parse(),
month.parse(),
day.parse(),
hour.parse(),
minute.parse(),
second.parse(),
) {
(Ok(year), Ok(month), Ok(day), Ok(hour), Ok(minute), Ok(second)) => {
let d = chrono::NaiveDate::from_ymd(year, month, day).and_hms(hour, minute, second);
let d = DateTime::from_utc(d, chrono::offset::Utc);
Ok(d)
}
_ => Err(E::custom(format!("`{}` has the wrong format", raw))),
}
}
}
pub fn deserialize<'a, D>(d: D) -> Result<DateTime, D::Error>
where
D: serde::Deserializer<'a>,
{
d.deserialize_str(DateTimeVisitor)
}
#[cfg(test)]
mod test {
use serde::Deserialize;
#[derive(Deserialize, Debug)]
struct Dummy {
#[serde(with = "super")]
a: DateTime,
}
use super::*;
#[test]
fn non_ascii_fails() {
let r = serde_json::from_str::<Dummy>("{\"a\": \"Überraschung\"}");
println!("{:?}", r);
assert!(r.is_err());
}
#[test]
fn wrong_length_fails() {
let r = serde_json::from_str::<Dummy>("{\"a\": \"334\"}");
assert!(r.is_err())
}
#[test]
fn wrong_format_fails() {
let r = serde_json::from_str::<Dummy>("{\"a\": \"1111111111111111\"}");
assert!(r.is_err())
}
#[test]
fn correct_format_succeeds() {
let r = serde_json::from_str::<Dummy>("{\"a\": \"20120110T231200Z\"}");
assert!(r.is_ok());
let d = r.unwrap().a;
assert_eq!(d.timestamp(), 1_326_237_120);
}
}