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)]
59pub struct DateTime(String);
60
61impl From<DateTime> for Value<'_> {
62 fn from(value: DateTime) -> Self {
63 Self::tag(tag::DATE_TIME, value.0)
64 }
65}
66
67impl<'a> From<&'a DateTime> for Value<'a> {
73 fn from(value: &'a DateTime) -> Self {
74 Self::tag(tag::DATE_TIME, value.0.as_str())
75 }
76}
77
78impl TryFrom<SystemTime> for DateTime {
79 type Error = Error;
80
81 fn try_from(value: SystemTime) -> Result<Self> {
82 if let Ok(time) = value.duration_since(SystemTime::UNIX_EPOCH)
83 && time > Duration::from_secs(253402300799)
84 {
85 return Err(Error::InvalidValue);
86 }
87
88 let ts = Timestamp::try_new(value)?;
89 Ok(Self(ts.to_string()))
90 }
91}
92
93fn validate_date_time(s: &str) -> Result<()> {
94 let ts: Timestamp = s.parse()?;
95
96 if ts.year > 9999 {
97 return Err(Error::InvalidValue);
98 }
99
100 if ts.second == 60 {
101 if ts.hour != 23 || ts.minute != 59 {
102 return Err(Error::InvalidValue);
103 }
104
105 if !LEAP_SECOND_DATES.contains(&(ts.year, ts.month, ts.day)) {
106 return Err(Error::InvalidValue);
107 }
108 }
109
110 if ts.year < 9999 || ts.month < 12 || ts.day < 31 || ts.hour < 23 || ts.minute < 59 || ts.second < 59 {
111 Ok(())
112 } else if ts.second > 59 || (ts.nano_seconds > 0 && ts.offset == 0) || ts.offset < 0 {
113 Err(Error::InvalidValue)
114 } else {
115 Ok(())
116 }
117}
118
119impl TryFrom<&str> for DateTime {
120 type Error = Error;
121
122 fn try_from(value: &str) -> Result<Self> {
123 validate_date_time(value)?;
124 Ok(Self(value.to_string()))
125 }
126}
127
128impl TryFrom<String> for DateTime {
129 type Error = Error;
130
131 fn try_from(value: String) -> Result<Self> {
132 validate_date_time(&value)?;
133 Ok(Self(value))
134 }
135}
136
137impl TryFrom<&String> for DateTime {
138 type Error = Error;
139
140 fn try_from(value: &String) -> Result<Self> {
141 validate_date_time(value)?;
142 Ok(Self(value.clone()))
143 }
144}
145
146impl TryFrom<Box<str>> for DateTime {
147 type Error = Error;
148
149 fn try_from(value: Box<str>) -> Result<Self> {
150 validate_date_time(value.as_ref())?;
151 Ok(Self(value.to_string()))
152 }
153}