Skip to content

Commit

Permalink
Return Result for NaiveDate
Browse files Browse the repository at this point in the history
  • Loading branch information
Zomtir committed Feb 17, 2024
1 parent 8c5425d commit 5d476d3
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 87 deletions.
47 changes: 27 additions & 20 deletions src/datetime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use crate::offset::{FixedOffset, Offset, TimeZone, Utc};
use crate::try_opt;
#[cfg(any(feature = "clock", feature = "std"))]
use crate::OutOfRange;
use crate::{Datelike, Months, TimeDelta, Timelike, Weekday};
use crate::{Datelike, Error, Months, TimeDelta, Timelike, Weekday};

#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))]
use rkyv::{Archive, Deserialize, Serialize};
Expand Down Expand Up @@ -669,13 +669,19 @@ impl From<DateTime<Local>> for DateTime<FixedOffset> {
}

/// Maps the local datetime to other datetime with given conversion function.
fn map_local<Tz: TimeZone, F>(dt: &DateTime<Tz>, mut f: F) -> Option<DateTime<Tz>>
fn map_local<Tz: TimeZone, F>(dt: &DateTime<Tz>, mut f: F) -> Result<DateTime<Tz>, Error>
where
F: FnMut(NaiveDateTime) -> Option<NaiveDateTime>,
F: FnMut(NaiveDateTime) -> Result<NaiveDateTime, Error>,
{
f(dt.overflowing_naive_local())
.and_then(|datetime| dt.timezone().from_local_datetime(&datetime).single())
.filter(|dt| dt >= &DateTime::<Utc>::MIN_UTC && dt <= &DateTime::<Utc>::MAX_UTC)
let dt = f(dt.overflowing_naive_local()).and_then(|datetime| {
dt.timezone().from_local_datetime(&datetime).single().ok_or_else(|| Error::InvalidArgument)
})?;

if dt < DateTime::<Utc>::MIN_UTC || dt > DateTime::<Utc>::MAX_UTC {
return Err(Error::OutOfRange);
}

Ok(dt)
}

