der/asn1/
utc_time.rs
1use crate::{
4 datetime::{self, DateTime},
5 ord::OrdIsValueOrd,
6 DecodeValue, EncodeValue, Error, ErrorKind, FixedTag, Header, Length, Reader, Result, Tag,
7 Writer,
8};
9use core::time::Duration;
10
11#[cfg(feature = "std")]
12use std::time::SystemTime;
13
14#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
33pub struct UtcTime(DateTime);
34
35impl UtcTime {
36 pub const LENGTH: usize = 13;
38
39 pub const MAX_YEAR: u16 = 2049;
41
42 pub fn from_date_time(datetime: DateTime) -> Result<Self> {
44 if datetime.year() <= UtcTime::MAX_YEAR {
45 Ok(Self(datetime))
46 } else {
47 Err(Self::TAG.value_error())
48 }
49 }
50
51 pub fn to_date_time(&self) -> DateTime {
53 self.0
54 }
55
56 pub fn from_unix_duration(unix_duration: Duration) -> Result<Self> {
59 DateTime::from_unix_duration(unix_duration)?.try_into()
60 }
61
62 pub fn to_unix_duration(&self) -> Duration {
64 self.0.unix_duration()
65 }
66
67 #[cfg(feature = "std")]
69 pub fn from_system_time(time: SystemTime) -> Result<Self> {
70 DateTime::try_from(time)
71 .map_err(|_| Self::TAG.value_error())?
72 .try_into()
73 }
74
75 #[cfg(feature = "std")]
77 pub fn to_system_time(&self) -> SystemTime {
78 self.0.to_system_time()
79 }
80}
81
82impl_any_conversions!(UtcTime);
83
84impl<'a> DecodeValue<'a> for UtcTime {
85 fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
86 if Self::LENGTH != usize::try_from(header.length)? {
87 return Err(Self::TAG.value_error());
88 }
89
90 let mut bytes = [0u8; Self::LENGTH];
91 reader.read_into(&mut bytes)?;
92
93 match bytes {
94 [year1, year2, mon1, mon2, day1, day2, hour1, hour2, min1, min2, sec1, sec2, b'Z'] => {
96 let year = u16::from(datetime::decode_decimal(Self::TAG, year1, year2)?);
97 let month = datetime::decode_decimal(Self::TAG, mon1, mon2)?;
98 let day = datetime::decode_decimal(Self::TAG, day1, day2)?;
99 let hour = datetime::decode_decimal(Self::TAG, hour1, hour2)?;
100 let minute = datetime::decode_decimal(Self::TAG, min1, min2)?;
101 let second = datetime::decode_decimal(Self::TAG, sec1, sec2)?;
102
103 let year = if year >= 50 {
105 year.checked_add(1900)
106 } else {
107 year.checked_add(2000)
108 }
109 .ok_or(ErrorKind::DateTime)?;
110
111 DateTime::new(year, month, day, hour, minute, second)
112 .map_err(|_| Self::TAG.value_error())
113 .and_then(|dt| Self::from_unix_duration(dt.unix_duration()))
114 }
115 _ => Err(Self::TAG.value_error()),
116 }
117 }
118}
119
120impl EncodeValue for UtcTime {
121 fn value_len(&self) -> Result<Length> {
122 Self::LENGTH.try_into()
123 }
124
125 fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
126 let year = match self.0.year() {
127 y @ 1950..=1999 => y.checked_sub(1900),
128 y @ 2000..=2049 => y.checked_sub(2000),
129 _ => return Err(Self::TAG.value_error()),
130 }
131 .and_then(|y| u8::try_from(y).ok())
132 .ok_or(ErrorKind::DateTime)?;
133
134 datetime::encode_decimal(writer, Self::TAG, year)?;
135 datetime::encode_decimal(writer, Self::TAG, self.0.month())?;
136 datetime::encode_decimal(writer, Self::TAG, self.0.day())?;
137 datetime::encode_decimal(writer, Self::TAG, self.0.hour())?;
138 datetime::encode_decimal(writer, Self::TAG, self.0.minutes())?;
139 datetime::encode_decimal(writer, Self::TAG, self.0.seconds())?;
140 writer.write_byte(b'Z')
141 }
142}
143
144impl FixedTag for UtcTime {
145 const TAG: Tag = Tag::UtcTime;
146}
147
148impl OrdIsValueOrd for UtcTime {}
149
150impl From<&UtcTime> for UtcTime {
151 fn from(value: &UtcTime) -> UtcTime {
152 *value
153 }
154}
155
156impl From<UtcTime> for DateTime {
157 fn from(utc_time: UtcTime) -> DateTime {
158 utc_time.0
159 }
160}
161
162impl From<&UtcTime> for DateTime {
163 fn from(utc_time: &UtcTime) -> DateTime {
164 utc_time.0
165 }
166}
167
168impl TryFrom<DateTime> for UtcTime {
169 type Error = Error;
170
171 fn try_from(datetime: DateTime) -> Result<Self> {
172 Self::from_date_time(datetime)
173 }
174}
175
176impl TryFrom<&DateTime> for UtcTime {
177 type Error = Error;
178
179 fn try_from(datetime: &DateTime) -> Result<Self> {
180 Self::from_date_time(*datetime)
181 }
182}
183
184#[cfg(feature = "std")]
185impl From<UtcTime> for SystemTime {
186 fn from(utc_time: UtcTime) -> SystemTime {
187 utc_time.to_system_time()
188 }
189}
190
191#[cfg(feature = "arbitrary")]
196impl<'a> arbitrary::Arbitrary<'a> for UtcTime {
197 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
198 const MIN_YEAR: u16 = 1970;
199 const VALID_YEAR_COUNT: u16 = UtcTime::MAX_YEAR - MIN_YEAR + 1;
200 const AVERAGE_SECONDS_IN_YEAR: u64 = 31_556_952;
201
202 let datetime = DateTime::arbitrary(u)?;
203 let year = datetime.year();
204 let duration = datetime.unix_duration();
205
206 let valid_year = (year.saturating_sub(MIN_YEAR))
208 .rem_euclid(VALID_YEAR_COUNT)
209 .saturating_add(MIN_YEAR);
210 let year_to_remove = year.saturating_sub(valid_year);
211 let valid_duration = duration
212 - Duration::from_secs(
213 u64::from(year_to_remove).saturating_mul(AVERAGE_SECONDS_IN_YEAR),
214 );
215
216 Self::from_date_time(DateTime::from_unix_duration(valid_duration).expect("supported range"))
217 .map_err(|_| arbitrary::Error::IncorrectFormat)
218 }
219
220 fn size_hint(depth: usize) -> (usize, Option<usize>) {
221 DateTime::size_hint(depth)
222 }
223}
224
225#[cfg(test)]
226mod tests {
227 use super::UtcTime;
228 use crate::{Decode, Encode, SliceWriter};
229 use hex_literal::hex;
230
231 #[test]
232 fn round_trip_vector() {
233 let example_bytes = hex!("17 0d 39 31 30 35 30 36 32 33 34 35 34 30 5a");
234 let utc_time = UtcTime::from_der(&example_bytes).unwrap();
235 assert_eq!(utc_time.to_unix_duration().as_secs(), 673573540);
236
237 let mut buf = [0u8; 128];
238 let mut encoder = SliceWriter::new(&mut buf);
239 utc_time.encode(&mut encoder).unwrap();
240 assert_eq!(example_bytes, encoder.finish().unwrap());
241 }
242}