1use std::time::{Duration, SystemTime};
2
3use crate::iso3339::Timestamp;
4use crate::{Error, Result, Value, tag};
5
6const LEAP_SECOND_DATES: [(u16, u8, u8); 27] = [
7 (1972, 6, 30),
8 (1972, 12, 31),
9 (1973, 12, 31),
10 (1974, 12, 31),
11 (1975, 12, 31),
12 (1976, 12, 31),
13 (1977, 12, 31),
14 (1978, 12, 31),
15 (1979, 12, 31),
16 (1981, 6, 30),
17 (1982, 6, 30),
18 (1983, 6, 30),
19 (1985, 6, 30),
20 (1987, 12, 31),
21 (1989, 12, 31),
22 (1990, 12, 31),
23 (1992, 6, 30),
24 (1993, 6, 30),
25 (1994, 6, 30),
26 (1995, 12, 31),
27 (1997, 6, 30),
28 (1998, 12, 31),
29 (2005, 12, 31),
30 (2008, 12, 31),
31 (2012, 6, 30),
32 (2015, 6, 30),
33 (2016, 12, 31),
34];
35
36#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
58pub struct DateTime(String);
59
60impl From<DateTime> for Value<'_> {
61 fn from(value: DateTime) -> Self {
62 Self::tag(tag::DATE_TIME, value.0)
63 }
64}
65
66impl<'a> From<&'a DateTime> for Value<'a> {
72 fn from(value: &'a DateTime) -> Self {
73 Self::tag(tag::DATE_TIME, value.0.as_str())
74 }
75}
76
77impl TryFrom<SystemTime> for DateTime {
78 type Error = Error;
79
80 fn try_from(value: SystemTime) -> Result<Self> {
81 if let Ok(time) = value.duration_since(SystemTime::UNIX_EPOCH)
82 && time > Duration::from_secs(253402300799)
83 {
84 return Err(Error::InvalidValue);
85 }
86
87 let ts = Timestamp::try_new(value)?;
88 Ok(Self(ts.to_string()))
89 }
90}
91
92fn validate_date_time(s: &str) -> Result<()> {
93 let ts: Timestamp = s.parse()?;
94
95 if ts.year > 9999 {
96 return Err(Error::InvalidValue);
97 }
98
99 if ts.second == 60 {
100 if ts.hour != 23 || ts.minute != 59 {
101 return Err(Error::InvalidValue);
102 }
103
104 if !LEAP_SECOND_DATES.contains(&(ts.year, ts.month, ts.day)) {
105 return Err(Error::InvalidValue);
106 }
107 }
108
109 if ts.year < 9999 || ts.month < 12 || ts.day < 31 || ts.hour < 23 || ts.minute < 59 || ts.second < 59 {
110 Ok(())
111 } else if ts.second > 59 || (ts.nano_seconds > 0 && ts.offset == 0) || ts.offset < 0 {
112 Err(Error::InvalidValue)
113 } else {
114 Ok(())
115 }
116}
117
118impl TryFrom<&str> for DateTime {
119 type Error = Error;
120
121 fn try_from(value: &str) -> Result<Self> {
122 validate_date_time(value)?;
123 Ok(Self(value.to_string()))
124 }
125}
126
127impl TryFrom<String> for DateTime {
128 type Error = Error;
129
130 fn try_from(value: String) -> Result<Self> {
131 validate_date_time(&value)?;
132 Ok(Self(value))
133 }
134}
135
136impl TryFrom<&String> for DateTime {
137 type Error = Error;
138
139 fn try_from(value: &String) -> Result<Self> {
140 validate_date_time(value)?;
141 Ok(Self(value.clone()))
142 }
143}
144
145impl TryFrom<Box<str>> for DateTime {
146 type Error = Error;
147
148 fn try_from(value: Box<str>) -> Result<Self> {
149 validate_date_time(value.as_ref())?;
150 Ok(Self(value.to_string()))
151 }
152}