diff --git a/.cargo/config.toml b/.cargo/config.toml index f63d51264..9490b4f19 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,3 +1,4 @@ [alias] # Run doctests, displaying compiler output -dto = "test --doc -- --show-output" \ No newline at end of file +dto = "test --doc -- --show-output" +nowarn = "clippy --all-targets -- -D warnings" \ No newline at end of file diff --git a/src/errors.rs b/src/errors.rs index 41acff3f2..44a6ed818 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -73,6 +73,8 @@ pub enum GdalError { UnlinkMemFile { file_name: String }, #[error("BadArgument")] BadArgument(String), + #[error("Date conversion error: {0}")] + DateError(String), #[cfg(all(major_ge_3, minor_ge_1))] #[error("Unhandled type '{data_type}' on GDAL MD method {method_name}")] diff --git a/src/vector/feature.rs b/src/vector/feature.rs index 07459f2e0..6e89fee3a 100644 --- a/src/vector/feature.rs +++ b/src/vector/feature.rs @@ -7,7 +7,7 @@ use std::convert::TryInto; use std::ffi::{CString, NulError}; use std::ptr; -use chrono::{Date, DateTime, Datelike, FixedOffset, TimeZone, Timelike}; +use chrono::{DateTime, Datelike, FixedOffset, LocalResult, NaiveDate, TimeZone, Timelike}; use crate::errors::*; use std::slice; @@ -152,7 +152,7 @@ impl<'a> Feature<'a> { self._field_as_datetime(field_id)?, ))), OGRFieldType::OFTDate => Ok(Some(FieldValue::DateValue( - self._field_as_datetime(field_id)?.date(), + self._field_as_datetime(field_id)?.date_naive() ))), _ => Err(GdalError::UnhandledFieldType { field_type, @@ -428,10 +428,25 @@ impl<'a> Feature<'a> { } else { (tzflag as i32 - 100) * 15 * 60 }; - let rv = FixedOffset::east(tzoffset_secs) - .ymd(year as i32, month as u32, day as u32) - .and_hms(hour as u32, minute as u32, second as u32); - Ok(rv) + let rv = FixedOffset::east_opt(tzoffset_secs) + .ok_or_else(|| GdalError::DateError(tzoffset_secs.to_string()))? + .with_ymd_and_hms( + year as i32, + month as u32, + day as u32, + hour as u32, + minute as u32, + second as u32, + ); + match rv { + LocalResult::None => Err( + GdalError::DateError(format!("Unable to reconstruct valid date from fields: {year}, {month}, {day}, {hour}, {minute}, {second}")) + ), + LocalResult::Single(d) => Ok(d), + LocalResult::Ambiguous(d1, d2) => Err( + GdalError::DateError(format!("ambiguous date conversion; either '{d1}' or '{d2}'")) + ) + } } /// Get the field's geometry. @@ -614,7 +629,11 @@ impl<'a> Feature<'a> { FieldValue::RealValue(value) => self.set_field_double(field_name, *value), FieldValue::RealListValue(value) => self.set_field_double_list(field_name, value), FieldValue::DateValue(value) => { - self.set_field_datetime(field_name, value.and_hms(0, 0, 0)) + let dv = value.and_hms_opt(0, 0, 0) + .ok_or_else(|| GdalError::DateError("offset to midnight".into()))?; + let dt = DateTime::from_utc(dv, FixedOffset::east_opt(0) + .ok_or_else(|| GdalError::DateError("utc offset".into()))?); + self.set_field_datetime(field_name, dt) } FieldValue::DateTimeValue(value) => self.set_field_datetime(field_name, *value), } @@ -709,7 +728,7 @@ pub enum FieldValue { StringListValue(Vec), RealValue(f64), RealListValue(Vec), - DateValue(Date), + DateValue(NaiveDate), DateTimeValue(DateTime), } @@ -748,11 +767,11 @@ impl FieldValue { } } - /// Interpret the value as `Date`. Returns `None` if the value is something else. - pub fn into_date(self) -> Option> { + /// Interpret the value as `NaiveDate`. Returns `None` if the value is something else. + pub fn into_date(self) -> Option { match self { FieldValue::DateValue(rv) => Some(rv), - FieldValue::DateTimeValue(rv) => Some(rv.date()), + FieldValue::DateTimeValue(rv) => Some(rv.date_naive()), _ => None, } } diff --git a/src/vector/vector_tests/mod.rs b/src/vector/vector_tests/mod.rs index 881643475..ec72327f2 100644 --- a/src/vector/vector_tests/mod.rs +++ b/src/vector/vector_tests/mod.rs @@ -393,11 +393,15 @@ mod tests { with_features("points_with_datetime.json", |mut features| { let feature = features.next().unwrap(); - let dt = FixedOffset::east(-5 * hour_secs) - .ymd(2011, 7, 14) - .and_hms(19, 43, 37); + let dt = FixedOffset::east_opt(-5 * hour_secs) + .unwrap() + .with_ymd_and_hms(2011, 7, 14, 19, 43, 37) + .unwrap(); - let d = FixedOffset::east(0).ymd(2018, 1, 4).and_hms(0, 0, 0); + let d = FixedOffset::east_opt(0) + .unwrap() + .with_ymd_and_hms(2018, 1, 4, 0, 0, 0) + .unwrap(); assert_eq!(feature.field_as_datetime_by_name("dt").unwrap(), Some(dt));