1use crate::apdu::UnconfirmedRequestHeader;
2use crate::encoding::{
3 reader::Reader,
4 tag::{AppTag, Tag},
5 writer::Writer,
6};
7use crate::types::{Date, Time};
8use crate::{DecodeError, EncodeError};
9
10pub const SERVICE_TIME_SYNCHRONIZATION: u8 = 0x06;
11pub const SERVICE_UTC_TIME_SYNCHRONIZATION: u8 = 0x09;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub struct TimeSynchronizationRequest {
15 pub date: Date,
16 pub time: Time,
17 pub utc: bool,
18}
19
20impl TimeSynchronizationRequest {
21 pub const fn local(date: Date, time: Time) -> Self {
22 Self {
23 date,
24 time,
25 utc: false,
26 }
27 }
28
29 pub const fn utc(date: Date, time: Time) -> Self {
30 Self {
31 date,
32 time,
33 utc: true,
34 }
35 }
36
37 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
38 let service_choice = if self.utc {
39 SERVICE_UTC_TIME_SYNCHRONIZATION
40 } else {
41 SERVICE_TIME_SYNCHRONIZATION
42 };
43 UnconfirmedRequestHeader { service_choice }.encode(w)?;
44 Tag::Application {
45 tag: AppTag::Date,
46 len: 4,
47 }
48 .encode(w)?;
49 w.write_all(&[
50 self.date.year_since_1900,
51 self.date.month,
52 self.date.day,
53 self.date.weekday,
54 ])?;
55 Tag::Application {
56 tag: AppTag::Time,
57 len: 4,
58 }
59 .encode(w)?;
60 w.write_all(&[
61 self.time.hour,
62 self.time.minute,
63 self.time.second,
64 self.time.hundredths,
65 ])?;
66 Ok(())
67 }
68
69 pub fn decode_after_header(r: &mut Reader<'_>) -> Result<Self, DecodeError> {
70 let date = match Tag::decode(r)? {
71 Tag::Application {
72 tag: AppTag::Date,
73 len: 4,
74 } => {
75 let bytes = r.read_exact(4)?;
76 Date {
77 year_since_1900: bytes[0],
78 month: bytes[1],
79 day: bytes[2],
80 weekday: bytes[3],
81 }
82 }
83 _ => return Err(DecodeError::InvalidTag),
84 };
85 let time = match Tag::decode(r)? {
86 Tag::Application {
87 tag: AppTag::Time,
88 len: 4,
89 } => {
90 let bytes = r.read_exact(4)?;
91 Time {
92 hour: bytes[0],
93 minute: bytes[1],
94 second: bytes[2],
95 hundredths: bytes[3],
96 }
97 }
98 _ => return Err(DecodeError::InvalidTag),
99 };
100 Ok(Self {
101 date,
102 time,
103 utc: false,
104 })
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::{
111 TimeSynchronizationRequest, SERVICE_TIME_SYNCHRONIZATION, SERVICE_UTC_TIME_SYNCHRONIZATION,
112 };
113 use crate::apdu::UnconfirmedRequestHeader;
114 use crate::encoding::{reader::Reader, writer::Writer};
115 use crate::types::{Date, Time};
116
117 #[test]
118 fn encode_local_time_sync_request() {
119 let req = TimeSynchronizationRequest::local(
120 Date {
121 year_since_1900: 126,
122 month: 2,
123 day: 7,
124 weekday: 6,
125 },
126 Time {
127 hour: 10,
128 minute: 11,
129 second: 12,
130 hundredths: 13,
131 },
132 );
133 let mut buf = [0u8; 64];
134 let mut w = Writer::new(&mut buf);
135 req.encode(&mut w).unwrap();
136 let mut r = Reader::new(w.as_written());
137 let hdr = UnconfirmedRequestHeader::decode(&mut r).unwrap();
138 assert_eq!(hdr.service_choice, SERVICE_TIME_SYNCHRONIZATION);
139 }
140
141 #[test]
142 fn encode_utc_time_sync_request() {
143 let req = TimeSynchronizationRequest::utc(
144 Date {
145 year_since_1900: 126,
146 month: 2,
147 day: 7,
148 weekday: 6,
149 },
150 Time {
151 hour: 14,
152 minute: 15,
153 second: 16,
154 hundredths: 17,
155 },
156 );
157 let mut buf = [0u8; 64];
158 let mut w = Writer::new(&mut buf);
159 req.encode(&mut w).unwrap();
160 let mut r = Reader::new(w.as_written());
161 let hdr = UnconfirmedRequestHeader::decode(&mut r).unwrap();
162 assert_eq!(hdr.service_choice, SERVICE_UTC_TIME_SYNCHRONIZATION);
163 }
164
165 #[test]
166 fn decode_local_time_sync_after_header() {
167 let req = TimeSynchronizationRequest::local(
168 Date {
169 year_since_1900: 126,
170 month: 2,
171 day: 7,
172 weekday: 6,
173 },
174 Time {
175 hour: 10,
176 minute: 11,
177 second: 12,
178 hundredths: 13,
179 },
180 );
181 let mut buf = [0u8; 64];
182 let mut w = Writer::new(&mut buf);
183 req.encode(&mut w).unwrap();
184 let mut r = Reader::new(w.as_written());
185 let _hdr = UnconfirmedRequestHeader::decode(&mut r).unwrap();
186 let decoded = TimeSynchronizationRequest::decode_after_header(&mut r).unwrap();
187 assert_eq!(decoded.date, req.date);
188 assert_eq!(decoded.time, req.time);
189 }
190}