impl DateTime<FixedOffset> {
Expand Down Expand Up @@ -933,7 +939,7 @@ impl<Tz: TimeZone> Datelike for DateTime<Tz> {
/// - When the `NaiveDateTime` would be out of range.
/// - The local time at the resulting date does not exist or is ambiguous, for example during a
/// daylight saving time transition.
fn with_year(&self, year: i32) -> Option<DateTime<Tz>> {
fn with_year(&self, year: i32) -> Result<DateTime<Tz>, Error> {
map_local(self, |datetime| datetime.with_year(year))
}

Expand All @@ -949,7 +955,7 @@ impl<Tz: TimeZone> Datelike for DateTime<Tz> {
/// - The local time at the resulting date does not exist or is ambiguous, for example during a
/// daylight saving time transition.
#[inline]
fn with_month(&self, month: u32) -> Option<DateTime<Tz>> {
fn with_month(&self, month: u32) -> Result<DateTime<Tz>, Error> {
map_local(self, |datetime| datetime.with_month(month))
}

Expand All @@ -965,7 +971,7 @@ impl<Tz: TimeZone> Datelike for DateTime<Tz> {
/// - The local time at the resulting date does not exist or is ambiguous, for example during a
/// daylight saving time transition.
#[inline]
fn with_month0(&self, month0: u32) -> Option<DateTime<Tz>> {
fn with_month0(&self, month0: u32) -> Result<DateTime<Tz>, Error> {
map_local(self, |datetime| datetime.with_month0(month0))
}

Expand All @@ -981,7 +987,7 @@ impl<Tz: TimeZone> Datelike for DateTime<Tz> {
/// - The local time at the resulting date does not exist or is ambiguous, for example during a
/// daylight saving time transition.
#[inline]
fn with_day(&self, day: u32) -> Option<DateTime<Tz>> {
fn with_day(&self, day: u32) -> Result<DateTime<Tz>, Error> {
map_local(self, |datetime| datetime.with_day(day))
}

Expand All @@ -997,7 +1003,7 @@ impl<Tz: TimeZone> Datelike for DateTime<Tz> {
/// - The local time at the resulting date does not exist or is ambiguous, for example during a
/// daylight saving time transition.
#[inline]
fn with_day0(&self, day0: u32) -> Option<DateTime<Tz>> {
fn with_day0(&self, day0: u32) -> Result<DateTime<Tz>, Error> {
map_local(self, |datetime| datetime.with_day0(day0))
}

Expand All @@ -1013,7 +1019,7 @@ impl<Tz: TimeZone> Datelike for DateTime<Tz> {
/// - The local time at the resulting date does not exist or is ambiguous, for example during a
/// daylight saving time transition.
#[inline]
fn with_ordinal(&self, ordinal: u32) -> Option<DateTime<Tz>> {
fn with_ordinal(&self, ordinal: u32) -> Result<DateTime<Tz>, Error> {
map_local(self, |datetime| datetime.with_ordinal(ordinal))
}

Expand All @@ -1029,7 +1035,7 @@ impl<Tz: TimeZone> Datelike for DateTime<Tz> {
/// - The local time at the resulting date does not exist or is ambiguous, for example during a
/// daylight saving time transition.
#[inline]
fn with_ordinal0(&self, ordinal0: u32) -> Option<DateTime<Tz>> {
fn with_ordinal0(&self, ordinal0: u32) -> Result<DateTime<Tz>, Error> {
map_local(self, |datetime| datetime.with_ordinal0(ordinal0))
}
}
Expand Down Expand Up @@ -1058,12 +1064,12 @@ impl<Tz: TimeZone> Timelike for DateTime<Tz> {
///
/// # Errors
///
/// Returns `None` if:
/// Returns `Err` if:
/// - The value for `hour` is invalid.
/// - The local time at the resulting date does not exist or is ambiguous, for example during a
/// daylight saving time transition.
#[inline]
fn with_hour(&self, hour: u32) -> Option<DateTime<Tz>> {
fn with_hour(&self, hour: u32) -> Result<DateTime<Tz>, Error> {
map_local(self, |datetime| datetime.with_hour(hour))
}

Expand All @@ -1073,11 +1079,12 @@ impl<Tz: TimeZone> Timelike for DateTime<Tz> {
///
/// # Errors
///
/// Returns `Err` if:
/// - The value for `minute` is invalid.
/// - The local time at the resulting date does not exist or is ambiguous, for example during a
/// daylight saving time transition.
#[inline]
fn with_minute(&self, min: u32) -> Option<DateTime<Tz>> {
fn with_minute(&self, min: u32) -> Result<DateTime<Tz>, Error> {
map_local(self, |datetime| datetime.with_minute(min))
}

Expand All @@ -1090,18 +1097,18 @@ impl<Tz: TimeZone> Timelike for DateTime<Tz> {
///
/// # Errors
///
/// Returns `None` if:
/// Returns `Err` if:
/// - The value for `second` is invalid.
/// - The local time at the resulting date does not exist or is ambiguous, for example during a
/// daylight saving time transition.
#[inline]
fn with_second(&self, sec: u32) -> Option<DateTime<Tz>> {
fn with_second(&self, sec: u32) -> Result<DateTime<Tz>, Error> {
map_local(self, |datetime| datetime.with_second(sec))
}

/// Makes a new `DateTime` with nanoseconds since the whole non-leap second changed.
///
/// Returns `None` when the resulting `NaiveDateTime` would be invalid.
/// Returns `Err` when the resulting `NaiveDateTime` would be invalid.
/// As with the [`NaiveDateTime::nanosecond`] method,
/// the input range can exceed 1,000,000,000 for leap seconds.
///
Expand All @@ -1111,7 +1118,7 @@ impl<Tz: TimeZone> Timelike for DateTime<Tz> {
///
/// Returns `None` if `nanosecond >= 2,000,000,000`.
#[inline]
fn with_nanosecond(&self, nano: u32) -> Option<DateTime<Tz>> {
fn with_nanosecond(&self, nano: u32) -> Result<DateTime<Tz>, Error> {
map_local(self, |datetime| datetime.with_nanosecond(nano))
}
}
Expand Down
42 changes: 21 additions & 21 deletions src/naive/date/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -793,13 +793,13 @@ impl NaiveDate {
///
/// Returns `None` when the resulting `NaiveDate` would be invalid.
#[inline]
const fn with_mdf(&self, mdf: Mdf) -> Option<NaiveDate> {
const fn with_mdf(&self, mdf: Mdf) -> Result<NaiveDate, Error> {
debug_assert!(self.year_flags().0 == mdf.year_flags().0);
match mdf.ordinal() {
Ok(ordinal) => {
Some(NaiveDate::from_yof((self.yof() & !ORDINAL_MASK) | (ordinal << 4) as i32))
Ok(NaiveDate::from_yof((self.yof() & !ORDINAL_MASK) | (ordinal << 4) as i32))
}
Err(_) => None, // Non-existing date
Err(e) => Err(e), // Non-existing date
}
}

Expand Down Expand Up @@ -1455,15 +1455,15 @@ impl Datelike for NaiveDate {
/// assert!(NaiveDate::from_ymd(2016, 2, 29).unwrap().with_year(2020).is_some());
/// ```
#[inline]
fn with_year(&self, year: i32) -> Option<NaiveDate> {
fn with_year(&self, year: i32) -> Result<NaiveDate, Error> {
// we need to operate with `mdf` since we should keep the month and day number as is
let mdf = self.mdf();

// adjust the flags as needed
let flags = YearFlags::from_year(year);
let mdf = mdf.with_flags(flags);

ok!(NaiveDate::from_mdf(year, mdf))
NaiveDate::from_mdf(year, mdf)
}

/// Makes a new `NaiveDate` with the month number (starting from 1) changed.
Expand All @@ -1483,8 +1483,8 @@ impl Datelike for NaiveDate {
/// assert_eq!(NaiveDate::from_ymd(2015, 9, 30).unwrap().with_month(2), None); // no February 30
/// ```
#[inline]
fn with_month(&self, month: u32) -> Option<NaiveDate> {
self.with_mdf(self.mdf().with_month(month).ok()?)
fn with_month(&self, month: u32) -> Result<NaiveDate, Error> {
self.with_mdf(self.mdf().with_month(month)?)
}

/// Makes a new `NaiveDate` with the month number (starting from 0) changed.
Expand All @@ -1505,9 +1505,9 @@ impl Datelike for NaiveDate {
/// assert_eq!(NaiveDate::from_ymd(2015, 9, 30).unwrap().with_month0(1), None); // no February 30
/// ```
#[inline]
fn with_month0(&self, month0: u32) -> Option<NaiveDate> {
let month = month0.checked_add(1)?;
self.with_mdf(self.mdf().with_month(month).ok()?)
fn with_month0(&self, month0: u32) -> Result<NaiveDate, Error> {
let month = month0.checked_add(1).ok_or(Error::InvalidArgument)?;
self.with_mdf(self.mdf().with_month(month)?)
}

/// Makes a new `NaiveDate` with the day of month (starting from 1) changed.
Expand All @@ -1527,8 +1527,8 @@ impl Datelike for NaiveDate {
/// None); // no September 31
/// ```
#[inline]
fn with_day(&self, day: u32) -> Option<NaiveDate> {
self.with_mdf(self.mdf().with_day(day).ok()?)
fn with_day(&self, day: u32) -> Result<NaiveDate, Error> {
self.with_mdf(self.mdf().with_day(day)?)
}

/// Makes a new `NaiveDate` with the day of month (starting from 0) changed.
Expand All @@ -1548,9 +1548,9 @@ impl Datelike for NaiveDate {
/// None); // no September 31
/// ```
#[inline]
fn with_day0(&self, day0: u32) -> Option<NaiveDate> {
let day = day0.checked_add(1)?;
self.with_mdf(self.mdf().with_day(day).ok()?)
fn with_day0(&self, day0: u32) -> Result<NaiveDate, Error> {
let day = day0.checked_add(1).ok_or(Error::InvalidArgument)?;
self.with_mdf(self.mdf().with_day(day)?)
}

/// Makes a new `NaiveDate` with the day of year (starting from 1) changed.
Expand All @@ -1576,14 +1576,14 @@ impl Datelike for NaiveDate {
/// Some(NaiveDate::from_ymd(2016, 12, 31).unwrap()));
/// ```
#[inline]
fn with_ordinal(&self, ordinal: u32) -> Option<NaiveDate> {
fn with_ordinal(&self, ordinal: u32) -> Result<NaiveDate, Error> {
if ordinal == 0 || ordinal > 366 {
return None;
return Err(Error::InvalidArgument);
}
let yof = (self.yof() & !ORDINAL_MASK) | (ordinal << 4) as i32;
match yof & OL_MASK <= MAX_OL {
true => Some(NaiveDate::from_yof(yof)),
false => None, // Does not exist: Ordinal 366 in a common year.
true => Ok(NaiveDate::from_yof(yof)),
false => Err(Error::DoesNotExist), // Does not exist: Ordinal 366 in a common year.
}
}

Expand All @@ -1610,8 +1610,8 @@ impl Datelike for NaiveDate {
/// Some(NaiveDate::from_ymd(2016, 12, 31).unwrap()));
/// ```
#[inline]
fn with_ordinal0(&self, ordinal0: u32) -> Option<NaiveDate> {
let ordinal = ordinal0.checked_add(1)?;
fn with_ordinal0(&self, ordinal0: u32) -> Result<NaiveDate, Error> {
let ordinal = ordinal0.checked_add(1).ok_or(Error::InvalidArgument)?;
self.with_ordinal(ordinal)
}
}
Expand Down
26 changes: 13 additions & 13 deletions src/naive/datetime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ use crate::naive::{Days, IsoWeek, NaiveDate, NaiveTime};
use crate::offset::Utc;
use crate::time_delta::NANOS_PER_SEC;
use crate::{
expect, ok, try_opt, DateTime, Datelike, FixedOffset, LocalResult, Months, TimeDelta, TimeZone,
Timelike, Weekday,
expect, ok, try_opt, DateTime, Datelike, Error, FixedOffset, LocalResult, Months, TimeDelta,
TimeZone, Timelike, Weekday,
};

/// Tools to help serializing/deserializing `NaiveDateTime`s
Expand Down Expand Up @@ -1238,7 +1238,7 @@ impl Datelike for NaiveDateTime {
/// assert_eq!(dt.with_year(-308), Some(NaiveDate::from_ymd(-308, 9, 25).unwrap().and_hms(12, 34, 56).unwrap()));
/// ```
#[inline]
fn with_year(&self, year: i32) -> Option<NaiveDateTime> {
fn with_year(&self, year: i32) -> Result<NaiveDateTime, Error> {
self.date.with_year(year).map(|d| NaiveDateTime { date: d, ..*self })
}

Expand All @@ -1261,7 +1261,7 @@ impl Datelike for NaiveDateTime {
/// assert_eq!(dt.with_month(2), None); // no February 30
/// ```
#[inline]
fn with_month(&self, month: u32) -> Option<NaiveDateTime> {
fn with_month(&self, month: u32) -> Result<NaiveDateTime, Error> {
self.date.with_month(month).map(|d| NaiveDateTime { date: d, ..*self })
}

Expand All @@ -1285,7 +1285,7 @@ impl Datelike for NaiveDateTime {
/// assert_eq!(dt.with_month0(1), None); // no February 30
/// ```
#[inline]
fn with_month0(&self, month0: u32) -> Option<NaiveDateTime> {
fn with_month0(&self, month0: u32) -> Result<NaiveDateTime, Error> {
self.date.with_month0(month0).map(|d| NaiveDateTime { date: d, ..*self })
}

Expand All @@ -1307,7 +1307,7 @@ impl Datelike for NaiveDateTime {
/// assert_eq!(dt.with_day(31), None); // no September 31
/// ```
#[inline]
fn with_day(&self, day: u32) -> Option<NaiveDateTime> {
fn with_day(&self, day: u32) -> Result<NaiveDateTime, Error> {
self.date.with_day(day).map(|d| NaiveDateTime { date: d, ..*self })
}

Expand All @@ -1329,7 +1329,7 @@ impl Datelike for NaiveDateTime {
/// assert_eq!(dt.with_day0(30), None); // no September 31
/// ```
#[inline]
fn with_day0(&self, day0: u32) -> Option<NaiveDateTime> {
fn with_day0(&self, day0: u32) -> Result<NaiveDateTime, Error> {
self.date.with_day0(day0).map(|d| NaiveDateTime { date: d, ..*self })
}

Expand Down Expand Up @@ -1359,7 +1359,7 @@ impl Datelike for NaiveDateTime {
/// Some(NaiveDate::from_ymd(2016, 12, 31).unwrap().and_hms(12, 34, 56).unwrap()));
/// ```
#[inline]
fn with_ordinal(&self, ordinal: u32) -> Option<NaiveDateTime> {
fn with_ordinal(&self, ordinal: u32) -> Result<NaiveDateTime, Error> {
self.date.with_ordinal(ordinal).map(|d| NaiveDateTime { date: d, ..*self })
}

Expand Down Expand Up @@ -1389,7 +1389,7 @@ impl Datelike for NaiveDateTime {
/// Some(NaiveDate::from_ymd(2016, 12, 31).unwrap().and_hms(12, 34, 56).unwrap()));
/// ```
#[inline]
fn with_ordinal0(&self, ordinal0: u32) -> Option<NaiveDateTime> {
fn with_ordinal0(&self, ordinal0: u32) -> Result<NaiveDateTime, Error> {
self.date.with_ordinal0(ordinal0).map(|d| NaiveDateTime { date: d, ..*self })
}
}
Expand Down Expand Up @@ -1484,7 +1484,7 @@ impl Timelike for NaiveDateTime {
/// assert_eq!(dt.with_hour(24), None);
/// ```
#[inline]
fn with_hour(&self, hour: u32) -> Option<NaiveDateTime> {
fn with_hour(&self, hour: u32) -> Result<NaiveDateTime, Error> {
self.time.with_hour(hour).map(|t| NaiveDateTime { time: t, ..*self })
}

Expand All @@ -1507,7 +1507,7 @@ impl Timelike for NaiveDateTime {
/// assert_eq!(dt.with_minute(60), None);
/// ```
#[inline]
fn with_minute(&self, min: u32) -> Option<NaiveDateTime> {
fn with_minute(&self, min: u32) -> Result<NaiveDateTime, Error> {
self.time.with_minute(min).map(|t| NaiveDateTime { time: t, ..*self })
}

Expand All @@ -1533,7 +1533,7 @@ impl Timelike for NaiveDateTime {
/// assert_eq!(dt.with_second(60), None);
/// ```
#[inline]
fn with_second(&self, sec: u32) -> Option<NaiveDateTime> {
fn with_second(&self, sec: u32) -> Result<NaiveDateTime, Error> {
self.time.with_second(sec).map(|t| NaiveDateTime { time: t, ..*self })
}

Expand Down Expand Up @@ -1562,7 +1562,7 @@ impl Timelike for NaiveDateTime {
/// assert_eq!(dt.with_nanosecond(2_000_000_000), None);
/// ```
#[inline]
fn with_nanosecond(&self, nano: u32) -> Option<NaiveDateTime> {
fn with_nanosecond(&self, nano: u32) -> Result<NaiveDateTime, Error> {
self.time.with_nanosecond(nano).map(|t| NaiveDateTime { time: t, ..*self })
}
}
Expand Down
Loading

0 comments on commit 5d476d3

Please sign in to comment.