Skip to content

Commit

Permalink
Use ranged integers internally for Time
Browse files Browse the repository at this point in the history
  • Loading branch information
jhpratt committed Jul 29, 2023
1 parent 2cb18f8 commit 7e95cb2
Show file tree
Hide file tree
Showing 6 changed files with 254 additions and 198 deletions.
14 changes: 8 additions & 6 deletions time-macros/src/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,14 @@ pub(crate) fn parse(chars: &mut Peekable<token_stream::IntoIter>) -> Result<Time
impl ToTokenTree for Time {
fn into_token_tree(self) -> TokenTree {
quote_group! {{
const TIME: ::time::Time = ::time::Time::__from_hms_nanos_unchecked(
#(self.hour),
#(self.minute),
#(self.second),
#(self.nanosecond),
);
const TIME: ::time::Time = unsafe {
::time::Time::__from_hms_nanos_unchecked(
#(self.hour),
#(self.minute),
#(self.second),
#(self.nanosecond),
)
};
TIME
}}
}
Expand Down
50 changes: 28 additions & 22 deletions time/src/date_time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,10 +251,7 @@ impl<O: MaybeOffset> DateTime<O> {
#[allow(clippy::missing_docs_in_private_items)]
const MIN_TIMESTAMP: i64 = Date::MIN.midnight().assume_utc().unix_timestamp();
#[allow(clippy::missing_docs_in_private_items)]
const MAX_TIMESTAMP: i64 = Date::MAX
.with_time(Time::__from_hms_nanos_unchecked(23, 59, 59, 999_999_999))
.assume_utc()
.unix_timestamp();
const MAX_TIMESTAMP: i64 = Date::MAX.with_time(Time::MAX).assume_utc().unix_timestamp();

ensure_value_in_range!(timestamp in MIN_TIMESTAMP => MAX_TIMESTAMP);

Expand All @@ -264,12 +261,15 @@ impl<O: MaybeOffset> DateTime<O> {
);

let seconds_within_day = timestamp.rem_euclid(Second.per(Day) as _);
let time = Time::__from_hms_nanos_unchecked(
(seconds_within_day / Second.per(Hour) as i64) as _,
((seconds_within_day % Second.per(Hour) as i64) / Minute.per(Hour) as i64) as _,
(seconds_within_day % Second.per(Minute) as i64) as _,
0,
);
// Safety: All values are in range.
let time = unsafe {
Time::__from_hms_nanos_unchecked(
(seconds_within_day / Second.per(Hour) as i64) as _,
((seconds_within_day % Second.per(Hour) as i64) / Minute.per(Hour) as i64) as _,
(seconds_within_day % Second.per(Minute) as i64) as _,
0,
)
};

Ok(Self {
date,
Expand All @@ -289,12 +289,15 @@ impl<O: MaybeOffset> DateTime<O> {

Ok(Self {
date: datetime.date,
time: Time::__from_hms_nanos_unchecked(
datetime.hour(),
datetime.minute(),
datetime.second(),
timestamp.rem_euclid(Nanosecond.per(Second) as _) as u32,
),
// Safety: `nanosecond` is in range due to `rem_euclid`.
time: unsafe {
Time::__from_hms_nanos_unchecked(
datetime.hour(),
datetime.minute(),
datetime.second(),
timestamp.rem_euclid(Nanosecond.per(Second) as _) as u32,
)
},
offset: maybe_offset_from_offset::<O>(UtcOffset::UTC),
})
}
Expand Down Expand Up @@ -572,12 +575,15 @@ impl<O: MaybeOffset> DateTime<O> {
(
year,
ordinal as _,
Time::__from_hms_nanos_unchecked(
hour as _,
minute as _,
second as _,
self.nanosecond(),
),
// Safety: The cascades above ensure the values are in range.
unsafe {
Time::__from_hms_nanos_unchecked(
hour as _,
minute as _,
second as _,
self.nanosecond(),
)
},
)
}
// endregion to offset
Expand Down
33 changes: 33 additions & 0 deletions time/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,39 @@ macro_rules! ensure_value_in_range {
}};
}

/// Constructs a ranged integer, returning a `ComponentRange` error if the value is out of range.
macro_rules! ensure_ranged {
($type:ident : $value:ident) => {
match $type::new($value) {
Some(val) => val,
None => {
return Err(crate::error::ComponentRange {
name: stringify!($value),
minimum: $type::MIN.get() as _,
maximum: $type::MAX.get() as _,
value: $value as _,
conditional_range: false,
});
}
}
};

($type:ident : $value:ident $(as $as_type:ident)? * $factor:expr) => {
match $type::new($value $(as $as_type)? * $factor) {
Some(val) => val,
None => {
return Err(crate::error::ComponentRange {
name: stringify!($value),
minimum: $type::MIN.get() as i64 / $factor as i64,
maximum: $type::MAX.get() as i64 / $factor as i64,
value: $value as _,
conditional_range: false,
});
}
}
};
}

/// Try to unwrap an expression, returning if not possible.
///
/// This is similar to the `?` operator, but does not perform `.into()`. Because of this, it is
Expand Down
15 changes: 7 additions & 8 deletions time/src/quickcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ use alloc::boxed::Box;

use quickcheck::{empty_shrinker, single_shrinker, Arbitrary, Gen};

use crate::convert::*;
use crate::date_time::{DateTime, MaybeOffset};
use crate::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};

Expand Down Expand Up @@ -99,20 +98,20 @@ impl Arbitrary for Duration {

impl Arbitrary for Time {
fn arbitrary(g: &mut Gen) -> Self {
Self::__from_hms_nanos_unchecked(
arbitrary_between!(u8; g, 0, Hour.per(Day) - 1),
arbitrary_between!(u8; g, 0, Minute.per(Hour) - 1),
arbitrary_between!(u8; g, 0, Second.per(Minute) - 1),
arbitrary_between!(u32; g, 0, Nanosecond.per(Second) - 1),
Self::from_hms_nanos_ranged(
<_>::arbitrary(g),
<_>::arbitrary(g),
<_>::arbitrary(g),
<_>::arbitrary(g),
)
}

fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
self.as_hms_nano()
self.as_hms_nano_ranged()
.shrink()
.map(|(hour, minute, second, nanosecond)| {
Self::__from_hms_nanos_unchecked(hour, minute, second, nanosecond)
Self::from_hms_nanos_ranged(hour, minute, second, nanosecond)
}),
)
}
Expand Down
8 changes: 1 addition & 7 deletions time/src/rand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,11 @@
use rand::distributions::{Distribution, Standard};
use rand::Rng;

use crate::convert::*;
use crate::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};

impl Distribution<Time> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Time {
Time::__from_hms_nanos_unchecked(
rng.gen_range(0..Hour.per(Day)),
rng.gen_range(0..Minute.per(Hour)),
rng.gen_range(0..Second.per(Minute)),
rng.gen_range(0..Nanosecond.per(Second)),
)
Time::from_hms_nanos_ranged(rng.gen(), rng.gen(), rng.gen(), rng.gen())
}
}

Expand Down
Loading

0 comments on commit 7e95cb2

Please sign in to comment.