use std::time::{Duration, SystemTime};
use crate::iso3339::Timestamp;
use crate::{Error, Result, Tag, Value};
const LEAP_SECOND_DATES: [(u32, u8, u8); 27] = [
(1972, 6, 30),
(1972, 12, 31),
(1973, 12, 31),
(1974, 12, 31),
(1975, 12, 31),
(1976, 12, 31),
(1977, 12, 31),
(1978, 12, 31),
(1979, 12, 31),
(1981, 6, 30),
(1982, 6, 30),
(1983, 6, 30),
(1985, 6, 30),
(1987, 12, 31),
(1989, 12, 31),
(1990, 12, 31),
(1992, 6, 30),
(1993, 6, 30),
(1994, 6, 30),
(1995, 12, 31),
(1997, 6, 30),
(1998, 12, 31),
(2005, 12, 31),
(2008, 12, 31),
(2012, 6, 30),
(2015, 6, 30),
(2016, 12, 31),
];
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DateTime(String);
impl From<DateTime> for Value {
fn from(value: DateTime) -> Self {
Self::tag(Tag::DATE_TIME, value.0)
}
}
impl TryFrom<SystemTime> for DateTime {
type Error = Error;
fn try_from(value: SystemTime) -> Result<Self> {
if let Ok(time) = value.duration_since(SystemTime::UNIX_EPOCH)
&& time > Duration::from_secs(253402300799)
{
return Err(Error::Overflow);
}
let ts = Timestamp::try_new(value).or(Err(Error::Overflow))?;
Ok(Self(ts.to_string()))
}
}
fn validate_date_time(s: &str) -> Result<()> {
let ts: Timestamp = s.parse().or(Err(Error::InvalidEncoding))?;
if ts.year > 9999 {
return Err(Error::Overflow);
}
if ts.second == 60 {
if ts.hour != 23 || ts.minute != 59 {
return Err(Error::InvalidEncoding);
}
if !LEAP_SECOND_DATES.contains(&(ts.year, ts.month, ts.day)) {
return Err(Error::InvalidEncoding);
}
}
if ts.year < 9999 || ts.month < 12 || ts.day < 31 || ts.hour < 23 || ts.minute < 59 || ts.second < 59 {
Ok(())
} else if ts.second > 59 || (ts.nano_seconds > 0 && ts.offset == 0) || ts.offset < 0 {
Err(Error::Overflow)
} else {
Ok(())
}
}
impl TryFrom<&str> for DateTime {
type Error = Error;
fn try_from(value: &str) -> Result<Self> {
validate_date_time(value)?;
Ok(Self(value.to_string()))
}
}
impl TryFrom<String> for DateTime {
type Error = Error;
fn try_from(value: String) -> Result<Self> {
validate_date_time(&value)?;
Ok(Self(value))
}
}
impl TryFrom<&String> for DateTime {
type Error = Error;
fn try_from(value: &String) -> Result<Self> {
validate_date_time(value)?;
Ok(Self(value.clone()))
}
}
impl TryFrom<Box<str>> for DateTime {
type Error = Error;
fn try_from(value: Box<str>) -> Result<Self> {
validate_date_time(value.as_ref())?;
Ok(Self(value.to_string()))
}
}