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