1use crate::error::{Error, ErrorKind, Syntax};
4use crate::model::admit;
5use crate::source::Location;
6use structured_asn1::memory::Work;
7use structured_pkix::{temporal::Instant, time::Calendar};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub struct Date {
12 calendar: Calendar,
13 offset: i16,
14 components: u8,
15 explicit_zone: bool,
16 instant: Instant,
17}
18
19impl Date {
20 #[must_use]
22 pub fn calendar(self) -> Calendar {
23 self.calendar
24 }
25 #[must_use]
27 pub fn offset_minutes(self) -> i16 {
28 self.offset
29 }
30 #[must_use]
32 pub fn components(self) -> u8 {
33 self.components
34 }
35 #[must_use]
37 pub fn has_explicit_zone(self) -> bool {
38 self.explicit_zone
39 }
40 #[must_use]
42 pub fn instant(self) -> Instant {
43 self.instant
44 }
45
46 pub fn read(bytes: &[u8], location: Location, work: &impl Work) -> crate::Result<Self> {
51 admit(work, location, bytes.len() as u64 + 1)?;
52 let malformed = || Error {
53 kind: ErrorKind::Syntax(Syntax::String),
54 location,
55 };
56 if !bytes.starts_with(b"D:") || bytes.len() < 6 || bytes.len() > 22 {
57 return Err(malformed());
58 }
59 let mut at = 2;
60 let mut year = 0_i64;
61 for _ in 0..4 {
62 let byte = bytes[at];
63 if !byte.is_ascii_digit() {
64 return Err(malformed());
65 }
66 year = year * 10 + i64::from(byte - b'0');
67 at += 1;
68 }
69 let mut values = [1, 1, 0, 0, 0];
70 let mut components = 1_u8;
71 for value in &mut values {
72 if at == bytes.len() || !bytes[at].is_ascii_digit() {
73 break;
74 }
75 if at + 2 > bytes.len() || !bytes[at + 1].is_ascii_digit() {
76 return Err(malformed());
77 }
78 *value = (bytes[at] - b'0') * 10 + bytes[at + 1] - b'0';
79 components += 1;
80 at += 2;
81 }
82 let explicit_zone = at != bytes.len();
83 let mut offset = 0_i16;
84 if explicit_zone {
85 if components != 6 {
86 return Err(malformed());
87 }
88 let sign = bytes[at];
89 at += 1;
90 match sign {
91 b'Z' => {}
92 b'+' | b'-' => {
93 let mut hours = 0_u8;
94 let mut minutes = 0_u8;
95 if at != bytes.len() {
96 hours = pair(bytes, &mut at).ok_or_else(malformed)?;
97 if at != bytes.len() {
98 if bytes[at] != b'\'' {
99 return Err(malformed());
100 }
101 at += 1;
102 if at != bytes.len() {
103 minutes = pair(bytes, &mut at).ok_or_else(malformed)?;
104 }
105 }
106 }
107 if hours > 23 || minutes > 59 {
108 return Err(malformed());
109 }
110 offset = i16::from(hours) * 60 + i16::from(minutes);
111 if sign == b'-' {
112 offset = -offset;
113 }
114 }
115 _ => return Err(malformed()),
116 }
117 }
118 if at != bytes.len() || values[4] > 59 {
119 return Err(malformed());
120 }
121 let calendar = Calendar {
122 year,
123 month: values[0],
124 day: values[1],
125 hour: values[2],
126 minute: values[3],
127 second: values[4],
128 nanos: 0,
129 };
130 let instant = calendar
131 .instant_at_minutes(offset, work)
132 .map_err(|cause| Error {
133 kind: ErrorKind::Resource(cause),
134 location,
135 })?
136 .ok_or_else(malformed)?;
137 Ok(Self {
138 calendar,
139 offset,
140 components,
141 explicit_zone,
142 instant,
143 })
144 }
145}
146
147fn pair(bytes: &[u8], at: &mut usize) -> Option<u8> {
148 if bytes.len() - *at < 2 {
149 return None;
150 }
151 let first = bytes[*at];
152 let second = bytes[*at + 1];
153 if !first.is_ascii_digit() || !second.is_ascii_digit() {
154 return None;
155 }
156 *at += 2;
157 Some((first - b'0') * 10 + second - b'0')
158}