diff --git a/gds21/src/read.rs b/gds21/src/read.rs index 7ba5d41..2a7b7d8 100644 --- a/gds21/src/read.rs +++ b/gds21/src/read.rs @@ -835,13 +835,24 @@ where if d.len() != 12 { return self.fail("Invalid length GdsDateTimes"); } + let parse_datetime = |year, month, day, hour, minute, second| { + NaiveDate::from_ymd_opt(year, month, day) + .and_then(|date| date.and_hms_opt(hour, minute, second)) + .unwrap_or(NaiveDateTime::UNIX_EPOCH) + }; Ok(GdsDateTimes { - modified: NaiveDate::from_ymd(d[0] as i32, d[1] as u32, d[2] as u32).and_hms( + modified: parse_datetime( + d[0] as i32, + d[1] as u32, + d[2] as u32, d[3] as u32, d[4] as u32, d[5] as u32, ), - accessed: NaiveDate::from_ymd(d[6] as i32, d[7] as u32, d[8] as u32).and_hms( + accessed: parse_datetime( + d[6] as i32, + d[7] as u32, + d[8] as u32, d[9] as u32, d[10] as u32, d[11] as u32, diff --git a/gds21/src/tests.rs b/gds21/src/tests.rs index c0931ea..998d5d4 100644 --- a/gds21/src/tests.rs +++ b/gds21/src/tests.rs @@ -2,7 +2,10 @@ use super::*; /// Specified creation date for test cases fn test_dates() -> GdsDateTimes { - let test_date = NaiveDate::from_ymd(1970, 1, 1).and_hms(0, 0, 1); + let test_date = NaiveDate::from_ymd_opt(1970, 1, 1) + .unwrap() + .and_hms_opt(0, 0, 1) + .unwrap(); GdsDateTimes { modified: test_date, accessed: test_date, @@ -80,6 +83,50 @@ fn it_round_trips() -> GdsResult<()> { Ok(()) } #[test] +fn invalid_datetimes_preserve_geometry() -> GdsResult<()> { + let points = GdsPoint::vec(&[(0, 0), (0, 10), (10, 10), (10, 0), (0, 0)]); + let mut strukt = GdsStruct::new("cell"); + strukt.elems.push( + GdsBoundary { + xy: points.clone(), + ..Default::default() + } + .into(), + ); + let mut lib = GdsLibrary::new("lib"); + lib.structs.push(strukt); + + let mut bytes = Vec::new(); + lib.write(&mut bytes)?; + let mut offset = 0; + while offset < bytes.len() { + let record_len = u16::from_be_bytes([bytes[offset], bytes[offset + 1]]) as usize; + let record_type = bytes[offset + 2]; + if record_type == GdsRecordType::BgnLib as u8 + || record_type == GdsRecordType::BgnStruct as u8 + { + bytes[offset + 4..offset + record_len].fill(0); + } + offset += record_len; + } + + let parsed = GdsLibrary::from_bytes(&bytes)?; + let epoch = NaiveDate::from_ymd_opt(1970, 1, 1) + .unwrap() + .and_hms_opt(0, 0, 0) + .unwrap(); + assert_eq!(parsed.dates.modified, epoch); + assert_eq!(parsed.dates.accessed, epoch); + assert_eq!(parsed.structs.len(), 1); + assert_eq!(parsed.structs[0].dates.modified, epoch); + assert_eq!(parsed.structs[0].dates.accessed, epoch); + match &parsed.structs[0].elems[0] { + GdsElement::GdsBoundary(boundary) => assert_eq!(boundary.xy, points), + element => panic!("expected boundary, got {:?}", element), + } + Ok(()) +} +#[test] fn it_has_gds_properties() -> GdsResult<()> { // Read a sample let lib = GdsLibrary::load(&resource("has_properties.gds"))?;