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::{full_ranges, invalid_argument};
pub struct DateGenerator;
impl Generator<Date> for DateGenerator {
fn do_draw(&self, tc: &TestCase) -> Date {
let d = tc.generate_date(full_ranges::MIN_DATE, full_ranges::MAX_DATE);
Date::new(d.year as i16, d.month as i8, d.day as i8).unwrap()
}
}
pub fn dates() -> DateGenerator {
DateGenerator
}
pub struct TimeGenerator;
impl Generator<Time> for TimeGenerator {
fn do_draw(&self, tc: &TestCase) -> Time {
let t = tc.generate_time(full_ranges::MIDNIGHT, full_ranges::LAST_MICROSECOND);
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
}
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(),
}
}