mirror of
https://github.com/BurntSushi/jiff.git
synced 2025-12-23 08:47:45 +00:00
shared: use structured errors for shared/itime
This commit is contained in:
parent
3a832162be
commit
973876a9f1
13 changed files with 394 additions and 175 deletions
|
|
@ -79,7 +79,7 @@ fn copy(srcdir: &Path, dstdir: &Path, dir: &Path) -> anyhow::Result<()> {
|
|||
fn copy_rust_source_file(src: &Path, dst: &Path) -> anyhow::Result<()> {
|
||||
let code = fs::read_to_string(src)
|
||||
.with_context(|| format!("failed to read {}", src.display()))?;
|
||||
let code = remove_only_jiffs(&remove_cfg_alloc(&code));
|
||||
let code = remove_only_jiffs(&remove_cfg_alloc_or_std(&code));
|
||||
|
||||
let mut out = String::new();
|
||||
writeln!(out, "// auto-generated by: jiff-cli generate shared")?;
|
||||
|
|
@ -105,13 +105,13 @@ fn remove_only_jiffs(code: &str) -> String {
|
|||
RE.replace_all(code, "").into_owned()
|
||||
}
|
||||
|
||||
/// Removes all `#[cfg(feature = "alloc")]` gates.
|
||||
/// Removes all `#[cfg(feature = "alloc|std")]` gates.
|
||||
///
|
||||
/// This is because the proc-macro always runs in a context where `alloc`
|
||||
/// (and `std`) are enabled.
|
||||
fn remove_cfg_alloc(code: &str) -> String {
|
||||
fn remove_cfg_alloc_or_std(code: &str) -> String {
|
||||
static RE: LazyLock<Regex> = LazyLock::new(|| {
|
||||
Regex::new(r###"#\[cfg\(feature = "alloc"\)\]\n"###).unwrap()
|
||||
Regex::new(r###"#\[cfg\(feature = "(alloc|std)"\)\]\n"###).unwrap()
|
||||
});
|
||||
RE.replace_all(code, "").into_owned()
|
||||
}
|
||||
|
|
|
|||
100
crates/jiff-static/src/shared/error/itime.rs
Normal file
100
crates/jiff-static/src/shared/error/itime.rs
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
// auto-generated by: jiff-cli generate shared
|
||||
|
||||
use crate::shared::{
|
||||
error,
|
||||
util::itime::{days_in_month, days_in_year, IEpochDay},
|
||||
};
|
||||
|
||||
// N.B. Every variant in this error type is a range error.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub(crate) enum Error {
|
||||
DateInvalidDayOfYear { year: i16 },
|
||||
DateInvalidDayOfYearNoLeap,
|
||||
DateInvalidDays { year: i16, month: i8 },
|
||||
DateTimeSeconds,
|
||||
// TODO: I believe this can never happen.
|
||||
DayOfYear,
|
||||
EpochDayDays,
|
||||
EpochDayI32,
|
||||
NthWeekdayOfMonth,
|
||||
Tomorrow,
|
||||
YearNext,
|
||||
YearPrevious,
|
||||
Yesterday,
|
||||
}
|
||||
|
||||
impl From<Error> for error::Error {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn from(err: Error) -> error::Error {
|
||||
error::ErrorKind::Time(err).into()
|
||||
}
|
||||
}
|
||||
|
||||
// impl error::IntoError for Error {
|
||||
// fn into_error(self) -> error::Error {
|
||||
// self.into()
|
||||
// }
|
||||
// }
|
||||
|
||||
impl core::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
use self::Error::*;
|
||||
|
||||
match *self {
|
||||
DateInvalidDayOfYear { year } => write!(
|
||||
f,
|
||||
"number of days for `{year:04}` is invalid, \
|
||||
must be in range `1..={max_day}`",
|
||||
max_day = days_in_year(year),
|
||||
),
|
||||
DateInvalidDayOfYearNoLeap => f.write_str(
|
||||
"number of days is invalid, must be in range `1..=365`",
|
||||
),
|
||||
DateInvalidDays { year, month } => write!(
|
||||
f,
|
||||
"number of days for `{year:04}-{month:02}` is invalid, \
|
||||
must be in range `1..={max_day}`",
|
||||
max_day = days_in_month(year, month),
|
||||
),
|
||||
DateTimeSeconds => {
|
||||
f.write_str("adding seconds to datetime overflowed")
|
||||
}
|
||||
DayOfYear => f.write_str("day of year is invalid"),
|
||||
EpochDayDays => write!(
|
||||
f,
|
||||
"adding to epoch day resulted in a value outside \
|
||||
the allowed range of `{min}..={max}`",
|
||||
min = IEpochDay::MIN.epoch_day,
|
||||
max = IEpochDay::MAX.epoch_day,
|
||||
),
|
||||
EpochDayI32 => f.write_str(
|
||||
"adding to epoch day overflowed 32-bit signed integer",
|
||||
),
|
||||
NthWeekdayOfMonth => f.write_str(
|
||||
"invalid nth weekday of month, \
|
||||
must be non-zero and in range `-5..=5`",
|
||||
),
|
||||
Tomorrow => f.write_str(
|
||||
"returning tomorrow for `9999-12-31` is not \
|
||||
possible because it is greater than Jiff's supported
|
||||
maximum date",
|
||||
),
|
||||
YearNext => f.write_str(
|
||||
"creating a date for a year following `9999` is \
|
||||
not possible because it is greater than Jiff's supported \
|
||||
maximum date",
|
||||
),
|
||||
YearPrevious => f.write_str(
|
||||
"creating a date for a year preceding `-9999` is \
|
||||
not possible because it is less than Jiff's supported \
|
||||
minimum date",
|
||||
),
|
||||
Yesterday => f.write_str(
|
||||
"returning yesterday for `-9999-01-01` is not \
|
||||
possible because it is less than Jiff's supported
|
||||
minimum date",
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
57
crates/jiff-static/src/shared/error/mod.rs
Normal file
57
crates/jiff-static/src/shared/error/mod.rs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
// auto-generated by: jiff-cli generate shared
|
||||
|
||||
pub(crate) mod itime;
|
||||
|
||||
/// An error scoped to Jiff's `shared` module.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Error {
|
||||
kind: ErrorKind,
|
||||
}
|
||||
|
||||
impl core::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
self.kind.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
enum ErrorKind {
|
||||
Time(self::itime::Error),
|
||||
}
|
||||
|
||||
impl From<ErrorKind> for Error {
|
||||
fn from(kind: ErrorKind) -> Error {
|
||||
Error { kind }
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Display for ErrorKind {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
match *self {
|
||||
ErrorKind::Time(ref err) => err.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/// A slim error that occurs when an input value is out of bounds.
|
||||
#[derive(Clone, Debug)]
|
||||
struct SlimRangeError {
|
||||
what: &'static str,
|
||||
}
|
||||
|
||||
impl SlimRangeError {
|
||||
fn new(what: &'static str) -> SlimRangeError {
|
||||
SlimRangeError { what }
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for SlimRangeError {}
|
||||
|
||||
impl core::fmt::Display for SlimRangeError {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
let SlimRangeError { what } = *self;
|
||||
write!(f, "parameter '{what}' is not in the required range")
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
@ -474,6 +474,7 @@ pub struct PosixOffset {
|
|||
|
||||
// Does not require `alloc`, but is only used when `alloc` is enabled.
|
||||
pub(crate) mod crc32;
|
||||
pub(crate) mod error;
|
||||
pub(crate) mod posix;
|
||||
pub(crate) mod tzif;
|
||||
pub(crate) mod util;
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ they are internal types. Specifically, to distinguish them from Jiff's public
|
|||
types. For example, `Date` versus `IDate`.
|
||||
*/
|
||||
|
||||
use super::error::{err, Error};
|
||||
use crate::shared::error::{itime::Error as E, Error};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub(crate) struct ITimestamp {
|
||||
|
|
@ -144,10 +144,12 @@ impl IDateTime {
|
|||
&self,
|
||||
seconds: i32,
|
||||
) -> Result<IDateTime, Error> {
|
||||
let day_second =
|
||||
self.time.to_second().second.checked_add(seconds).ok_or_else(
|
||||
|| err!("adding `{seconds}s` to datetime overflowed"),
|
||||
)?;
|
||||
let day_second = self
|
||||
.time
|
||||
.to_second()
|
||||
.second
|
||||
.checked_add(seconds)
|
||||
.ok_or_else(|| Error::from(E::DateTimeSeconds))?;
|
||||
let days = day_second.div_euclid(86400);
|
||||
let second = day_second.rem_euclid(86400);
|
||||
let date = self.date.checked_add_days(days)?;
|
||||
|
|
@ -162,8 +164,8 @@ pub(crate) struct IEpochDay {
|
|||
}
|
||||
|
||||
impl IEpochDay {
|
||||
const MIN: IEpochDay = IEpochDay { epoch_day: -4371587 };
|
||||
const MAX: IEpochDay = IEpochDay { epoch_day: 2932896 };
|
||||
pub(crate) const MIN: IEpochDay = IEpochDay { epoch_day: -4371587 };
|
||||
pub(crate) const MAX: IEpochDay = IEpochDay { epoch_day: 2932896 };
|
||||
|
||||
/// Converts days since the Unix epoch to a Gregorian date.
|
||||
///
|
||||
|
|
@ -221,18 +223,12 @@ impl IEpochDay {
|
|||
#[inline]
|
||||
pub(crate) fn checked_add(&self, amount: i32) -> Result<IEpochDay, Error> {
|
||||
let epoch_day = self.epoch_day;
|
||||
let sum = epoch_day.checked_add(amount).ok_or_else(|| {
|
||||
err!("adding `{amount}` to epoch day `{epoch_day}` overflowed i32")
|
||||
})?;
|
||||
let sum = epoch_day
|
||||
.checked_add(amount)
|
||||
.ok_or_else(|| Error::from(E::EpochDayI32))?;
|
||||
let ret = IEpochDay { epoch_day: sum };
|
||||
if !(IEpochDay::MIN <= ret && ret <= IEpochDay::MAX) {
|
||||
return Err(err!(
|
||||
"adding `{amount}` to epoch day `{epoch_day}` \
|
||||
resulted in `{sum}`, which is not in the required \
|
||||
epoch day range of `{min}..={max}`",
|
||||
min = IEpochDay::MIN.epoch_day,
|
||||
max = IEpochDay::MAX.epoch_day,
|
||||
));
|
||||
return Err(Error::from(E::EpochDayDays));
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
|
|
@ -264,10 +260,7 @@ impl IDate {
|
|||
if day > 28 {
|
||||
let max_day = days_in_month(year, month);
|
||||
if day > max_day {
|
||||
return Err(err!(
|
||||
"day={day} is out of range for year={year} \
|
||||
and month={month}, must be in range 1..={max_day}",
|
||||
));
|
||||
return Err(Error::from(E::DateInvalidDays { year, month }));
|
||||
}
|
||||
}
|
||||
Ok(IDate { year, month, day })
|
||||
|
|
@ -285,35 +278,19 @@ impl IDate {
|
|||
day: i16,
|
||||
) -> Result<IDate, Error> {
|
||||
if !(1 <= day && day <= 366) {
|
||||
return Err(err!(
|
||||
"day-of-year={day} is out of range for year={year}, \
|
||||
must be in range 1..={max_day}",
|
||||
max_day = days_in_year(year),
|
||||
));
|
||||
return Err(Error::from(E::DateInvalidDayOfYear { year }));
|
||||
}
|
||||
let start = IDate { year, month: 1, day: 1 }.to_epoch_day();
|
||||
let end = start
|
||||
.checked_add(i32::from(day) - 1)
|
||||
.map_err(|_| {
|
||||
err!(
|
||||
"failed to find date for \
|
||||
year={year} and day-of-year={day}: \
|
||||
adding `{day}` to `{start}` overflows \
|
||||
Jiff's range",
|
||||
start = start.epoch_day,
|
||||
)
|
||||
})?
|
||||
.map_err(|_| Error::from(E::DayOfYear))?
|
||||
.to_date();
|
||||
// If we overflowed into the next year, then `day` is too big.
|
||||
if year != end.year {
|
||||
// Can only happen given day=366 and this is a leap year.
|
||||
debug_assert_eq!(day, 366);
|
||||
debug_assert!(!is_leap_year(year));
|
||||
return Err(err!(
|
||||
"day-of-year={day} is out of range for year={year}, \
|
||||
must be in range 1..={max_day}",
|
||||
max_day = days_in_year(year),
|
||||
));
|
||||
return Err(Error::from(E::DateInvalidDayOfYear { year }));
|
||||
}
|
||||
Ok(end)
|
||||
}
|
||||
|
|
@ -331,10 +308,7 @@ impl IDate {
|
|||
mut day: i16,
|
||||
) -> Result<IDate, Error> {
|
||||
if !(1 <= day && day <= 365) {
|
||||
return Err(err!(
|
||||
"day-of-year={day} is out of range for year={year}, \
|
||||
must be in range 1..=365",
|
||||
));
|
||||
return Err(Error::from(E::DateInvalidDayOfYearNoLeap));
|
||||
}
|
||||
if day >= 60 && is_leap_year(year) {
|
||||
day += 1;
|
||||
|
|
@ -394,10 +368,7 @@ impl IDate {
|
|||
weekday: IWeekday,
|
||||
) -> Result<IDate, Error> {
|
||||
if nth == 0 || !(-5 <= nth && nth <= 5) {
|
||||
return Err(err!(
|
||||
"got nth weekday of `{nth}`, but \
|
||||
must be non-zero and in range `-5..=5`",
|
||||
));
|
||||
return Err(Error::from(E::NthWeekdayOfMonth));
|
||||
}
|
||||
if nth > 0 {
|
||||
let first_weekday = self.first_of_month().weekday();
|
||||
|
|
@ -414,13 +385,10 @@ impl IDate {
|
|||
// of `Day`, we can't let this boundary condition escape. So we
|
||||
// check it here.
|
||||
if day < 1 {
|
||||
return Err(err!(
|
||||
"day={day} is out of range for year={year} \
|
||||
and month={month}, must be in range 1..={max_day}",
|
||||
year = self.year,
|
||||
month = self.month,
|
||||
max_day = days_in_month(self.year, self.month),
|
||||
));
|
||||
return Err(Error::from(E::DateInvalidDays {
|
||||
year: self.year,
|
||||
month: self.month,
|
||||
}));
|
||||
}
|
||||
IDate::try_new(self.year, self.month, day)
|
||||
}
|
||||
|
|
@ -433,11 +401,7 @@ impl IDate {
|
|||
if self.month == 1 {
|
||||
let year = self.year - 1;
|
||||
if year <= -10000 {
|
||||
return Err(err!(
|
||||
"returning yesterday for -9999-01-01 is not \
|
||||
possible because it is less than Jiff's supported
|
||||
minimum date",
|
||||
));
|
||||
return Err(Error::from(E::Yesterday));
|
||||
}
|
||||
return Ok(IDate { year, month: 12, day: 31 });
|
||||
}
|
||||
|
|
@ -455,11 +419,7 @@ impl IDate {
|
|||
if self.month == 12 {
|
||||
let year = self.year + 1;
|
||||
if year >= 10000 {
|
||||
return Err(err!(
|
||||
"returning tomorrow for 9999-12-31 is not \
|
||||
possible because it is greater than Jiff's supported
|
||||
maximum date",
|
||||
));
|
||||
return Err(Error::from(E::Tomorrow));
|
||||
}
|
||||
return Ok(IDate { year, month: 1, day: 1 });
|
||||
}
|
||||
|
|
@ -474,14 +434,7 @@ impl IDate {
|
|||
pub(crate) fn prev_year(self) -> Result<i16, Error> {
|
||||
let year = self.year - 1;
|
||||
if year <= -10_000 {
|
||||
return Err(err!(
|
||||
"returning previous year for {year:04}-{month:02}-{day:02} is \
|
||||
not possible because it is less than Jiff's supported \
|
||||
minimum date",
|
||||
year = self.year,
|
||||
month = self.month,
|
||||
day = self.day,
|
||||
));
|
||||
return Err(Error::from(E::YearPrevious));
|
||||
}
|
||||
Ok(year)
|
||||
}
|
||||
|
|
@ -491,14 +444,7 @@ impl IDate {
|
|||
pub(crate) fn next_year(self) -> Result<i16, Error> {
|
||||
let year = self.year + 1;
|
||||
if year >= 10_000 {
|
||||
return Err(err!(
|
||||
"returning next year for {year:04}-{month:02}-{day:02} is \
|
||||
not possible because it is greater than Jiff's supported \
|
||||
maximum date",
|
||||
year = self.year,
|
||||
month = self.month,
|
||||
day = self.day,
|
||||
));
|
||||
return Err(Error::from(E::YearNext));
|
||||
}
|
||||
Ok(year)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -903,7 +903,9 @@ impl Date {
|
|||
let weekday = weekday.to_iweekday();
|
||||
let idate = self.to_idate_const();
|
||||
Ok(Date::from_idate_const(
|
||||
idate.nth_weekday_of_month(nth, weekday).map_err(Error::shared)?,
|
||||
idate
|
||||
.nth_weekday_of_month(nth, weekday)
|
||||
.map_err(Error::shared2)?,
|
||||
))
|
||||
}
|
||||
|
||||
|
|
@ -3189,13 +3191,13 @@ impl DateWith {
|
|||
Some(DateWithDay::OfYear(day)) => {
|
||||
let year = year.get_unchecked();
|
||||
let idate = IDate::from_day_of_year(year, day)
|
||||
.map_err(Error::shared)?;
|
||||
.map_err(Error::shared2)?;
|
||||
return Ok(Date::from_idate_const(idate));
|
||||
}
|
||||
Some(DateWithDay::OfYearNoLeap(day)) => {
|
||||
let year = year.get_unchecked();
|
||||
let idate = IDate::from_day_of_year_no_leap(year, day)
|
||||
.map_err(Error::shared)?;
|
||||
.map_err(Error::shared2)?;
|
||||
return Ok(Date::from_idate_const(idate));
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
use crate::{shared::util::error::Error as SharedError, util::sync::Arc};
|
||||
use crate::{
|
||||
shared::{
|
||||
error::Error as SharedError2, util::error::Error as SharedError,
|
||||
},
|
||||
util::sync::Arc,
|
||||
};
|
||||
|
||||
pub(crate) mod civil;
|
||||
pub(crate) mod duration;
|
||||
|
|
@ -151,6 +156,11 @@ impl Error {
|
|||
Error::from(ErrorKind::Shared(err))
|
||||
}
|
||||
|
||||
/// Creates a new error from the special "shared" error type.
|
||||
pub(crate) fn shared2(err: SharedError2) -> Error {
|
||||
Error::from(ErrorKind::Shared2(err))
|
||||
}
|
||||
|
||||
/// A convenience constructor for building an I/O error.
|
||||
///
|
||||
/// This returns an error that is just a simple wrapper around the
|
||||
|
|
@ -281,6 +291,7 @@ enum ErrorKind {
|
|||
Range(RangeError),
|
||||
RoundingIncrement(self::util::RoundingIncrementError),
|
||||
Shared(SharedError),
|
||||
Shared2(SharedError2),
|
||||
SignedDuration(self::signed_duration::Error),
|
||||
SlimRange(SlimRangeError),
|
||||
Span(self::span::Error),
|
||||
|
|
@ -324,6 +335,7 @@ impl core::fmt::Display for ErrorKind {
|
|||
Range(ref err) => err.fmt(f),
|
||||
RoundingIncrement(ref err) => err.fmt(f),
|
||||
Shared(ref err) => err.fmt(f),
|
||||
Shared2(ref err) => err.fmt(f),
|
||||
SignedDuration(ref err) => err.fmt(f),
|
||||
SlimRange(ref err) => err.fmt(f),
|
||||
Span(ref err) => err.fmt(f),
|
||||
|
|
|
|||
|
|
@ -2079,8 +2079,8 @@ impl BrokenDownTime {
|
|||
/// // An error only occurs when you try to extract a date:
|
||||
/// assert_eq!(
|
||||
/// tm.to_date().unwrap_err().to_string(),
|
||||
/// "invalid date: day-of-year=366 is out of range \
|
||||
/// for year=2023, must be in range 1..=365",
|
||||
/// "invalid date: number of days for `2023` is invalid, \
|
||||
/// must be in range `1..=365`",
|
||||
/// );
|
||||
/// // But parsing a value that is always illegal will
|
||||
/// // result in an error:
|
||||
|
|
|
|||
98
src/shared/error/itime.rs
Normal file
98
src/shared/error/itime.rs
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
use crate::shared::{
|
||||
error,
|
||||
util::itime::{days_in_month, days_in_year, IEpochDay},
|
||||
};
|
||||
|
||||
// N.B. Every variant in this error type is a range error.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub(crate) enum Error {
|
||||
DateInvalidDayOfYear { year: i16 },
|
||||
DateInvalidDayOfYearNoLeap,
|
||||
DateInvalidDays { year: i16, month: i8 },
|
||||
DateTimeSeconds,
|
||||
// TODO: I believe this can never happen.
|
||||
DayOfYear,
|
||||
EpochDayDays,
|
||||
EpochDayI32,
|
||||
NthWeekdayOfMonth,
|
||||
Tomorrow,
|
||||
YearNext,
|
||||
YearPrevious,
|
||||
Yesterday,
|
||||
}
|
||||
|
||||
impl From<Error> for error::Error {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn from(err: Error) -> error::Error {
|
||||
error::ErrorKind::Time(err).into()
|
||||
}
|
||||
}
|
||||
|
||||
// impl error::IntoError for Error {
|
||||
// fn into_error(self) -> error::Error {
|
||||
// self.into()
|
||||
// }
|
||||
// }
|
||||
|
||||
impl core::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
use self::Error::*;
|
||||
|
||||
match *self {
|
||||
DateInvalidDayOfYear { year } => write!(
|
||||
f,
|
||||
"number of days for `{year:04}` is invalid, \
|
||||
must be in range `1..={max_day}`",
|
||||
max_day = days_in_year(year),
|
||||
),
|
||||
DateInvalidDayOfYearNoLeap => f.write_str(
|
||||
"number of days is invalid, must be in range `1..=365`",
|
||||
),
|
||||
DateInvalidDays { year, month } => write!(
|
||||
f,
|
||||
"number of days for `{year:04}-{month:02}` is invalid, \
|
||||
must be in range `1..={max_day}`",
|
||||
max_day = days_in_month(year, month),
|
||||
),
|
||||
DateTimeSeconds => {
|
||||
f.write_str("adding seconds to datetime overflowed")
|
||||
}
|
||||
DayOfYear => f.write_str("day of year is invalid"),
|
||||
EpochDayDays => write!(
|
||||
f,
|
||||
"adding to epoch day resulted in a value outside \
|
||||
the allowed range of `{min}..={max}`",
|
||||
min = IEpochDay::MIN.epoch_day,
|
||||
max = IEpochDay::MAX.epoch_day,
|
||||
),
|
||||
EpochDayI32 => f.write_str(
|
||||
"adding to epoch day overflowed 32-bit signed integer",
|
||||
),
|
||||
NthWeekdayOfMonth => f.write_str(
|
||||
"invalid nth weekday of month, \
|
||||
must be non-zero and in range `-5..=5`",
|
||||
),
|
||||
Tomorrow => f.write_str(
|
||||
"returning tomorrow for `9999-12-31` is not \
|
||||
possible because it is greater than Jiff's supported
|
||||
maximum date",
|
||||
),
|
||||
YearNext => f.write_str(
|
||||
"creating a date for a year following `9999` is \
|
||||
not possible because it is greater than Jiff's supported \
|
||||
maximum date",
|
||||
),
|
||||
YearPrevious => f.write_str(
|
||||
"creating a date for a year preceding `-9999` is \
|
||||
not possible because it is less than Jiff's supported \
|
||||
minimum date",
|
||||
),
|
||||
Yesterday => f.write_str(
|
||||
"returning yesterday for `-9999-01-01` is not \
|
||||
possible because it is less than Jiff's supported
|
||||
minimum date",
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
56
src/shared/error/mod.rs
Normal file
56
src/shared/error/mod.rs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
pub(crate) mod itime;
|
||||
|
||||
/// An error scoped to Jiff's `shared` module.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Error {
|
||||
kind: ErrorKind,
|
||||
}
|
||||
|
||||
impl core::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
self.kind.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
enum ErrorKind {
|
||||
Time(self::itime::Error),
|
||||
}
|
||||
|
||||
impl From<ErrorKind> for Error {
|
||||
fn from(kind: ErrorKind) -> Error {
|
||||
Error { kind }
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Display for ErrorKind {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
match *self {
|
||||
ErrorKind::Time(ref err) => err.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/// A slim error that occurs when an input value is out of bounds.
|
||||
#[derive(Clone, Debug)]
|
||||
struct SlimRangeError {
|
||||
what: &'static str,
|
||||
}
|
||||
|
||||
impl SlimRangeError {
|
||||
fn new(what: &'static str) -> SlimRangeError {
|
||||
SlimRangeError { what }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl std::error::Error for SlimRangeError {}
|
||||
|
||||
impl core::fmt::Display for SlimRangeError {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
let SlimRangeError { what } = *self;
|
||||
write!(f, "parameter '{what}' is not in the required range")
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
@ -502,6 +502,7 @@ impl PosixTimeZone<&'static str> {
|
|||
// Does not require `alloc`, but is only used when `alloc` is enabled.
|
||||
#[cfg(feature = "alloc")]
|
||||
pub(crate) mod crc32;
|
||||
pub(crate) mod error;
|
||||
pub(crate) mod posix;
|
||||
#[cfg(feature = "alloc")]
|
||||
pub(crate) mod tzif;
|
||||
|
|
|
|||
|
|
@ -230,8 +230,8 @@ impl TzifOwned {
|
|||
let clamped = timestamp.clamp(TIMESTAMP_MIN, TIMESTAMP_MAX);
|
||||
// only-jiff-start
|
||||
warn!(
|
||||
"found Unix timestamp {timestamp} that is outside \
|
||||
Jiff's supported range, clamping to {clamped}",
|
||||
"found Unix timestamp `{timestamp}` that is outside \
|
||||
Jiff's supported range, clamping to `{clamped}`",
|
||||
);
|
||||
// only-jiff-end
|
||||
timestamp = clamped;
|
||||
|
|
@ -378,7 +378,7 @@ impl TzifOwned {
|
|||
if !(TIMESTAMP_MIN <= occur && occur <= TIMESTAMP_MAX) {
|
||||
// only-jiff-start
|
||||
warn!(
|
||||
"leap second occurrence {occur} is \
|
||||
"leap second occurrence `{occur}` is \
|
||||
not in Jiff's supported range"
|
||||
)
|
||||
// only-jiff-end
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ they are internal types. Specifically, to distinguish them from Jiff's public
|
|||
types. For example, `Date` versus `IDate`.
|
||||
*/
|
||||
|
||||
use super::error::{err, Error};
|
||||
use crate::shared::error::{itime::Error as E, Error};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub(crate) struct ITimestamp {
|
||||
|
|
@ -142,10 +142,12 @@ impl IDateTime {
|
|||
&self,
|
||||
seconds: i32,
|
||||
) -> Result<IDateTime, Error> {
|
||||
let day_second =
|
||||
self.time.to_second().second.checked_add(seconds).ok_or_else(
|
||||
|| err!("adding `{seconds}s` to datetime overflowed"),
|
||||
)?;
|
||||
let day_second = self
|
||||
.time
|
||||
.to_second()
|
||||
.second
|
||||
.checked_add(seconds)
|
||||
.ok_or_else(|| Error::from(E::DateTimeSeconds))?;
|
||||
let days = day_second.div_euclid(86400);
|
||||
let second = day_second.rem_euclid(86400);
|
||||
let date = self.date.checked_add_days(days)?;
|
||||
|
|
@ -160,8 +162,8 @@ pub(crate) struct IEpochDay {
|
|||
}
|
||||
|
||||
impl IEpochDay {
|
||||
const MIN: IEpochDay = IEpochDay { epoch_day: -4371587 };
|
||||
const MAX: IEpochDay = IEpochDay { epoch_day: 2932896 };
|
||||
pub(crate) const MIN: IEpochDay = IEpochDay { epoch_day: -4371587 };
|
||||
pub(crate) const MAX: IEpochDay = IEpochDay { epoch_day: 2932896 };
|
||||
|
||||
/// Converts days since the Unix epoch to a Gregorian date.
|
||||
///
|
||||
|
|
@ -219,18 +221,12 @@ impl IEpochDay {
|
|||
#[inline]
|
||||
pub(crate) fn checked_add(&self, amount: i32) -> Result<IEpochDay, Error> {
|
||||
let epoch_day = self.epoch_day;
|
||||
let sum = epoch_day.checked_add(amount).ok_or_else(|| {
|
||||
err!("adding `{amount}` to epoch day `{epoch_day}` overflowed i32")
|
||||
})?;
|
||||
let sum = epoch_day
|
||||
.checked_add(amount)
|
||||
.ok_or_else(|| Error::from(E::EpochDayI32))?;
|
||||
let ret = IEpochDay { epoch_day: sum };
|
||||
if !(IEpochDay::MIN <= ret && ret <= IEpochDay::MAX) {
|
||||
return Err(err!(
|
||||
"adding `{amount}` to epoch day `{epoch_day}` \
|
||||
resulted in `{sum}`, which is not in the required \
|
||||
epoch day range of `{min}..={max}`",
|
||||
min = IEpochDay::MIN.epoch_day,
|
||||
max = IEpochDay::MAX.epoch_day,
|
||||
));
|
||||
return Err(Error::from(E::EpochDayDays));
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
|
|
@ -262,10 +258,7 @@ impl IDate {
|
|||
if day > 28 {
|
||||
let max_day = days_in_month(year, month);
|
||||
if day > max_day {
|
||||
return Err(err!(
|
||||
"day={day} is out of range for year={year} \
|
||||
and month={month}, must be in range 1..={max_day}",
|
||||
));
|
||||
return Err(Error::from(E::DateInvalidDays { year, month }));
|
||||
}
|
||||
}
|
||||
Ok(IDate { year, month, day })
|
||||
|
|
@ -283,35 +276,19 @@ impl IDate {
|
|||
day: i16,
|
||||
) -> Result<IDate, Error> {
|
||||
if !(1 <= day && day <= 366) {
|
||||
return Err(err!(
|
||||
"day-of-year={day} is out of range for year={year}, \
|
||||
must be in range 1..={max_day}",
|
||||
max_day = days_in_year(year),
|
||||
));
|
||||
return Err(Error::from(E::DateInvalidDayOfYear { year }));
|
||||
}
|
||||
let start = IDate { year, month: 1, day: 1 }.to_epoch_day();
|
||||
let end = start
|
||||
.checked_add(i32::from(day) - 1)
|
||||
.map_err(|_| {
|
||||
err!(
|
||||
"failed to find date for \
|
||||
year={year} and day-of-year={day}: \
|
||||
adding `{day}` to `{start}` overflows \
|
||||
Jiff's range",
|
||||
start = start.epoch_day,
|
||||
)
|
||||
})?
|
||||
.map_err(|_| Error::from(E::DayOfYear))?
|
||||
.to_date();
|
||||
// If we overflowed into the next year, then `day` is too big.
|
||||
if year != end.year {
|
||||
// Can only happen given day=366 and this is a leap year.
|
||||
debug_assert_eq!(day, 366);
|
||||
debug_assert!(!is_leap_year(year));
|
||||
return Err(err!(
|
||||
"day-of-year={day} is out of range for year={year}, \
|
||||
must be in range 1..={max_day}",
|
||||
max_day = days_in_year(year),
|
||||
));
|
||||
return Err(Error::from(E::DateInvalidDayOfYear { year }));
|
||||
}
|
||||
Ok(end)
|
||||
}
|
||||
|
|
@ -329,10 +306,7 @@ impl IDate {
|
|||
mut day: i16,
|
||||
) -> Result<IDate, Error> {
|
||||
if !(1 <= day && day <= 365) {
|
||||
return Err(err!(
|
||||
"day-of-year={day} is out of range for year={year}, \
|
||||
must be in range 1..=365",
|
||||
));
|
||||
return Err(Error::from(E::DateInvalidDayOfYearNoLeap));
|
||||
}
|
||||
if day >= 60 && is_leap_year(year) {
|
||||
day += 1;
|
||||
|
|
@ -392,10 +366,7 @@ impl IDate {
|
|||
weekday: IWeekday,
|
||||
) -> Result<IDate, Error> {
|
||||
if nth == 0 || !(-5 <= nth && nth <= 5) {
|
||||
return Err(err!(
|
||||
"got nth weekday of `{nth}`, but \
|
||||
must be non-zero and in range `-5..=5`",
|
||||
));
|
||||
return Err(Error::from(E::NthWeekdayOfMonth));
|
||||
}
|
||||
if nth > 0 {
|
||||
let first_weekday = self.first_of_month().weekday();
|
||||
|
|
@ -412,13 +383,10 @@ impl IDate {
|
|||
// of `Day`, we can't let this boundary condition escape. So we
|
||||
// check it here.
|
||||
if day < 1 {
|
||||
return Err(err!(
|
||||
"day={day} is out of range for year={year} \
|
||||
and month={month}, must be in range 1..={max_day}",
|
||||
year = self.year,
|
||||
month = self.month,
|
||||
max_day = days_in_month(self.year, self.month),
|
||||
));
|
||||
return Err(Error::from(E::DateInvalidDays {
|
||||
year: self.year,
|
||||
month: self.month,
|
||||
}));
|
||||
}
|
||||
IDate::try_new(self.year, self.month, day)
|
||||
}
|
||||
|
|
@ -431,11 +399,7 @@ impl IDate {
|
|||
if self.month == 1 {
|
||||
let year = self.year - 1;
|
||||
if year <= -10000 {
|
||||
return Err(err!(
|
||||
"returning yesterday for -9999-01-01 is not \
|
||||
possible because it is less than Jiff's supported
|
||||
minimum date",
|
||||
));
|
||||
return Err(Error::from(E::Yesterday));
|
||||
}
|
||||
return Ok(IDate { year, month: 12, day: 31 });
|
||||
}
|
||||
|
|
@ -453,11 +417,7 @@ impl IDate {
|
|||
if self.month == 12 {
|
||||
let year = self.year + 1;
|
||||
if year >= 10000 {
|
||||
return Err(err!(
|
||||
"returning tomorrow for 9999-12-31 is not \
|
||||
possible because it is greater than Jiff's supported
|
||||
maximum date",
|
||||
));
|
||||
return Err(Error::from(E::Tomorrow));
|
||||
}
|
||||
return Ok(IDate { year, month: 1, day: 1 });
|
||||
}
|
||||
|
|
@ -472,14 +432,7 @@ impl IDate {
|
|||
pub(crate) fn prev_year(self) -> Result<i16, Error> {
|
||||
let year = self.year - 1;
|
||||
if year <= -10_000 {
|
||||
return Err(err!(
|
||||
"returning previous year for {year:04}-{month:02}-{day:02} is \
|
||||
not possible because it is less than Jiff's supported \
|
||||
minimum date",
|
||||
year = self.year,
|
||||
month = self.month,
|
||||
day = self.day,
|
||||
));
|
||||
return Err(Error::from(E::YearPrevious));
|
||||
}
|
||||
Ok(year)
|
||||
}
|
||||
|
|
@ -489,14 +442,7 @@ impl IDate {
|
|||
pub(crate) fn next_year(self) -> Result<i16, Error> {
|
||||
let year = self.year + 1;
|
||||
if year >= 10_000 {
|
||||
return Err(err!(
|
||||
"returning next year for {year:04}-{month:02}-{day:02} is \
|
||||
not possible because it is greater than Jiff's supported \
|
||||
maximum date",
|
||||
year = self.year,
|
||||
month = self.month,
|
||||
day = self.day,
|
||||
));
|
||||
return Err(Error::from(E::YearNext));
|
||||
}
|
||||
Ok(year)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue