use jiff::civil::{Date, DateTime, Time};
use jiff::tz::{Offset, TimeZone};
use jiff::{SignedDuration, Span, Timestamp, Zoned};
use crate::generators::{BoxedGenerator, Generator, TestCase, integers};
use crate::test_case::invalid_argument;
fn hegel_date(d: Date) -> hegel_c::hegel_date_t {
hegel_c::hegel_date_t {
year: i32::from(d.year()),
month: d.month() as u8,
day: d.day() as u8,
}
}
pub struct DateGenerator {
min_value: Date,
max_value: Date,
}
impl DateGenerator {
pub fn min_value(mut self, min: Date) -> Self {
self.min_value = min;
self
}
pub fn max_value(mut self, max: Date) -> Self {
self.max_value = max;
self
}
}
impl Generator<Date> for DateGenerator {
fn do_draw(&self, tc: &TestCase) -> Date {
if self.min_value > self.max_value {
invalid_argument!("Cannot have max_value < min_value");
}
let d = tc.generate_date(hegel_date(self.min_value), hegel_date(self.max_value));
Date::new(d.year as i16, d.month as i8, d.day as i8).unwrap()
}
}
pub fn dates() -> DateGenerator {
DateGenerator {
min_value: Date::constant(1, 1, 1),
max_value: Date::constant(9999, 12, 31),
}
}
fn time_total_nanos(t: Time) -> i64 {
(i64::from(t.hour()) * 3_600 + i64::from(t.minute()) * 60 + i64::from(t.second()))
* 1_000_000_000
+ i64::from(t.subsec_nanosecond())
}
fn hegel_time(total_micros: i64) -> hegel_c::hegel_time_t {
hegel_c::hegel_time_t {
hour: (total_micros / 3_600_000_000) as u8,
minute: (total_micros / 60_000_000 % 60) as u8,
second: (total_micros / 1_000_000 % 60) as u8,
microsecond: (total_micros % 1_000_000) as u32,
}
}
pub struct TimeGenerator {
min_value: Time,
max_value: Time,
}
impl TimeGenerator {
pub fn min_value(mut self, min: Time) -> Self {
self.min_value = min;
self
}
pub fn max_value(mut self, max: Time) -> Self {
self.max_value = max;
self
}
}
impl Generator<Time> for TimeGenerator {
fn do_draw(&self, tc: &TestCase) -> Time {
if self.min_value > self.max_value {
invalid_argument!("Cannot have max_value < min_value");
}
let min_micros = (time_total_nanos(self.min_value) + 999) / 1_000;
let max_micros = time_total_nanos(self.max_value) / 1_000;
if min_micros > max_micros {
invalid_argument!(
"times() generates whole-microsecond values, and no whole microsecond \
lies between min_value and max_value"
);
}
let t = tc.generate_time(hegel_time(min_micros), hegel_time(max_micros));
Time::new(
t.hour as i8,
t.minute as i8,
t.second as i8,
(t.microsecond * 1000) as i32,
)
.unwrap()
}
}
pub fn times() -> TimeGenerator {
TimeGenerator {
min_value: Time::MIN,
max_value: Time::MAX,
}
}
fn datetime_to_nanos(dt: DateTime) -> i128 {
dt.to_zoned(TimeZone::UTC)
.expect("DateTime bound out of jiff::Timestamp range")
.timestamp()
.as_nanosecond()
}
fn nanos_to_datetime(n: i128) -> DateTime {
Timestamp::from_nanosecond(n)
.unwrap()
.to_zoned(TimeZone::UTC)
.datetime()
}
pub struct DateTimeGenerator {
min_value: DateTime,
max_value: DateTime,
}
impl DateTimeGenerator {
pub fn min_value(mut self, min: DateTime) -> Self {
self.min_value = min;
self
}
pub fn max_value(mut self, max: DateTime) -> Self {
self.max_value = max;
self
}
}
impl Generator<DateTime> for DateTimeGenerator {
fn do_draw(&self, tc: &TestCase) -> DateTime {
if self.min_value > self.max_value {
invalid_argument!("Cannot have max_value < min_value");
}
let n = integers::<i128>()
.min_value(datetime_to_nanos(self.min_value))
.max_value(datetime_to_nanos(self.max_value))
.do_draw(tc);
nanos_to_datetime(n)
}
}
pub fn datetimes() -> DateTimeGenerator {
DateTimeGenerator {
min_value: Timestamp::MIN.to_zoned(TimeZone::UTC).datetime(),
max_value: Timestamp::MAX.to_zoned(TimeZone::UTC).datetime(),
}
}
pub struct TimestampGenerator {
min_value: Timestamp,
max_value: Timestamp,
}
impl TimestampGenerator {
pub fn min_value(mut self, min: Timestamp) -> Self {
self.min_value = min;
self
}
pub fn max_value(mut self, max: Timestamp) -> Self {
self.max_value = max;
self
}
}
impl Generator<Timestamp> for TimestampGenerator {
fn do_draw(&self, tc: &TestCase) -> Timestamp {
if self.min_value > self.max_value {
invalid_argument!("Cannot have max_value < min_value");
}
let nanos = integers::<i128>()
.min_value(self.min_value.as_nanosecond())
.max_value(self.max_value.as_nanosecond())
.do_draw(tc);
Timestamp::from_nanosecond(nanos).unwrap()
}
}
pub fn timestamps() -> TimestampGenerator {
TimestampGenerator {
min_value: Timestamp::MIN,
max_value: Timestamp::MAX,
}
}
const SPAN_NANOS_LIMIT: i64 = i64::MAX;
pub struct SpanGenerator {
min_nanos: i64,
max_nanos: i64,
}
impl SpanGenerator {
pub fn min_nanoseconds(mut self, min: i64) -> Self {
self.min_nanos = min;
self
}
pub fn max_nanoseconds(mut self, max: i64) -> Self {
self.max_nanos = max;
self
}
}
impl Generator<Span> for SpanGenerator {
fn do_draw(&self, tc: &TestCase) -> Span {
if self.min_nanos > self.max_nanos {
invalid_argument!("Cannot have max_nanoseconds < min_nanoseconds");
}
if self.min_nanos < -SPAN_NANOS_LIMIT {
invalid_argument!("min_nanoseconds must be >= -i64::MAX (the Span nanosecond limit)");
}
let nanos = integers::<i64>()
.min_value(self.min_nanos)
.max_value(self.max_nanos)
.do_draw(tc);
Span::new().try_nanoseconds(nanos).unwrap()
}
}
pub fn spans() -> SpanGenerator {
SpanGenerator {
min_nanos: -SPAN_NANOS_LIMIT,
max_nanos: SPAN_NANOS_LIMIT,
}
}
fn nanos_to_signed_duration(n: i128) -> SignedDuration {
let secs = (n / 1_000_000_000) as i64;
let subnanos = (n % 1_000_000_000) as i32;
SignedDuration::new(secs, subnanos)
}
pub struct SignedDurationGenerator {
min_value: SignedDuration,
max_value: SignedDuration,
}
impl SignedDurationGenerator {
pub fn min_value(mut self, min: SignedDuration) -> Self {
self.min_value = min;
self
}
pub fn max_value(mut self, max: SignedDuration) -> Self {
self.max_value = max;
self
}
}
impl Generator<SignedDuration> for SignedDurationGenerator {
fn do_draw(&self, tc: &TestCase) -> SignedDuration {
if self.min_value > self.max_value {
invalid_argument!("Cannot have max_value < min_value");
}
let nanos = integers::<i128>()
.min_value(self.min_value.as_nanos())
.max_value(self.max_value.as_nanos())
.do_draw(tc);
nanos_to_signed_duration(nanos)
}
}
pub fn signed_durations() -> SignedDurationGenerator {
SignedDurationGenerator {
min_value: SignedDuration::MIN,
max_value: SignedDuration::MAX,
}
}
const OFFSET_MIN_SECS: i32 = -93_599;
const OFFSET_MAX_SECS: i32 = 93_599;
pub struct OffsetGenerator {
min_value: Offset,
max_value: Offset,
}
impl OffsetGenerator {
pub fn min_value(mut self, min: Offset) -> Self {
self.min_value = min;
self
}
pub fn max_value(mut self, max: Offset) -> Self {
self.max_value = max;
self
}
}
impl Generator<Offset> for OffsetGenerator {
fn do_draw(&self, tc: &TestCase) -> Offset {
if self.min_value.seconds() > self.max_value.seconds() {
invalid_argument!("Cannot have max_value < min_value");
}
let secs = integers::<i32>()
.min_value(self.min_value.seconds())
.max_value(self.max_value.seconds())
.do_draw(tc);
Offset::from_seconds(secs).unwrap()
}
}
pub fn offsets() -> OffsetGenerator {
OffsetGenerator {
min_value: Offset::from_seconds(OFFSET_MIN_SECS).unwrap(),
max_value: Offset::from_seconds(OFFSET_MAX_SECS).unwrap(),
}
}
pub struct ZonedGenerator<TS = TimestampGenerator, TZ = BoxedGenerator<'static, TimeZone>> {
timestamp_gen: TS,
timezone_gen: TZ,
}
impl<TS, TZ> ZonedGenerator<TS, TZ> {
pub fn timestamps<TS2>(self, timestamp_gen: TS2) -> ZonedGenerator<TS2, TZ>
where
TS2: Generator<Timestamp>,
{
ZonedGenerator {
timestamp_gen,
timezone_gen: self.timezone_gen,
}
}
pub fn timezones<TZ2>(self, timezone_gen: TZ2) -> ZonedGenerator<TS, TZ2>
where
TZ2: Generator<TimeZone>,
{
ZonedGenerator {
timestamp_gen: self.timestamp_gen,
timezone_gen,
}
}
}
impl<TS, TZ> Generator<Zoned> for ZonedGenerator<TS, TZ>
where
TS: Generator<Timestamp>,
TZ: Generator<TimeZone>,
{
fn do_draw(&self, tc: &TestCase) -> Zoned {
let (ts, tz) =
crate::generators::tuples2(&self.timestamp_gen, &self.timezone_gen).do_draw(tc);
Zoned::new(ts, tz)
}
}
pub fn zoneds() -> ZonedGenerator {
use crate::generators::DefaultGenerator;
ZonedGenerator {
timestamp_gen: timestamps(),
timezone_gen: <TimeZone as DefaultGenerator>::default_generator(),
}
}