1use jiff::{
32 civil::{date, Date, DateTime, Time},
33 Zoned,
34};
35
36use super::{DateTimeParseError, TimeStamp as HL7TimeStamp, Time as HL7Time, Date as HL7Date};
37
38impl TryFrom<HL7TimeStamp> for Date {
39 type Error = DateTimeParseError;
40
41 fn try_from(value: HL7TimeStamp) -> Result<Self, Self::Error> {
42 let HL7TimeStamp {
43 year, month, day, ..
44 } = value;
45
46 let month = month.unwrap_or(1);
47 let day = day.unwrap_or(1);
48
49 Ok(date(year as i16, month as i8, day as i8))
50 }
51}
52
53impl From<Date> for HL7TimeStamp {
54 fn from(value: Date) -> Self {
55 let year = value.year();
56 let month = value.month();
57 let day = value.day();
58
59 HL7TimeStamp {
60 year: year as u16,
61 month: Some(month as u8),
62 day: Some(day as u8),
63 ..Default::default()
64 }
65 }
66}
67
68impl TryFrom<HL7Date> for Date {
69 type Error = DateTimeParseError;
70
71 fn try_from(value: super::Date) -> Result<Self, Self::Error> {
72 let super::Date { year, month, day } = value;
73
74 let month = month.unwrap_or(1);
75 let day = day.unwrap_or(1);
76
77 Ok(date(year as i16, month as i8, day as i8))
78 }
79}
80
81impl From<Date> for HL7Date {
82 fn from(value: Date) -> Self {
83 let year = value.year();
84 let month = value.month();
85 let day = value.day();
86
87 super::Date {
88 year: year as u16,
89 month: Some(month as u8),
90 day: Some(day as u8),
91 }
92 }
93}
94
95impl TryFrom<HL7Time> for Time {
96 type Error = DateTimeParseError;
97
98 fn try_from(value: super::Time) -> Result<Self, Self::Error> {
99 let super::Time {
100 hour,
101 minute,
102 second,
103 microsecond,
104 ..
105 } = value;
106
107 let minute = minute.unwrap_or(0);
108 let second = second.unwrap_or(0);
109 let microsecond = microsecond.unwrap_or(0);
110 Ok(jiff::civil::time(
111 hour as i8,
112 minute as i8,
113 second as i8,
114 microsecond as i32,
115 ))
116 }
117}
118
119impl From<Time> for HL7Time {
120 fn from(value: Time) -> Self {
121 let hour = value.hour();
122 let minute = value.minute();
123 let second = value.second();
124 let microsecond = value.microsecond();
125
126 super::Time {
127 hour: hour as u8,
128 minute: Some(minute as u8),
129 second: Some(second as u8),
130 microsecond: Some(microsecond as u32),
131 offset: None,
132 }
133 }
134}
135
136impl TryFrom<HL7TimeStamp> for Time {
137 type Error = DateTimeParseError;
138
139 fn try_from(value: HL7TimeStamp) -> Result<Self, Self::Error> {
140 let HL7TimeStamp {
141 hour,
142 minute,
143 second,
144 microsecond,
145 ..
146 } = value;
147
148 let hour = hour.unwrap_or(0);
149 let minute = minute.unwrap_or(0);
150 let second = second.unwrap_or(0);
151 let microsecond = microsecond.unwrap_or(0);
152 Ok(jiff::civil::time(
153 hour as i8,
154 minute as i8,
155 second as i8,
156 microsecond as i32,
157 ))
158 }
159}
160
161impl TryFrom<HL7TimeStamp> for DateTime {
162 type Error = DateTimeParseError;
163
164 fn try_from(value: HL7TimeStamp) -> Result<Self, Self::Error> {
165 let date = Date::try_from(value)?;
166 let time = Time::try_from(value)?;
167
168 Ok(jiff::civil::datetime(
169 date.year(),
170 date.month(),
171 date.day(),
172 time.hour(),
173 time.minute(),
174 time.second(),
175 time.microsecond().into(),
176 ))
177 }
178}
179
180impl From<DateTime> for HL7TimeStamp {
181 fn from(value: DateTime) -> Self {
182 let date = value.date();
183 let time = value.time();
184
185 let year = date.year();
186 let month = date.month();
187 let day = date.day();
188
189 let hour = time.hour();
190 let minute = time.minute();
191 let second = time.second();
192 let microsecond = time.microsecond();
193
194 HL7TimeStamp {
195 year: year as u16,
196 month: Some(month as u8),
197 day: Some(day as u8),
198 hour: Some(hour as u8),
199 minute: Some(minute as u8),
200 second: Some(second as u8),
201 microsecond: Some(microsecond as u32),
202 offset: None,
203 }
204 }
205}
206
207impl TryFrom<Zoned> for HL7TimeStamp {
208 type Error = DateTimeParseError;
209
210 fn try_from(value: Zoned) -> Result<Self, Self::Error> {
211 let date = value.date();
212 let time = value.time();
213 let offset = value.offset();
214
215 let year = date.year();
216 let month = date.month();
217 let day = date.day();
218
219 let hour = time.hour();
220 let minute = time.minute();
221 let second = time.second();
222 let microsecond = time.microsecond();
223
224 let offset_seconds = offset.seconds();
225 let offset_hours: i8 = (offset_seconds / 3600) as i8;
226 let offset_minutes: u8 = ((offset_seconds.abs() % 3600) / 60) as u8;
227
228 Ok(HL7TimeStamp {
229 year: year as u16,
230 month: Some(month as u8),
231 day: Some(day as u8),
232 hour: Some(hour as u8),
233 minute: Some(minute as u8),
234 second: Some(second as u8),
235 microsecond: Some(microsecond as u32),
236 offset: Some(super::TimeStampOffset {
237 hours: offset_hours,
238 minutes: offset_minutes,
239 }),
240 })
241 }
242}
243
244impl TryFrom<HL7TimeStamp> for Zoned {
245 type Error = jiff::Error;
246
247 fn try_from(value: HL7TimeStamp) -> Result<Self, Self::Error> {
248 let date = Date::try_from(value).unwrap();
249 let time = Time::try_from(value).unwrap();
250 let offset = value.offset.unwrap_or_default();
251
252 let year = date.year();
253 let month = date.month();
254 let day = date.day();
255
256 let hour = time.hour();
257 let minute = time.minute();
258 let second = time.second();
259 let microsecond = time.microsecond();
260
261 let offset_seconds = (offset.hours as i32 * 3600) + (offset.minutes as i32 * 60);
262 let offset = jiff::tz::Offset::from_seconds(offset_seconds)?;
263 let timezone = jiff::tz::TimeZone::fixed(offset);
264
265 let datetime =
266 jiff::civil::datetime(year, month, day, hour, minute, second, microsecond.into());
267
268 datetime.to_zoned(timezone)
269 }
270}
271
272#[cfg(test)]
273mod tests {
274 use super::*;
275
276 #[test]
277 fn can_roundtrip_date() {
278 let date = date(2021, 1, 1);
279 let hl7_date = HL7Date::from(date);
280 let date2 = Date::try_from(hl7_date).unwrap();
281 assert_eq!(date, date2);
282 }
283
284 #[test]
285 fn can_roundtrip_time() {
286 let time = jiff::civil::time(12, 0, 0, 0);
287 let hl7_time = HL7Time::from(time);
288 let time2 = Time::try_from(hl7_time).unwrap();
289 assert_eq!(time, time2);
290 }
291
292 #[test]
293 fn can_roundtrip_timestamp() {
294 let timestamp = jiff::civil::datetime(2021, 1, 1, 12, 0, 0, 0);
295 let hl7_timestamp = HL7TimeStamp::from(timestamp);
296 let timestamp2 = DateTime::try_from(hl7_timestamp).unwrap();
297 assert_eq!(timestamp, timestamp2);
298 }
299
300 #[test]
301 fn can_convert_timestamp_to_zoned() {
302 let timestamp = jiff::civil::datetime(2021, 1, 1, 12, 0, 0, 0);
303 let hl7_timestamp = HL7TimeStamp::from(timestamp);
304 let zoned = Zoned::try_from(hl7_timestamp).unwrap();
305 assert_eq!(zoned.date(), timestamp.date());
306 assert_eq!(zoned.time(), timestamp.time());
307 }
308
309 #[test]
310 fn can_convert_zoned_to_timestamp() {
311 let timestamp = jiff::civil::datetime(2021, 1, 1, 12, 0, 0, 0);
312 let zoned = timestamp.to_zoned(jiff::tz::TimeZone::UTC).unwrap();
313 let hl7_timestamp = HL7TimeStamp::try_from(zoned).unwrap();
314
315 assert_eq!(hl7_timestamp.year, 2021);
316 assert_eq!(hl7_timestamp.month, Some(1));
317 assert_eq!(hl7_timestamp.day, Some(1));
318 assert_eq!(hl7_timestamp.hour, Some(12));
319 assert_eq!(hl7_timestamp.minute, Some(0));
320 assert_eq!(hl7_timestamp.second, Some(0));
321 assert_eq!(hl7_timestamp.microsecond, Some(0));
322 assert_eq!(hl7_timestamp.offset, Some(crate::datetime::TimeStampOffset { hours: 0, minutes: 0 }));
323 }
324}