1use crate::{
4 asn1::AnyRef,
5 datetime::{self, DateTime},
6 ord::OrdIsValueOrd,
7 DecodeValue, EncodeValue, Error, ErrorKind, FixedTag, Header, Length, Reader, Result, Tag,
8 Writer,
9};
10use core::time::Duration;
11
12#[cfg(feature = "std")]
13use std::time::SystemTime;
14
15pub const MAX_YEAR: u16 = 2049;
17
18#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
34pub struct UtcTime(DateTime);
35
36impl UtcTime {
37 pub const LENGTH: usize = 13;
39
40 pub fn from_date_time(datetime: DateTime) -> Result<Self> {
42 if datetime.year() <= MAX_YEAR {
43 Ok(Self(datetime))
44 } else {
45 Err(Self::TAG.value_error())
46 }
47 }
48
49 pub fn to_date_time(&self) -> DateTime {
51 self.0
52 }
53
54 pub fn from_unix_duration(unix_duration: Duration) -> Result<Self> {
57 DateTime::from_unix_duration(unix_duration)?.try_into()
58 }
59
60 pub fn to_unix_duration(&self) -> Duration {
62 self.0.unix_duration()
63 }
64
65 #[cfg(feature = "std")]
67 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
68 pub fn from_system_time(time: SystemTime) -> Result<Self> {
69 DateTime::try_from(time)
70 .map_err(|_| Self::TAG.value_error())?
71 .try_into()
72 }
73
74 #[cfg(feature = "std")]
76 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
77 pub fn to_system_time(&self) -> SystemTime {
78 self.0.to_system_time()
79 }
80}
81
82impl<'a> DecodeValue<'a> for UtcTime {
83 fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
84 if Self::LENGTH != usize::try_from(header.length)? {
85 return Err(Self::TAG.value_error());
86 }
87
88 let mut bytes = [0u8; Self::LENGTH];
89 reader.read_into(&mut bytes)?;
90
91 match bytes {
92 [year1, year2, mon1, mon2, day1, day2, hour1, hour2, min1, min2, sec1, sec2, b'Z'] => {
94 let year = u16::from(datetime::decode_decimal(Self::TAG, year1, year2)?);
95 let month = datetime::decode_decimal(Self::TAG, mon1, mon2)?;
96 let day = datetime::decode_decimal(Self::TAG, day1, day2)?;
97 let hour = datetime::decode_decimal(Self::TAG, hour1, hour2)?;
98 let minute = datetime::decode_decimal(Self::TAG, min1, min2)?;
99 let second = datetime::decode_decimal(Self::TAG, sec1, sec2)?;
100
101 let year = if year >= 50 {
103 year.checked_add(1900)
104 } else {
105 year.checked_add(2000)
106 }
107 .ok_or(ErrorKind::DateTime)?;
108
109 DateTime::new(year, month, day, hour, minute, second)
110 .map_err(|_| Self::TAG.value_error())
111 .and_then(|dt| Self::from_unix_duration(dt.unix_duration()))
112 }
113 _ => Err(Self::TAG.value_error()),
114 }
115 }
116}
117
118impl EncodeValue for UtcTime {
119 fn value_len(&self) -> Result<Length> {
120 Self::LENGTH.try_into()
121 }
122
123 fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
124 let year = match self.0.year() {
125 y @ 1950..=1999 => y.checked_sub(1900),
126 y @ 2000..=2049 => y.checked_sub(2000),
127 _ => return Err(Self::TAG.value_error()),
128 }
129 .and_then(|y| u8::try_from(y).ok())
130 .ok_or(ErrorKind::DateTime)?;
131
132 datetime::encode_decimal(writer, Self::TAG, year)?;
133 datetime::encode_decimal(writer, Self::TAG, self.0.month())?;
134 datetime::encode_decimal(writer, Self::TAG, self.0.day())?;
135 datetime::encode_decimal(writer, Self::TAG, self.0.hour())?;
136 datetime::encode_decimal(writer, Self::TAG, self.0.minutes())?;
137 datetime::encode_decimal(writer, Self::TAG, self.0.seconds())?;
138 writer.write_byte(b'Z')
139 }
140}
141
142impl FixedTag for UtcTime {
143 const TAG: Tag = Tag::UtcTime;
144}
145
146impl OrdIsValueOrd for UtcTime {}
147
148impl From<&UtcTime> for UtcTime {
149 fn from(value: &UtcTime) -> UtcTime {
150 *value
151 }
152}
153
154impl From<UtcTime> for DateTime {
155 fn from(utc_time: UtcTime) -> DateTime {
156 utc_time.0
157 }
158}
159
160impl From<&UtcTime> for DateTime {
161 fn from(utc_time: &UtcTime) -> DateTime {
162 utc_time.0
163 }
164}
165
166impl TryFrom<DateTime> for UtcTime {
167 type Error = Error;
168
169 fn try_from(datetime: DateTime) -> Result<Self> {
170 Self::from_date_time(datetime)
171 }
172}
173
174impl TryFrom<&DateTime> for UtcTime {
175 type Error = Error;
176
177 fn try_from(datetime: &DateTime) -> Result<Self> {
178 Self::from_date_time(*datetime)
179 }
180}
181
182#[cfg(feature = "std")]
183#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
184impl From<UtcTime> for SystemTime {
185 fn from(utc_time: UtcTime) -> SystemTime {
186 utc_time.to_system_time()
187 }
188}
189
190impl TryFrom<AnyRef<'_>> for UtcTime {
191 type Error = Error;
192
193 fn try_from(any: AnyRef<'_>) -> Result<UtcTime> {
194 any.decode_into()
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use super::UtcTime;
201 use crate::{Decode, Encode, SliceWriter};
202 use hex_literal::hex;
203
204 #[test]
205 fn round_trip_vector() {
206 let example_bytes = hex!("17 0d 39 31 30 35 30 36 32 33 34 35 34 30 5a");
207 let utc_time = UtcTime::from_der(&example_bytes).unwrap();
208 assert_eq!(utc_time.to_unix_duration().as_secs(), 673573540);
209
210 let mut buf = [0u8; 128];
211 let mut encoder = SliceWriter::new(&mut buf);
212 utc_time.encode(&mut encoder).unwrap();
213 assert_eq!(example_bytes, encoder.finish().unwrap());
214 }
215}