|
1 | 1 | //! GRIB2 metadata carried by Sections 1 and 4. |
2 | 2 |
|
3 | 3 | use crate::error::{Error, Result}; |
| 4 | +use crate::metadata::ReferenceTime; |
4 | 5 | use crate::parameter; |
5 | 6 | use crate::util::{grib_i32, grib_i8}; |
6 | 7 |
|
@@ -37,18 +38,28 @@ impl Identification { |
37 | 38 | }); |
38 | 39 | } |
39 | 40 |
|
| 41 | + let reference_time = ReferenceTime { |
| 42 | + year: u16::from_be_bytes(section_bytes[12..14].try_into().unwrap()), |
| 43 | + month: section_bytes[14], |
| 44 | + day: section_bytes[15], |
| 45 | + hour: section_bytes[16], |
| 46 | + minute: section_bytes[17], |
| 47 | + second: section_bytes[18], |
| 48 | + }; |
| 49 | + reference_time.validate_in_section(1)?; |
| 50 | + |
40 | 51 | Ok(Self { |
41 | 52 | center_id: u16::from_be_bytes(section_bytes[5..7].try_into().unwrap()), |
42 | 53 | subcenter_id: u16::from_be_bytes(section_bytes[7..9].try_into().unwrap()), |
43 | 54 | master_table_version: section_bytes[9], |
44 | 55 | local_table_version: section_bytes[10], |
45 | 56 | significance_of_reference_time: section_bytes[11], |
46 | | - reference_year: u16::from_be_bytes(section_bytes[12..14].try_into().unwrap()), |
47 | | - reference_month: section_bytes[14], |
48 | | - reference_day: section_bytes[15], |
49 | | - reference_hour: section_bytes[16], |
50 | | - reference_minute: section_bytes[17], |
51 | | - reference_second: section_bytes[18], |
| 57 | + reference_year: reference_time.year, |
| 58 | + reference_month: reference_time.month, |
| 59 | + reference_day: reference_time.day, |
| 60 | + reference_hour: reference_time.hour, |
| 61 | + reference_minute: reference_time.minute, |
| 62 | + reference_second: reference_time.second, |
52 | 63 | production_status: section_bytes[19], |
53 | 64 | processed_data_type: section_bytes[20], |
54 | 65 | }) |
@@ -239,29 +250,29 @@ mod tests { |
239 | 250 |
|
240 | 251 | #[test] |
241 | 252 | fn parses_identification_section() { |
242 | | - let mut section = vec![0u8; 21]; |
243 | | - section[..4].copy_from_slice(&(21u32).to_be_bytes()); |
244 | | - section[4] = 1; |
245 | | - section[5..7].copy_from_slice(&7u16.to_be_bytes()); |
246 | | - section[7..9].copy_from_slice(&14u16.to_be_bytes()); |
247 | | - section[9] = 35; |
248 | | - section[10] = 1; |
249 | | - section[11] = 1; |
250 | | - section[12..14].copy_from_slice(&2026u16.to_be_bytes()); |
251 | | - section[14] = 3; |
252 | | - section[15] = 20; |
253 | | - section[16] = 12; |
254 | | - section[17] = 30; |
255 | | - section[18] = 45; |
256 | | - section[19] = 0; |
257 | | - section[20] = 1; |
| 253 | + let section = valid_identification_section(); |
258 | 254 |
|
259 | 255 | let id = Identification::parse(§ion).unwrap(); |
260 | 256 | assert_eq!(id.center_id, 7); |
261 | 257 | assert_eq!(id.reference_year, 2026); |
262 | 258 | assert_eq!(id.reference_hour, 12); |
263 | 259 | } |
264 | 260 |
|
| 261 | + #[test] |
| 262 | + fn rejects_invalid_identification_reference_time() { |
| 263 | + let mut section = valid_identification_section(); |
| 264 | + section[14] = 2; |
| 265 | + section[15] = 29; |
| 266 | + let err = Identification::parse(§ion).unwrap_err(); |
| 267 | + assert!(matches!(err, Error::InvalidSection { section: 1, .. })); |
| 268 | + assert!(err.to_string().contains("invalid reference timestamp")); |
| 269 | + |
| 270 | + let mut section = valid_identification_section(); |
| 271 | + section[18] = 60; |
| 272 | + let err = Identification::parse(§ion).unwrap_err(); |
| 273 | + assert!(matches!(err, Error::InvalidSection { section: 1, .. })); |
| 274 | + } |
| 275 | + |
265 | 276 | #[test] |
266 | 277 | fn parses_product_definition_template_zero_fields() { |
267 | 278 | let mut section = vec![0u8; 34]; |
@@ -321,4 +332,24 @@ mod tests { |
321 | 332 | let err = ProductDefinition::parse(§ion).unwrap_err(); |
322 | 333 | assert!(matches!(err, Error::InvalidSection { section: 4, .. })); |
323 | 334 | } |
| 335 | + |
| 336 | + fn valid_identification_section() -> Vec<u8> { |
| 337 | + let mut section = vec![0u8; 21]; |
| 338 | + section[..4].copy_from_slice(&(21u32).to_be_bytes()); |
| 339 | + section[4] = 1; |
| 340 | + section[5..7].copy_from_slice(&7u16.to_be_bytes()); |
| 341 | + section[7..9].copy_from_slice(&14u16.to_be_bytes()); |
| 342 | + section[9] = 35; |
| 343 | + section[10] = 1; |
| 344 | + section[11] = 1; |
| 345 | + section[12..14].copy_from_slice(&2026u16.to_be_bytes()); |
| 346 | + section[14] = 3; |
| 347 | + section[15] = 20; |
| 348 | + section[16] = 12; |
| 349 | + section[17] = 30; |
| 350 | + section[18] = 45; |
| 351 | + section[19] = 0; |
| 352 | + section[20] = 1; |
| 353 | + section |
| 354 | + } |
324 | 355 | } |
0 commit comments