use crate::{
error::{
unit::{MustDivide, UnitConfigError},
util::RoundingIncrementError,
Error, ErrorContext,
},
util::b,
SignedDuration, Unit,
};
#[derive(Clone, Copy, Debug)]
pub(crate) struct Increment {
unit: Unit,
value: i32,
}
impl Increment {
pub(crate) fn for_span(
unit: Unit,
value: i64,
) -> Result<Increment, Error> {
static LIMITS: &[MustDivide] = &[
MustDivide::NanosPerMicro,
MustDivide::MicrosPerMilli,
MustDivide::MillisPerSec,
MustDivide::SecsPerMin,
MustDivide::MinsPerHour,
MustDivide::HoursPerCivilDay,
];
if unit < Unit::Day {
Increment::for_limits(unit, value, LIMITS)
.context(RoundingIncrementError::ForSpan)
} else {
let value = b::Increment32::check(value)
.context(RoundingIncrementError::ForSpan)?;
Ok(Increment { unit, value })
}
}
pub(crate) fn for_signed_duration(
unit: Unit,
value: i64,
) -> Result<Increment, Error> {
if unit > Unit::Hour {
return Err(Error::from(
UnitConfigError::SignedDurationCalendarUnit { smallest: unit },
));
}
let value = b::Increment32::check(value)
.context(RoundingIncrementError::ForSignedDuration)?;
Ok(Increment { unit, value })
}
pub(crate) fn for_offset(
unit: Unit,
value: i64,
) -> Result<Increment, Error> {
if !(Unit::Second <= unit && unit <= Unit::Hour) {
return Err(Error::from(UnitConfigError::TimeZoneOffset {
given: unit,
}));
}
let value = b::Increment32::check(value)
.context(RoundingIncrementError::ForOffset)?;
Ok(Increment { unit, value })
}
pub(crate) fn for_datetime(
unit: Unit,
value: i64,
) -> Result<Increment, Error> {
static LIMITS: &[MustDivide] = &[
MustDivide::NanosPerMicro,
MustDivide::MicrosPerMilli,
MustDivide::MillisPerSec,
MustDivide::SecsPerMin,
MustDivide::MinsPerHour,
MustDivide::HoursPerCivilDay,
MustDivide::Days,
];
Increment::for_limits(unit, value, LIMITS)
.context(RoundingIncrementError::ForDateTime)
}
pub(crate) fn for_time(
unit: Unit,
value: i64,
) -> Result<Increment, Error> {
static LIMITS: &[MustDivide] = &[
MustDivide::NanosPerMicro,
MustDivide::MicrosPerMilli,
MustDivide::MillisPerSec,
MustDivide::SecsPerMin,
MustDivide::MinsPerHour,
MustDivide::HoursPerCivilDay,
];
Increment::for_limits(unit, value, LIMITS)
.context(RoundingIncrementError::ForTime)
}
pub(crate) fn for_timestamp(
unit: Unit,
value: i64,
) -> Result<Increment, Error> {
static MAXIMUMS: &[MustDivide] = &[
MustDivide::NanosPerCivilDay,
MustDivide::MicrosPerCivilDay,
MustDivide::MillisPerCivilDay,
MustDivide::SecsPerCivilDay,
MustDivide::MinsPerCivilDay,
MustDivide::HoursPerCivilDay,
];
Increment::for_maximums(unit, value, MAXIMUMS)
.context(RoundingIncrementError::ForTimestamp)
}
pub(crate) fn round(
&self,
mode: RoundMode,
quantity: SignedDuration,
) -> Result<SignedDuration, Error> {
let increment = self.value() * self.unit().duration();
mode.round_by_duration(quantity, increment)
}
pub(crate) fn unit(&self) -> Unit {
self.unit
}
pub(crate) fn value(&self) -> i32 {
self.value
}
}
impl Increment {
fn for_limits(
unit: Unit,
value: i64,
limits: &[MustDivide],
) -> Result<Increment, Error> {
let Some(&must_divide) = limits.get(unit as usize) else {
return Err(Error::from(
UnitConfigError::RoundToUnitUnsupported { unit },
));
};
let value32 = b::Increment32::check(value)?;
let must_divide_i64 = must_divide.as_i64();
if value < must_divide_i64 && must_divide_i64 % value == 0 {
return Ok(Increment { unit, value: value32 });
}
Err(Error::from(UnitConfigError::IncrementDivide {
unit,
must_divide,
}))
}
fn for_maximums(
unit: Unit,
value: i64,
maximums: &[MustDivide],
) -> Result<Increment, Error> {
let Some(&must_divide) = maximums.get(unit as usize) else {
return Err(Error::from(
UnitConfigError::RoundToUnitUnsupported { unit },
));
};
let value32 = b::Increment32::check(value)?;
let must_divide_i64 = must_divide.as_i64();
if value <= must_divide_i64 && must_divide_i64 % value == 0 {
return Ok(Increment { unit, value: value32 });
}
Err(Error::from(UnitConfigError::IncrementDivide {
unit,
must_divide,
}))
}
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum RoundMode {
Ceil,
Floor,
Expand,
Trunc,
HalfCeil,
HalfFloor,
HalfExpand,
HalfTrunc,
HalfEven,
}
impl RoundMode {
pub(crate) fn round_by_duration(
self,
quantity: SignedDuration,
increment: SignedDuration,
) -> Result<SignedDuration, Error> {
let quantity = quantity.as_nanos();
let increment = increment.as_nanos();
assert!(increment >= 1);
let rounded = self.round_by_i128(quantity, increment);
SignedDuration::try_from_nanos_i128(rounded)
.ok_or_else(|| Error::from(b::SignedDurationSeconds::error()))
}
fn round_by_i128(self, quantity: i128, increment: i128) -> i128 {
assert!(increment >= 1);
let mode = self;
let mut quotient = quantity / increment;
let remainder = quantity % increment;
if remainder == 0 {
return quantity;
}
let sign = if remainder < 0 { -1 } else { 1 };
let tiebreaker = (remainder * 2).abs();
let tie = tiebreaker == increment;
let expand_is_nearer = tiebreaker > increment;
match mode {
RoundMode::Ceil => {
if sign > 0 {
quotient += sign;
}
}
RoundMode::Floor => {
if sign < 0 {
quotient += sign;
}
}
RoundMode::Expand => {
quotient += sign;
}
RoundMode::Trunc => {}
RoundMode::HalfCeil => {
if expand_is_nearer || (tie && sign > 0) {
quotient += sign;
}
}
RoundMode::HalfFloor => {
if expand_is_nearer || (tie && sign < 0) {
quotient += sign;
}
}
RoundMode::HalfExpand => {
if expand_is_nearer || tie {
quotient += sign;
}
}
RoundMode::HalfTrunc => {
if expand_is_nearer {
quotient += sign;
}
}
RoundMode::HalfEven => {
if expand_is_nearer || (tie && quotient.rem_euclid(2) == 1) {
quotient += sign;
}
}
}
quotient.saturating_mul(increment)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn for_span() {
let get = |unit, value| Increment::for_span(unit, value);
assert!(get(Unit::Nanosecond, 1).is_ok());
assert!(get(Unit::Nanosecond, 500).is_ok());
assert!(get(Unit::Nanosecond, -1).is_err());
assert!(get(Unit::Nanosecond, 0).is_err());
assert!(get(Unit::Nanosecond, 7).is_err());
assert!(get(Unit::Nanosecond, 1_000).is_err());
assert!(get(Unit::Nanosecond, i64::MIN).is_err());
assert!(get(Unit::Nanosecond, i64::MAX).is_err());
assert!(get(Unit::Microsecond, 1).is_ok());
assert!(get(Unit::Microsecond, 500).is_ok());
assert!(get(Unit::Microsecond, -1).is_err());
assert!(get(Unit::Microsecond, 0).is_err());
assert!(get(Unit::Microsecond, 7).is_err());
assert!(get(Unit::Microsecond, 1_000).is_err());
assert!(get(Unit::Microsecond, i64::MIN).is_err());
assert!(get(Unit::Microsecond, i64::MAX).is_err());
assert!(get(Unit::Millisecond, 1).is_ok());
assert!(get(Unit::Millisecond, 500).is_ok());
assert!(get(Unit::Millisecond, -1).is_err());
assert!(get(Unit::Millisecond, 0).is_err());
assert!(get(Unit::Millisecond, 7).is_err());
assert!(get(Unit::Millisecond, 1_000).is_err());
assert!(get(Unit::Millisecond, i64::MIN).is_err());
assert!(get(Unit::Millisecond, i64::MAX).is_err());
assert!(get(Unit::Second, 1).is_ok());
assert!(get(Unit::Second, 15).is_ok());
assert!(get(Unit::Second, -1).is_err());
assert!(get(Unit::Second, 0).is_err());
assert!(get(Unit::Second, 7).is_err());
assert!(get(Unit::Second, 1_000).is_err());
assert!(get(Unit::Second, i64::MIN).is_err());
assert!(get(Unit::Second, i64::MAX).is_err());
assert!(get(Unit::Minute, 1).is_ok());
assert!(get(Unit::Minute, 15).is_ok());
assert!(get(Unit::Minute, -1).is_err());
assert!(get(Unit::Minute, 0).is_err());
assert!(get(Unit::Minute, 7).is_err());
assert!(get(Unit::Minute, 1_000).is_err());
assert!(get(Unit::Minute, i64::MIN).is_err());
assert!(get(Unit::Minute, i64::MAX).is_err());
assert!(get(Unit::Hour, 1).is_ok());
assert!(get(Unit::Hour, 6).is_ok());
assert!(get(Unit::Hour, 12).is_ok());
assert!(get(Unit::Hour, -1).is_err());
assert!(get(Unit::Hour, 0).is_err());
assert!(get(Unit::Hour, 15).is_err());
assert!(get(Unit::Hour, 18).is_err());
assert!(get(Unit::Hour, 23).is_err());
assert!(get(Unit::Hour, 24).is_err());
assert!(get(Unit::Hour, i64::MIN).is_err());
assert!(get(Unit::Hour, i64::MAX).is_err());
assert!(get(Unit::Day, 1).is_ok());
assert!(get(Unit::Day, 6).is_ok());
assert!(get(Unit::Day, 7).is_ok());
assert!(get(Unit::Day, 12).is_ok());
assert!(get(Unit::Day, 30).is_ok());
assert!(get(Unit::Day, 31).is_ok());
assert!(get(Unit::Day, 100).is_ok());
assert!(get(Unit::Day, 10_000_000).is_ok());
assert!(get(Unit::Day, 1_000_000_000).is_ok());
assert!(get(Unit::Day, -1).is_err());
assert!(get(Unit::Day, 0).is_err());
assert!(get(Unit::Day, 1_000_000_001).is_err());
assert!(get(Unit::Day, i64::MIN).is_err());
assert!(get(Unit::Day, i64::MAX).is_err());
assert!(get(Unit::Week, 1).is_ok());
assert!(get(Unit::Week, 6).is_ok());
assert!(get(Unit::Week, 7).is_ok());
assert!(get(Unit::Week, 12).is_ok());
assert!(get(Unit::Week, 30).is_ok());
assert!(get(Unit::Week, 31).is_ok());
assert!(get(Unit::Week, 100).is_ok());
assert!(get(Unit::Week, 2_000_000).is_ok());
assert!(get(Unit::Week, 1_000_000_000).is_ok());
assert!(get(Unit::Week, -1).is_err());
assert!(get(Unit::Week, 0).is_err());
assert!(get(Unit::Week, 1_000_000_001).is_err());
assert!(get(Unit::Week, i64::MIN).is_err());
assert!(get(Unit::Week, i64::MAX).is_err());
assert!(get(Unit::Month, 1).is_ok());
assert!(get(Unit::Month, 6).is_ok());
assert!(get(Unit::Month, 7).is_ok());
assert!(get(Unit::Month, 12).is_ok());
assert!(get(Unit::Month, 30).is_ok());
assert!(get(Unit::Month, 31).is_ok());
assert!(get(Unit::Month, 100).is_ok());
assert!(get(Unit::Month, 300_000).is_ok());
assert!(get(Unit::Month, 1_000_000_000).is_ok());
assert!(get(Unit::Month, -1).is_err());
assert!(get(Unit::Month, 0).is_err());
assert!(get(Unit::Month, 1_000_000_001).is_err());
assert!(get(Unit::Month, i64::MIN).is_err());
assert!(get(Unit::Month, i64::MAX).is_err());
assert!(get(Unit::Year, 1).is_ok());
assert!(get(Unit::Year, 6).is_ok());
assert!(get(Unit::Year, 7).is_ok());
assert!(get(Unit::Year, 12).is_ok());
assert!(get(Unit::Year, 30).is_ok());
assert!(get(Unit::Year, 31).is_ok());
assert!(get(Unit::Year, 100).is_ok());
assert!(get(Unit::Year, 50_000).is_ok());
assert!(get(Unit::Year, 1_000_000_000).is_ok());
assert!(get(Unit::Year, -1).is_err());
assert!(get(Unit::Year, 0).is_err());
assert!(get(Unit::Year, 1_000_000_001).is_err());
assert!(get(Unit::Year, i64::MIN).is_err());
assert!(get(Unit::Year, i64::MAX).is_err());
}
#[test]
fn for_datetime() {
let get = |unit, value| Increment::for_datetime(unit, value);
assert!(get(Unit::Nanosecond, 1).is_ok());
assert!(get(Unit::Nanosecond, 500).is_ok());
assert!(get(Unit::Nanosecond, -1).is_err());
assert!(get(Unit::Nanosecond, 0).is_err());
assert!(get(Unit::Nanosecond, 7).is_err());
assert!(get(Unit::Nanosecond, 1_000).is_err());
assert!(get(Unit::Nanosecond, i64::MIN).is_err());
assert!(get(Unit::Nanosecond, i64::MAX).is_err());
assert!(get(Unit::Microsecond, 1).is_ok());
assert!(get(Unit::Microsecond, 500).is_ok());
assert!(get(Unit::Microsecond, -1).is_err());
assert!(get(Unit::Microsecond, 0).is_err());
assert!(get(Unit::Microsecond, 7).is_err());
assert!(get(Unit::Microsecond, 1_000).is_err());
assert!(get(Unit::Microsecond, i64::MIN).is_err());
assert!(get(Unit::Microsecond, i64::MAX).is_err());
assert!(get(Unit::Millisecond, 1).is_ok());
assert!(get(Unit::Millisecond, 500).is_ok());
assert!(get(Unit::Millisecond, -1).is_err());
assert!(get(Unit::Millisecond, 0).is_err());
assert!(get(Unit::Millisecond, 7).is_err());
assert!(get(Unit::Millisecond, 1_000).is_err());
assert!(get(Unit::Millisecond, i64::MIN).is_err());
assert!(get(Unit::Millisecond, i64::MAX).is_err());
assert!(get(Unit::Second, 1).is_ok());
assert!(get(Unit::Second, 15).is_ok());
assert!(get(Unit::Second, -1).is_err());
assert!(get(Unit::Second, 0).is_err());
assert!(get(Unit::Second, 7).is_err());
assert!(get(Unit::Second, 1_000).is_err());
assert!(get(Unit::Second, i64::MIN).is_err());
assert!(get(Unit::Second, i64::MAX).is_err());
assert!(get(Unit::Minute, 1).is_ok());
assert!(get(Unit::Minute, 15).is_ok());
assert!(get(Unit::Minute, -1).is_err());
assert!(get(Unit::Minute, 0).is_err());
assert!(get(Unit::Minute, 7).is_err());
assert!(get(Unit::Minute, 1_000).is_err());
assert!(get(Unit::Minute, i64::MIN).is_err());
assert!(get(Unit::Minute, i64::MAX).is_err());
assert!(get(Unit::Hour, 1).is_ok());
assert!(get(Unit::Hour, 6).is_ok());
assert!(get(Unit::Hour, 12).is_ok());
assert!(get(Unit::Hour, -1).is_err());
assert!(get(Unit::Hour, 0).is_err());
assert!(get(Unit::Hour, 15).is_err());
assert!(get(Unit::Hour, 18).is_err());
assert!(get(Unit::Hour, 23).is_err());
assert!(get(Unit::Hour, 24).is_err());
assert!(get(Unit::Hour, i64::MIN).is_err());
assert!(get(Unit::Hour, i64::MAX).is_err());
assert!(get(Unit::Day, 1).is_ok());
assert!(get(Unit::Day, -1).is_err());
assert!(get(Unit::Day, 0).is_err());
assert!(get(Unit::Day, 2).is_err());
assert!(get(Unit::Day, 4).is_err());
assert!(get(Unit::Day, 7).is_err());
assert!(get(Unit::Day, i64::MIN).is_err());
assert!(get(Unit::Day, i64::MAX).is_err());
}
#[test]
fn for_time() {
let get = |unit, value| Increment::for_time(unit, value);
assert!(get(Unit::Nanosecond, 1).is_ok());
assert!(get(Unit::Nanosecond, 500).is_ok());
assert!(get(Unit::Nanosecond, -1).is_err());
assert!(get(Unit::Nanosecond, 0).is_err());
assert!(get(Unit::Nanosecond, 7).is_err());
assert!(get(Unit::Nanosecond, 1_000).is_err());
assert!(get(Unit::Nanosecond, i64::MIN).is_err());
assert!(get(Unit::Nanosecond, i64::MAX).is_err());
assert!(get(Unit::Microsecond, 1).is_ok());
assert!(get(Unit::Microsecond, 500).is_ok());
assert!(get(Unit::Microsecond, -1).is_err());
assert!(get(Unit::Microsecond, 0).is_err());
assert!(get(Unit::Microsecond, 7).is_err());
assert!(get(Unit::Microsecond, 1_000).is_err());
assert!(get(Unit::Microsecond, i64::MIN).is_err());
assert!(get(Unit::Microsecond, i64::MAX).is_err());
assert!(get(Unit::Millisecond, 1).is_ok());
assert!(get(Unit::Millisecond, 500).is_ok());
assert!(get(Unit::Millisecond, -1).is_err());
assert!(get(Unit::Millisecond, 0).is_err());
assert!(get(Unit::Millisecond, 7).is_err());
assert!(get(Unit::Millisecond, 1_000).is_err());
assert!(get(Unit::Millisecond, i64::MIN).is_err());
assert!(get(Unit::Millisecond, i64::MAX).is_err());
assert!(get(Unit::Second, 1).is_ok());
assert!(get(Unit::Second, 15).is_ok());
assert!(get(Unit::Second, -1).is_err());
assert!(get(Unit::Second, 0).is_err());
assert!(get(Unit::Second, 7).is_err());
assert!(get(Unit::Second, 1_000).is_err());
assert!(get(Unit::Second, i64::MIN).is_err());
assert!(get(Unit::Second, i64::MAX).is_err());
assert!(get(Unit::Minute, 1).is_ok());
assert!(get(Unit::Minute, 15).is_ok());
assert!(get(Unit::Minute, -1).is_err());
assert!(get(Unit::Minute, 0).is_err());
assert!(get(Unit::Minute, 7).is_err());
assert!(get(Unit::Minute, 1_000).is_err());
assert!(get(Unit::Minute, i64::MIN).is_err());
assert!(get(Unit::Minute, i64::MAX).is_err());
assert!(get(Unit::Hour, 1).is_ok());
assert!(get(Unit::Hour, 6).is_ok());
assert!(get(Unit::Hour, 12).is_ok());
assert!(get(Unit::Hour, -1).is_err());
assert!(get(Unit::Hour, 0).is_err());
assert!(get(Unit::Hour, 15).is_err());
assert!(get(Unit::Hour, 18).is_err());
assert!(get(Unit::Hour, 23).is_err());
assert!(get(Unit::Hour, 24).is_err());
assert!(get(Unit::Hour, i64::MIN).is_err());
assert!(get(Unit::Hour, i64::MAX).is_err());
assert!(get(Unit::Day, 1).is_err());
assert!(get(Unit::Day, -1).is_err());
assert!(get(Unit::Day, 0).is_err());
assert!(get(Unit::Day, 2).is_err());
assert!(get(Unit::Day, 4).is_err());
assert!(get(Unit::Day, 7).is_err());
assert!(get(Unit::Day, i64::MIN).is_err());
assert!(get(Unit::Day, i64::MAX).is_err());
}
#[test]
fn for_timestamp() {
let get = |unit, value| Increment::for_timestamp(unit, value);
assert!(get(Unit::Nanosecond, 1).is_ok());
assert!(get(Unit::Nanosecond, 500).is_ok());
assert!(get(Unit::Nanosecond, 1_000).is_ok());
assert!(get(Unit::Nanosecond, 1_000_000_000).is_ok());
assert!(get(Unit::Nanosecond, -1).is_err());
assert!(get(Unit::Nanosecond, 0).is_err());
assert!(get(Unit::Nanosecond, 7).is_err());
assert!(get(Unit::Nanosecond, 1_000_000_001).is_err());
assert!(get(Unit::Nanosecond, i64::MIN).is_err());
assert!(get(Unit::Nanosecond, i64::MAX).is_err());
assert!(get(Unit::Microsecond, 1).is_ok());
assert!(get(Unit::Microsecond, 500).is_ok());
assert!(get(Unit::Microsecond, 1_000).is_ok());
assert!(get(Unit::Microsecond, 2_000).is_ok());
assert!(get(Unit::Microsecond, -1).is_err());
assert!(get(Unit::Microsecond, 0).is_err());
assert!(get(Unit::Microsecond, 7).is_err());
assert!(get(Unit::Microsecond, 1_000_000_000).is_err());
assert!(get(Unit::Microsecond, 1_000_000_001).is_err());
assert!(get(Unit::Microsecond, i64::MIN).is_err());
assert!(get(Unit::Microsecond, i64::MAX).is_err());
assert!(get(Unit::Millisecond, 1).is_ok());
assert!(get(Unit::Millisecond, 500).is_ok());
assert!(get(Unit::Millisecond, 1_000).is_ok());
assert!(get(Unit::Millisecond, 2_000).is_ok());
assert!(get(Unit::Millisecond, 86_400_000).is_ok());
assert!(get(Unit::Millisecond, -1).is_err());
assert!(get(Unit::Millisecond, 0).is_err());
assert!(get(Unit::Millisecond, 7).is_err());
assert!(get(Unit::Millisecond, 1_000_000_000).is_err());
assert!(get(Unit::Millisecond, 1_000_000_001).is_err());
assert!(get(Unit::Millisecond, i64::MIN).is_err());
assert!(get(Unit::Millisecond, i64::MAX).is_err());
assert!(get(Unit::Second, 1).is_ok());
assert!(get(Unit::Second, 15).is_ok());
assert!(get(Unit::Second, 3_600).is_ok());
assert!(get(Unit::Second, 86_400).is_ok());
assert!(get(Unit::Second, -1).is_err());
assert!(get(Unit::Second, 0).is_err());
assert!(get(Unit::Second, 7).is_err());
assert!(get(Unit::Second, 1_000).is_err());
assert!(get(Unit::Second, 86_401).is_err());
assert!(get(Unit::Second, 172_800).is_err());
assert!(get(Unit::Second, 1_000_000_000).is_err());
assert!(get(Unit::Second, 1_000_000_001).is_err());
assert!(get(Unit::Second, i64::MIN).is_err());
assert!(get(Unit::Second, i64::MAX).is_err());
assert!(get(Unit::Minute, 1).is_ok());
assert!(get(Unit::Minute, 15).is_ok());
assert!(get(Unit::Minute, 1_440).is_ok());
assert!(get(Unit::Minute, -1).is_err());
assert!(get(Unit::Minute, 0).is_err());
assert!(get(Unit::Minute, 7).is_err());
assert!(get(Unit::Minute, 1_000).is_err());
assert!(get(Unit::Minute, 1_441).is_err());
assert!(get(Unit::Minute, 2_880).is_err());
assert!(get(Unit::Minute, 1_000_000_000).is_err());
assert!(get(Unit::Minute, 1_000_000_001).is_err());
assert!(get(Unit::Minute, i64::MIN).is_err());
assert!(get(Unit::Minute, i64::MAX).is_err());
assert!(get(Unit::Hour, 1).is_ok());
assert!(get(Unit::Hour, 6).is_ok());
assert!(get(Unit::Hour, 12).is_ok());
assert!(get(Unit::Hour, 24).is_ok());
assert!(get(Unit::Hour, -1).is_err());
assert!(get(Unit::Hour, 0).is_err());
assert!(get(Unit::Hour, 15).is_err());
assert!(get(Unit::Hour, 18).is_err());
assert!(get(Unit::Hour, 23).is_err());
assert!(get(Unit::Hour, 25).is_err());
assert!(get(Unit::Hour, 1_000_000_000).is_err());
assert!(get(Unit::Hour, 1_000_000_001).is_err());
assert!(get(Unit::Hour, i64::MIN).is_err());
assert!(get(Unit::Hour, i64::MAX).is_err());
assert!(get(Unit::Day, 1).is_err());
assert!(get(Unit::Day, -1).is_err());
assert!(get(Unit::Day, 0).is_err());
assert!(get(Unit::Day, 2).is_err());
assert!(get(Unit::Day, 4).is_err());
assert!(get(Unit::Day, 7).is_err());
assert!(get(Unit::Day, i64::MIN).is_err());
assert!(get(Unit::Day, i64::MAX).is_err());
assert!(get(Unit::Week, 1).is_err());
assert!(get(Unit::Week, -1).is_err());
assert!(get(Unit::Week, 0).is_err());
assert!(get(Unit::Week, 2).is_err());
assert!(get(Unit::Week, 4).is_err());
assert!(get(Unit::Week, 7).is_err());
assert!(get(Unit::Week, i64::MIN).is_err());
assert!(get(Unit::Week, i64::MAX).is_err());
assert!(get(Unit::Month, 1).is_err());
assert!(get(Unit::Month, -1).is_err());
assert!(get(Unit::Month, 0).is_err());
assert!(get(Unit::Month, 2).is_err());
assert!(get(Unit::Month, 4).is_err());
assert!(get(Unit::Month, 7).is_err());
assert!(get(Unit::Month, i64::MIN).is_err());
assert!(get(Unit::Month, i64::MAX).is_err());
assert!(get(Unit::Year, 1).is_err());
assert!(get(Unit::Year, -1).is_err());
assert!(get(Unit::Year, 0).is_err());
assert!(get(Unit::Year, 2).is_err());
assert!(get(Unit::Year, 4).is_err());
assert!(get(Unit::Year, 7).is_err());
assert!(get(Unit::Year, i64::MIN).is_err());
assert!(get(Unit::Year, i64::MAX).is_err());
}
#[test]
fn round_to_increment_half_expand_ad_hoc() {
let round = |quantity: i128, increment: i128| -> i128 {
RoundMode::HalfExpand.round_by_i128(quantity, increment)
};
assert_eq!(26, round(20, 13));
assert_eq!(0, round(29, 60));
assert_eq!(60, round(30, 60));
assert_eq!(60, round(31, 60));
assert_eq!(0, round(3, 7));
assert_eq!(7, round(4, 7));
}
#[test]
fn round_to_increment_temporal_table_ceil() {
let round = |quantity: i128, increment: i128| -> i128 {
RoundMode::Ceil.round_by_i128(quantity, increment)
};
assert_eq!(-10, round(-15, 10));
assert_eq!(0, round(-5, 10));
assert_eq!(10, round(4, 10));
assert_eq!(10, round(5, 10));
assert_eq!(10, round(6, 10));
assert_eq!(20, round(15, 10));
assert_eq!(i128::MAX, round(i128::MAX, 1));
assert_eq!(i128::MAX, round(i128::MAX, i128::MAX));
}
#[test]
fn round_to_increment_temporal_table_floor() {
let round = |quantity: i128, increment: i128| -> i128 {
RoundMode::Floor.round_by_i128(quantity, increment)
};
assert_eq!(-20, round(-15, 10));
assert_eq!(-10, round(-5, 10));
assert_eq!(0, round(4, 10));
assert_eq!(0, round(5, 10));
assert_eq!(0, round(6, 10));
assert_eq!(10, round(15, 10));
}
#[test]
fn round_to_increment_temporal_table_expand() {
let round = |quantity: i128, increment: i128| -> i128 {
RoundMode::Expand.round_by_i128(quantity, increment)
};
assert_eq!(-20, round(-15, 10));
assert_eq!(-10, round(-5, 10));
assert_eq!(10, round(4, 10));
assert_eq!(10, round(5, 10));
assert_eq!(10, round(6, 10));
assert_eq!(20, round(15, 10));
}
#[test]
fn round_to_increment_temporal_table_trunc() {
let round = |quantity: i128, increment: i128| -> i128 {
RoundMode::Trunc.round_by_i128(quantity, increment)
};
assert_eq!(-10, round(-15, 10));
assert_eq!(0, round(-5, 10));
assert_eq!(0, round(4, 10));
assert_eq!(0, round(5, 10));
assert_eq!(0, round(6, 10));
assert_eq!(10, round(15, 10));
}
#[test]
fn round_to_increment_temporal_table_half_ceil() {
let round = |quantity: i128, increment: i128| -> i128 {
RoundMode::HalfCeil.round_by_i128(quantity, increment)
};
assert_eq!(-10, round(-15, 10));
assert_eq!(0, round(-5, 10));
assert_eq!(0, round(4, 10));
assert_eq!(10, round(5, 10));
assert_eq!(10, round(6, 10));
assert_eq!(20, round(15, 10));
}
#[test]
fn round_to_increment_temporal_table_half_floor() {
let round = |quantity: i128, increment: i128| -> i128 {
RoundMode::HalfFloor.round_by_i128(quantity, increment)
};
assert_eq!(-20, round(-15, 10));
assert_eq!(-10, round(-5, 10));
assert_eq!(0, round(4, 10));
assert_eq!(0, round(5, 10));
assert_eq!(10, round(6, 10));
assert_eq!(10, round(15, 10));
}
#[test]
fn round_to_increment_temporal_table_half_expand() {
let round = |quantity: i128, increment: i128| -> i128 {
RoundMode::HalfExpand.round_by_i128(quantity, increment)
};
assert_eq!(-20, round(-15, 10));
assert_eq!(-10, round(-5, 10));
assert_eq!(0, round(4, 10));
assert_eq!(10, round(5, 10));
assert_eq!(10, round(6, 10));
assert_eq!(20, round(15, 10));
}
#[test]
fn round_to_increment_temporal_table_half_trunc() {
let round = |quantity: i128, increment: i128| -> i128 {
RoundMode::HalfTrunc.round_by_i128(quantity, increment)
};
assert_eq!(-10, round(-15, 10));
assert_eq!(0, round(-5, 10));
assert_eq!(0, round(4, 10));
assert_eq!(0, round(5, 10));
assert_eq!(10, round(6, 10));
assert_eq!(10, round(15, 10));
}
#[test]
fn round_to_increment_temporal_table_half_even() {
let round = |quantity: i128, increment: i128| -> i128 {
RoundMode::HalfEven.round_by_i128(quantity, increment)
};
assert_eq!(-20, round(-15, 10));
assert_eq!(0, round(-5, 10));
assert_eq!(0, round(4, 10));
assert_eq!(0, round(5, 10));
assert_eq!(10, round(6, 10));
assert_eq!(20, round(15, 10));
}
#[test]
fn maximum_increment_nanos() {
assert!(Increment::for_span(Unit::Week, 1_000_000_000).is_ok());
assert!(Increment::for_span(Unit::Week, 1_000_000_001).is_err());
assert_eq!(
Unit::Week.duration() * 1_000_000_000,
SignedDuration::from_secs(168_000_000_000 * 60 * 60)
);
}
#[test]
fn round_by_duration_overflow() {
let mode = RoundMode::Expand;
let quantity = SignedDuration::MAX;
let increment = SignedDuration::MAX - SignedDuration::from_secs(1);
assert!(mode.round_by_duration(quantity, increment).is_err());
let mode = RoundMode::Expand;
let quantity = SignedDuration::MIN;
let increment = SignedDuration::MAX;
assert!(mode.round_by_duration(quantity, increment).is_err());
}
}