Remove dependency on num-traits

Co-authored-by: Dirkjan Ochtman <dirkjan@ochtman.nl>
This commit is contained in:
Eric Sheppard 2022-08-27 23:16:46 +10:00 committed by Dirkjan Ochtman
parent eb927846b6
commit b0983c25d9
7 changed files with 133 additions and 37 deletions

View File

@ -4,7 +4,7 @@
//! A collection of parsed date and time items.
//! They can be constructed incrementally while being checked for consistency.
use num_traits::ToPrimitive;
use core::convert::TryFrom;
use super::{ParseResult, IMPOSSIBLE, NOT_ENOUGH, OUT_OF_RANGE};
use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime};
@ -136,7 +136,7 @@ impl Parsed {
/// Tries to set the [`year`](#structfield.year) field from given value.
#[inline]
pub fn set_year(&mut self, value: i64) -> ParseResult<()> {
set_if_consistent(&mut self.year, value.to_i32().ok_or(OUT_OF_RANGE)?)
set_if_consistent(&mut self.year, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
}
/// Tries to set the [`year_div_100`](#structfield.year_div_100) field from given value.
@ -145,7 +145,7 @@ impl Parsed {
if value < 0 {
return Err(OUT_OF_RANGE);
}
set_if_consistent(&mut self.year_div_100, value.to_i32().ok_or(OUT_OF_RANGE)?)
set_if_consistent(&mut self.year_div_100, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
}
/// Tries to set the [`year_mod_100`](#structfield.year_mod_100) field from given value.
@ -154,13 +154,13 @@ impl Parsed {
if value < 0 {
return Err(OUT_OF_RANGE);
}
set_if_consistent(&mut self.year_mod_100, value.to_i32().ok_or(OUT_OF_RANGE)?)
set_if_consistent(&mut self.year_mod_100, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
}
/// Tries to set the [`isoyear`](#structfield.isoyear) field from given value.
#[inline]
pub fn set_isoyear(&mut self, value: i64) -> ParseResult<()> {
set_if_consistent(&mut self.isoyear, value.to_i32().ok_or(OUT_OF_RANGE)?)
set_if_consistent(&mut self.isoyear, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
}
/// Tries to set the [`isoyear_div_100`](#structfield.isoyear_div_100) field from given value.
@ -169,7 +169,10 @@ impl Parsed {
if value < 0 {
return Err(OUT_OF_RANGE);
}
set_if_consistent(&mut self.isoyear_div_100, value.to_i32().ok_or(OUT_OF_RANGE)?)
set_if_consistent(
&mut self.isoyear_div_100,
i32::try_from(value).map_err(|_| OUT_OF_RANGE)?,
)
}
/// Tries to set the [`isoyear_mod_100`](#structfield.isoyear_mod_100) field from given value.
@ -178,31 +181,34 @@ impl Parsed {
if value < 0 {
return Err(OUT_OF_RANGE);
}
set_if_consistent(&mut self.isoyear_mod_100, value.to_i32().ok_or(OUT_OF_RANGE)?)
set_if_consistent(
&mut self.isoyear_mod_100,
i32::try_from(value).map_err(|_| OUT_OF_RANGE)?,
)
}
/// Tries to set the [`month`](#structfield.month) field from given value.
#[inline]
pub fn set_month(&mut self, value: i64) -> ParseResult<()> {
set_if_consistent(&mut self.month, value.to_u32().ok_or(OUT_OF_RANGE)?)
set_if_consistent(&mut self.month, u32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
}
/// Tries to set the [`week_from_sun`](#structfield.week_from_sun) field from given value.
#[inline]
pub fn set_week_from_sun(&mut self, value: i64) -> ParseResult<()> {
set_if_consistent(&mut self.week_from_sun, value.to_u32().ok_or(OUT_OF_RANGE)?)
set_if_consistent(&mut self.week_from_sun, u32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
}
/// Tries to set the [`week_from_mon`](#structfield.week_from_mon) field from given value.
#[inline]
pub fn set_week_from_mon(&mut self, value: i64) -> ParseResult<()> {
set_if_consistent(&mut self.week_from_mon, value.to_u32().ok_or(OUT_OF_RANGE)?)
set_if_consistent(&mut self.week_from_mon, u32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
}
/// Tries to set the [`isoweek`](#structfield.isoweek) field from given value.
#[inline]
pub fn set_isoweek(&mut self, value: i64) -> ParseResult<()> {
set_if_consistent(&mut self.isoweek, value.to_u32().ok_or(OUT_OF_RANGE)?)
set_if_consistent(&mut self.isoweek, u32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
}
/// Tries to set the [`weekday`](#structfield.weekday) field from given value.
@ -214,13 +220,13 @@ impl Parsed {
/// Tries to set the [`ordinal`](#structfield.ordinal) field from given value.
#[inline]
pub fn set_ordinal(&mut self, value: i64) -> ParseResult<()> {
set_if_consistent(&mut self.ordinal, value.to_u32().ok_or(OUT_OF_RANGE)?)
set_if_consistent(&mut self.ordinal, u32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
}
/// Tries to set the [`day`](#structfield.day) field from given value.
#[inline]
pub fn set_day(&mut self, value: i64) -> ParseResult<()> {
set_if_consistent(&mut self.day, value.to_u32().ok_or(OUT_OF_RANGE)?)
set_if_consistent(&mut self.day, u32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
}
/// Tries to set the [`hour_div_12`](#structfield.hour_div_12) field from given value.
@ -244,7 +250,7 @@ impl Parsed {
/// [`hour_mod_12`](#structfield.hour_mod_12) fields from given value.
#[inline]
pub fn set_hour(&mut self, value: i64) -> ParseResult<()> {
let v = value.to_u32().ok_or(OUT_OF_RANGE)?;
let v = u32::try_from(value).map_err(|_| OUT_OF_RANGE)?;
set_if_consistent(&mut self.hour_div_12, v / 12)?;
set_if_consistent(&mut self.hour_mod_12, v % 12)?;
Ok(())
@ -253,19 +259,19 @@ impl Parsed {
/// Tries to set the [`minute`](#structfield.minute) field from given value.
#[inline]
pub fn set_minute(&mut self, value: i64) -> ParseResult<()> {
set_if_consistent(&mut self.minute, value.to_u32().ok_or(OUT_OF_RANGE)?)
set_if_consistent(&mut self.minute, u32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
}
/// Tries to set the [`second`](#structfield.second) field from given value.
#[inline]
pub fn set_second(&mut self, value: i64) -> ParseResult<()> {
set_if_consistent(&mut self.second, value.to_u32().ok_or(OUT_OF_RANGE)?)
set_if_consistent(&mut self.second, u32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
}
/// Tries to set the [`nanosecond`](#structfield.nanosecond) field from given value.
#[inline]
pub fn set_nanosecond(&mut self, value: i64) -> ParseResult<()> {
set_if_consistent(&mut self.nanosecond, value.to_u32().ok_or(OUT_OF_RANGE)?)
set_if_consistent(&mut self.nanosecond, u32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
}
/// Tries to set the [`timestamp`](#structfield.timestamp) field from given value.
@ -277,7 +283,7 @@ impl Parsed {
/// Tries to set the [`offset`](#structfield.offset) field from given value.
#[inline]
pub fn set_offset(&mut self, value: i64) -> ParseResult<()> {
set_if_consistent(&mut self.offset, value.to_i32().ok_or(OUT_OF_RANGE)?)
set_if_consistent(&mut self.offset, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?)
}
/// Returns a parsed naive date out of given fields.

View File

@ -422,6 +422,8 @@ mod oldtime;
// this reexport is to aid the transition and should not be in the prelude!
pub use oldtime::{Duration, OutOfRangeError};
use core::fmt;
#[cfg(feature = "__doctest")]
#[cfg_attr(feature = "__doctest", cfg(doctest))]
use doc_comment::doctest;
@ -517,3 +519,30 @@ pub use naive::__BenchYearFlags;
pub mod serde {
pub use super::datetime::serde::*;
}
/// Out of range error type used in various converting APIs
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub struct OutOfRange {
_private: (),
}
impl OutOfRange {
const fn new() -> OutOfRange {
OutOfRange { _private: () }
}
}
impl fmt::Display for OutOfRange {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "out of range")
}
}
impl fmt::Debug for OutOfRange {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "out of range")
}
}
#[cfg(feature = "std")]
impl std::error::Error for OutOfRange {}

View File

@ -1,8 +1,10 @@
use core::fmt;
use core::{convert::TryFrom, fmt};
#[cfg(feature = "rkyv")]
use rkyv::{Archive, Deserialize, Serialize};
use crate::OutOfRange;
/// The month of the year.
///
/// This enum is just a convenience implementation.
@ -10,11 +12,11 @@ use rkyv::{Archive, Deserialize, Serialize};
///
/// It is possible to convert from a date to a month independently
/// ```
/// use num_traits::FromPrimitive;
/// # use std::convert::TryFrom;
/// use chrono::prelude::*;
/// let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap();
/// // `2019-10-28T09:10:11Z`
/// let month = Month::from_u32(date.month());
/// let month = Month::try_from(u8::try_from(date.month()).unwrap()).ok();
/// assert_eq!(month, Some(Month::October))
/// ```
/// Or from a Month to an integer usable by dates
@ -157,6 +159,28 @@ impl Month {
}
}
impl TryFrom<u8> for Month {
type Error = OutOfRange;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
1 => Ok(Month::January),
2 => Ok(Month::February),
3 => Ok(Month::March),
4 => Ok(Month::April),
5 => Ok(Month::May),
6 => Ok(Month::June),
7 => Ok(Month::July),
8 => Ok(Month::August),
9 => Ok(Month::September),
10 => Ok(Month::October),
11 => Ok(Month::November),
12 => Ok(Month::December),
_ => Err(OutOfRange::new()),
}
}
}
impl num_traits::FromPrimitive for Month {
/// Returns an `Option<Month>` from a i64, assuming a 1-index, January = 1.
///
@ -325,8 +349,25 @@ mod month_serde {
#[cfg(test)]
mod tests {
use core::convert::TryFrom;
use super::Month;
use crate::{Datelike, TimeZone, Utc};
use crate::{Datelike, OutOfRange, TimeZone, Utc};
#[test]
fn test_month_enum_try_from() {
assert_eq!(Month::try_from(1), Ok(Month::January));
assert_eq!(Month::try_from(2), Ok(Month::February));
assert_eq!(Month::try_from(12), Ok(Month::December));
assert_eq!(Month::try_from(13), Err(OutOfRange::new()));
let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap();
assert_eq!(Month::try_from(date.month() as u8), Ok(Month::October));
let month = Month::January;
let dt = Utc.with_ymd_and_hms(2019, month.number_from_month(), 28, 9, 10, 11).unwrap();
assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28));
}
#[test]
fn test_month_enum_primitive_parse() {

View File

@ -9,7 +9,6 @@ use core::convert::TryFrom;
use core::ops::{Add, AddAssign, RangeInclusive, Sub, SubAssign};
use core::{fmt, str};
use num_traits::ToPrimitive;
#[cfg(feature = "rkyv")]
use rkyv::{Archive, Deserialize, Serialize};
@ -1030,7 +1029,7 @@ impl NaiveDate {
let year = self.year();
let (mut year_div_400, year_mod_400) = div_mod_floor(year, 400);
let cycle = internals::yo_to_cycle(year_mod_400 as u32, self.of().ordinal());
let cycle = (cycle as i32).checked_add(rhs.num_days().to_i32()?)?;
let cycle = (cycle as i32).checked_add(i32::try_from(rhs.num_days()).ok()?)?;
let (cycle_div_400y, cycle) = div_mod_floor(cycle, 146_097);
year_div_400 += cycle_div_400y;
@ -1062,7 +1061,7 @@ impl NaiveDate {
let year = self.year();
let (mut year_div_400, year_mod_400) = div_mod_floor(year, 400);
let cycle = internals::yo_to_cycle(year_mod_400 as u32, self.of().ordinal());
let cycle = (cycle as i32).checked_sub(rhs.num_days().to_i32()?)?;
let cycle = (cycle as i32).checked_sub(i32::try_from(rhs.num_days()).ok()?)?;
let (cycle_div_400y, cycle) = div_mod_floor(cycle, 146_097);
year_div_400 += cycle_div_400y;

View File

@ -5,11 +5,11 @@
#[cfg(any(feature = "alloc", feature = "std", test))]
use core::borrow::Borrow;
use core::convert::TryFrom;
use core::fmt::Write;
use core::ops::{Add, AddAssign, Sub, SubAssign};
use core::{fmt, str};
use num_traits::ToPrimitive;
#[cfg(feature = "rkyv")]
use rkyv::{Archive, Deserialize, Serialize};
@ -211,8 +211,8 @@ impl NaiveDateTime {
pub fn from_timestamp_opt(secs: i64, nsecs: u32) -> Option<NaiveDateTime> {
let days = secs.div_euclid(86_400);
let secs = secs.rem_euclid(86_400);
let date = days
.to_i32()
let date = i32::try_from(days)
.ok()
.and_then(|days| days.checked_add(719_163))
.and_then(NaiveDate::from_num_days_from_ce_opt);
let time = NaiveTime::from_num_seconds_from_midnight_opt(secs as u32, nsecs);

View File

@ -16,8 +16,8 @@
#![cfg_attr(feature = "__internal_bench", allow(missing_docs))]
use crate::Weekday;
use core::convert::TryFrom;
use core::{fmt, i32};
use num_traits::FromPrimitive;
/// The internal date representation. This also includes the packed `Mdf` value.
pub(super) type DateImpl = i32;
@ -319,7 +319,7 @@ impl Of {
#[inline]
pub(super) fn weekday(&self) -> Weekday {
let Of(of) = *self;
Weekday::from_u32(((of >> 4) + (of & 0b111)) % 7).unwrap()
Weekday::try_from((((of >> 4) + (of & 0b111)) % 7) as u8).unwrap()
}
#[inline]
@ -327,7 +327,7 @@ impl Of {
// week ordinal = ordinal + delta
let Of(of) = *self;
let weekord = (of >> 4).wrapping_add(self.flags().isoweek_delta());
(weekord / 7, Weekday::from_u32(weekord % 7).unwrap())
(weekord / 7, Weekday::try_from((weekord % 7) as u8).unwrap())
}
#[cfg_attr(feature = "cargo-clippy", allow(clippy::wrong_self_convention))]

View File

@ -1,8 +1,10 @@
use core::fmt;
use core::{convert::TryFrom, fmt};
#[cfg(feature = "rkyv")]
use rkyv::{Archive, Deserialize, Serialize};
use crate::OutOfRange;
/// The day of week.
///
/// The order of the days of week depends on the context.
@ -12,12 +14,12 @@ use rkyv::{Archive, Deserialize, Serialize};
/// # Example
/// ```
/// use chrono::Weekday;
/// use num_traits::cast::FromPrimitive;
/// use std::convert::TryFrom;
///
/// let monday = "Monday".parse::<Weekday>().unwrap();
/// assert_eq!(monday, Weekday::Mon);
///
/// let sunday = Weekday::from_u8(6).unwrap();
/// let sunday = Weekday::try_from(6).unwrap();
/// assert_eq!(sunday, Weekday::Sun);
///
/// assert_eq!(sunday.num_days_from_monday(), 6); // starts counting with Monday = 0
@ -153,6 +155,26 @@ impl fmt::Display for Weekday {
}
}
/// Any weekday can be represented as an integer from 0 to 6, which equals to
/// [`Weekday::num_days_from_monday`](#method.num_days_from_monday) in this implementation.
/// Do not heavily depend on this though; use explicit methods whenever possible.
impl TryFrom<u8> for Weekday {
type Error = OutOfRange;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(Weekday::Mon),
1 => Ok(Weekday::Tue),
2 => Ok(Weekday::Wed),
3 => Ok(Weekday::Thu),
4 => Ok(Weekday::Fri),
5 => Ok(Weekday::Sat),
6 => Ok(Weekday::Sun),
_ => Err(OutOfRange::new()),
}
}
}
/// Any weekday can be represented as an integer from 0 to 6, which equals to
/// [`Weekday::num_days_from_monday`](#method.num_days_from_monday) in this implementation.
/// Do not heavily depend on this though; use explicit methods whenever possible.
@ -210,14 +232,13 @@ impl fmt::Debug for ParseWeekdayError {
#[cfg(test)]
mod tests {
use num_traits::FromPrimitive;
use super::Weekday;
use std::convert::TryFrom;
#[test]
fn test_num_days_from() {
for i in 0..7 {
let base_day = Weekday::from_u64(i).unwrap();
let base_day = Weekday::try_from(i).unwrap();
assert_eq!(base_day.num_days_from_monday(), base_day.num_days_from(Weekday::Mon));
assert_eq!(base_day.num_days_from_sunday(), base_day.num_days_from(Weekday::Sun));