@@ -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 ) ]
108193pub struct TypeGDate {
109194 pub day : u8 ,
0 commit comments