Skip to content

Commit 775ea53

Browse files
committed
Type F datetime parsing
Unhelpfully one of the tests is now failing because libmbus ignores invalid data and I haven't added any sensible invalid data handling
1 parent a7539ca commit 775ea53

File tree

1 file changed

+90
-5
lines changed

1 file changed

+90
-5
lines changed

src/parse/types/date.rs

Lines changed: 90 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,20 +90,105 @@ impl TypeFDateTime {
9090
parse_dmy,
9191
))
9292
.map(
93-
|(_, _, minute, in_dst, hundred_year, hour, (day, month, year))| TypeFDateTime {
93+
|(
94+
_,
95+
_,
9496
minute,
9597
in_dst,
98+
mut hundred_year,
9699
hour,
97-
day,
98-
month,
99-
year,
100-
hundred_year,
100+
(day, month, year),
101+
//
102+
)| {
103+
// EN 13757-3:2018 Annex A table A.5 footnote a:
104+
// "For compatibility with old meters with a circular two digit
105+
// date it is recommended to consider in any master software the
106+
// years “00” to “80” as the years 2000 to 2080.""
107+
if hundred_year == 0 && year <= 80 {
108+
hundred_year = 1;
109+
}
110+
TypeFDateTime {
111+
minute,
112+
in_dst,
113+
hour,
114+
day,
115+
month,
116+
year,
117+
hundred_year,
118+
}
101119
},
102120
)
103121
.parse_next(input)
104122
}
105123
}
106124

125+
#[cfg(test)]
126+
mod test_type_f_date_time {
127+
use rstest::rstest;
128+
use winnow::error::ErrorKind;
129+
use winnow::error::StrContext;
130+
use winnow::prelude::*;
131+
use winnow::Bytes;
132+
133+
use super::TypeFDateTime;
134+
135+
#[rstest]
136+
#[case::ACW_Itron_BM_plus_m__0([0x0B, 0x0B, 0xCD, 0x13], TypeFDateTime{
137+
hundred_year: 1,
138+
year: 14,
139+
in_dst: false,
140+
month: 3,
141+
day: 13,
142+
hour: 11,
143+
minute: 11,
144+
})]
145+
#[case::amt_calec_mb([0x10, 0x09, 0x05, 0xC5], TypeFDateTime{
146+
hundred_year: 0,
147+
year: 96,
148+
in_dst: false,
149+
month: 5,
150+
day: 5,
151+
hour: 9,
152+
minute: 16,
153+
})]
154+
#[case::kamstrup_multical_601([0x1A, 0x2F, 0x65, 0x11], TypeFDateTime{
155+
hundred_year: 1,
156+
year: 11,
157+
month: 1,
158+
day: 5,
159+
hour: 15,
160+
minute: 26,
161+
in_dst: false,
162+
})]
163+
#[allow(non_snake_case)]
164+
fn test_file_values(#[case] input: [u8; 4], #[case] expected: TypeFDateTime) {
165+
let input = Bytes::new(&input);
166+
167+
let result = TypeFDateTime::parse.parse(input).unwrap();
168+
169+
assert_eq!(result, expected);
170+
}
171+
172+
#[rstest]
173+
#[case::REL_Relay_Padpuls2([0xA1, 0x15, 0xE9, 0x17], "invalid bit")]
174+
#[case::invalid_bit([0b1000_0000, 0x00, 0x01, 0x01], "invalid bit")]
175+
#[case::reserved_bit([0b0100_0000, 0x00, 0x01, 0x01], "reserved")]
176+
#[case::invalid_minute([0x3C, 0x00, 0x01, 0x01], "minute")]
177+
#[case::invalid_hour([0x00, 0x18, 0x01, 0x01], "hour")]
178+
#[case::invalid_month([0x00, 0x00, 0b111_00001, 0b0000_1101], "month")]
179+
#[case::invalid_year([0x00, 0x00, 0b100_00001, 0b1100_0001], "year")]
180+
#[allow(non_snake_case)]
181+
fn test_validation(#[case] input: [u8; 4], #[case] context: &'static str) {
182+
let input = Bytes::new(&input);
183+
184+
let result = TypeFDateTime::parse.parse(input).unwrap_err();
185+
186+
let err = result.inner();
187+
assert_eq!(err.kind(), ErrorKind::Verify);
188+
assert_eq!(err.context().next(), Some(&StrContext::Label(context)));
189+
}
190+
}
191+
107192
#[derive(Debug, PartialEq, Eq)]
108193
pub struct TypeGDate {
109194
pub day: u8,

0 commit comments

Comments
 (0)