hl7_parser/datetime/
date.rs1use crate::parser::Span;
2use nom::{
3 bytes::complete::take_while_m_n,
4 combinator::{map_res, opt},
5 IResult,
6};
7use std::{fmt::Display, str::FromStr};
8
9use super::DateTimeParseError;
10
11#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14pub struct Date {
15 pub year: u16,
17 pub month: Option<u8>,
19 pub day: Option<u8>,
21}
22
23pub fn parse_date(s: &str, lenient_trailing_chars: bool) -> Result<Date, DateTimeParseError> {
42 fn is_decimal_digit(c: char) -> bool {
43 c.is_ascii_digit()
44 }
45
46 fn from_digits<F: FromStr>(i: Span) -> Result<F, F::Err> {
47 i.input.parse::<F>()
48 }
49
50 fn digit2<F: FromStr>(input: Span) -> IResult<Span, F> {
51 map_res(take_while_m_n(2, 2, is_decimal_digit), from_digits::<F>)(input)
52 }
53
54 fn digit4<F: FromStr>(input: Span) -> IResult<Span, F> {
55 map_res(take_while_m_n(4, 4, is_decimal_digit), from_digits::<F>)(input)
56 }
57
58 let s = Span::new(s);
59 let (s, year): (Span, u16) =
60 digit4(s).map_err(|_| DateTimeParseError::ParsingFailed("year"))?;
61 let (s, month): (Span, Option<u8>) =
62 opt(digit2)(s).map_err(|_| DateTimeParseError::ParsingFailed("month"))?;
63 let (s, day): (Span, Option<u8>) =
64 opt(digit2)(s).map_err(|_| DateTimeParseError::ParsingFailed("day"))?;
65
66 if !lenient_trailing_chars && !s.is_empty() {
67 return Err(DateTimeParseError::UnexpectedCharacter(
68 s.offset,
69 s.input.chars().next().unwrap_or_default(),
70 ));
71 }
72
73 Ok(Date { year, month, day })
74}
75
76impl FromStr for Date {
78 type Err = DateTimeParseError;
79
80 fn from_str(s: &str) -> Result<Self, Self::Err> {
82 parse_date(s, false)
83 }
84}
85
86impl Display for Date {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 write!(f, "{:04}", self.year)?;
90 if let Some(month) = self.month {
91 write!(f, "{:02}", month)?;
92 if let Some(day) = self.day {
93 write!(f, "{:02}", day)?;
94 }
95 }
96 Ok(())
97 }
98}
99
100#[cfg(test)]
101mod test {
102 use super::*;
103 use pretty_assertions_sorted::assert_eq;
104
105 #[test]
106 fn can_parse_date() {
107 let date = "20230312";
108 let date = parse_date(date, false).expect("can parse date");
109
110 assert_eq!(date.year, 2023);
111 assert_eq!(date.month, Some(3));
112 assert_eq!(date.day, Some(12));
113 }
114}