use crate::extended::Extended;
use time::{Date, OffsetDateTime, Time, UtcOffset};
#[inline]
fn end_of_day_time() -> Time {
Time::from_hms_nano(23, 59, 59, 999_999_999).expect("23:59:59.999_999_999 is a valid Time")
}
#[cfg(test)]
#[inline]
fn odtm_min_instant() -> OffsetDateTime {
OffsetDateTime::new_in_offset(
Date::MIN,
Time::MIDNIGHT,
UtcOffset::from_whole_seconds(93_599).expect("+25:59:59 is a valid offset"),
)
}
#[cfg(test)]
#[inline]
fn odtm_max_instant() -> OffsetDateTime {
OffsetDateTime::new_in_offset(
Date::MAX,
end_of_day_time(),
UtcOffset::from_whole_seconds(-93_599).expect("-25:59:59 is a valid offset"),
)
}
#[inline]
fn utc_min_ns() -> i128 {
OffsetDateTime::new_in_offset(Date::MIN, Time::MIDNIGHT, UtcOffset::UTC).unix_timestamp_nanos()
}
#[inline]
fn utc_max_ns() -> i128 {
OffsetDateTime::new_in_offset(Date::MAX, end_of_day_time(), UtcOffset::UTC)
.unix_timestamp_nanos()
}
#[inline]
fn utc_min_s() -> i64 {
OffsetDateTime::new_in_offset(Date::MIN, Time::MIDNIGHT, UtcOffset::UTC).unix_timestamp()
}
#[inline]
fn utc_max_s() -> i64 {
OffsetDateTime::new_in_offset(Date::MAX, end_of_day_time(), UtcOffset::UTC).unix_timestamp()
}
fn odtmnano_ceil(o: Extended<OffsetDateTime>) -> i128 {
let min_ns = utc_min_ns();
let max_ns = utc_max_ns();
match o {
Extended::NegInf => i128::MIN,
Extended::Finite(odt) => {
let n = odt.unix_timestamp_nanos();
if n < min_ns {
min_ns
} else if n > max_ns {
max_ns + 1
} else {
n
}
}
Extended::PosInf => max_ns + 1,
}
}
fn odtmnano_inner(n: i128) -> Extended<OffsetDateTime> {
let min_ns = utc_min_ns();
let max_ns = utc_max_ns();
if n < min_ns {
Extended::NegInf
} else if n > max_ns {
Extended::PosInf
} else {
match OffsetDateTime::from_unix_timestamp_nanos(n) {
Ok(odt) => Extended::Finite(odt),
Err(_) => unreachable!("n in [utc_min_ns, utc_max_ns]"),
}
}
}
crate::conn_l! {
pub ODTMNANO : Extended<OffsetDateTime> => i128 {
ceil: odtmnano_ceil,
inner: odtmnano_inner,
}
}
fn odtmsecs_ceil(o: Extended<OffsetDateTime>) -> i64 {
let min_s = utc_min_s();
let max_s = utc_max_s();
match o {
Extended::NegInf => i64::MIN,
Extended::Finite(odt) => {
let s = odt.unix_timestamp();
if s < min_s {
min_s
} else if s >= max_s {
if s > max_s || odt.nanosecond() != 0 {
max_s + 1
} else {
max_s
}
} else if odt.nanosecond() != 0 {
s + 1
} else {
s
}
}
Extended::PosInf => max_s + 1,
}
}
fn odtmsecs_inner(s: i64) -> Extended<OffsetDateTime> {
let min_s = utc_min_s();
let max_s = utc_max_s();
if s < min_s {
Extended::NegInf
} else if s > max_s {
Extended::PosInf
} else {
match OffsetDateTime::from_unix_timestamp(s) {
Ok(odt) => Extended::Finite(odt),
Err(_) => unreachable!("s in [utc_min_s, utc_max_s]"),
}
}
}
crate::conn_l! {
pub ODTMSECS : Extended<OffsetDateTime> => i64 {
ceil: odtmsecs_ceil,
inner: odtmsecs_inner,
}
}
#[cfg(test)]
mod odtm_tests {
use super::*;
#[allow(unused_imports)]
use crate::conn::{ConnL, ConnR};
use crate::prop::arb::{
arb_extended_offset_dt, arb_offset_dt, arb_unix_nanos_in_range, arb_unix_secs_in_range,
};
use crate::prop::{conn as conn_laws, lattice as lattice_laws};
use proptest::prelude::*;
use time::Duration;
mod offset_dt_preorder {
use super::*;
proptest! {
#[test]
fn reflexive(x in arb_offset_dt()) {
prop_assert!(lattice_laws::lattice_reflexive(&x));
}
#[test]
fn transitive(x in arb_offset_dt(), y in arb_offset_dt(), z in arb_offset_dt()) {
prop_assert!(lattice_laws::lattice_transitive(&x, &y, &z));
}
#[test]
fn antisymmetric(x in arb_offset_dt(), y in arb_offset_dt()) {
prop_assert!(lattice_laws::lattice_antisymmetric(&x, &y));
}
#[test]
fn bot(x in arb_offset_dt()) {
prop_assert!(lattice_laws::lattice_bot(&odtm_min_instant(), &x));
}
#[test]
fn top(x in arb_offset_dt()) {
prop_assert!(lattice_laws::lattice_top(&odtm_max_instant(), &x));
}
}
}
#[test]
fn epoch_is_zero() {
assert_eq!(
ODTMNANO.ceil(Extended::Finite(OffsetDateTime::UNIX_EPOCH)),
0
);
assert_eq!(
ODTMNANO.upper(0),
Extended::Finite(OffsetDateTime::UNIX_EPOCH)
);
}
#[test]
fn one_ns_past_epoch() {
let mid = OffsetDateTime::UNIX_EPOCH + Duration::nanoseconds(1);
assert_eq!(ODTMNANO.ceil(Extended::Finite(mid)), 1);
assert_eq!(ODTMNANO.upper(1), Extended::Finite(mid));
}
#[test]
fn one_ns_before_epoch() {
let mid = OffsetDateTime::UNIX_EPOCH - Duration::nanoseconds(1);
assert_eq!(ODTMNANO.ceil(Extended::Finite(mid)), -1);
assert_eq!(ODTMNANO.upper(-1), Extended::Finite(mid));
}
#[test]
fn y2038() {
let y2038_ns: i128 = 2_147_483_648_000_000_000;
let y2038 = OffsetDateTime::from_unix_timestamp_nanos(y2038_ns).unwrap();
assert_eq!(ODTMNANO.ceil(Extended::Finite(y2038)), y2038_ns);
assert_eq!(ODTMNANO.upper(y2038_ns), Extended::Finite(y2038));
}
#[test]
fn utc_extremes_round_trip() {
let min_utc = OffsetDateTime::new_in_offset(Date::MIN, Time::MIDNIGHT, UtcOffset::UTC);
let max_utc = OffsetDateTime::new_in_offset(
Date::MAX,
Time::from_hms_nano(23, 59, 59, 999_999_999).unwrap(),
UtcOffset::UTC,
);
let min_ns = utc_min_ns();
let max_ns = utc_max_ns();
assert_eq!(ODTMNANO.ceil(Extended::Finite(min_utc)), min_ns);
assert_eq!(ODTMNANO.ceil(Extended::Finite(max_utc)), max_ns);
assert_eq!(ODTMNANO.upper(min_ns), Extended::Finite(min_utc));
assert_eq!(ODTMNANO.upper(max_ns), Extended::Finite(max_utc));
}
#[test]
fn instant_extreme_saturates() {
let max_inst = odtm_max_instant();
assert_eq!(ODTMNANO.ceil(Extended::Finite(max_inst)), utc_max_ns() + 1);
let min_inst = odtm_min_instant();
assert_eq!(ODTMNANO.ceil(Extended::Finite(min_inst)), utc_min_ns());
}
#[test]
fn odtmnano_saturation_extremes() {
assert_eq!(ODTMNANO.upper(i128::MAX), Extended::PosInf);
assert_eq!(ODTMNANO.upper(i128::MIN), Extended::NegInf);
let max_ns = utc_max_ns();
assert_eq!(ODTMNANO.ceil(Extended::PosInf), max_ns + 1);
assert_eq!(ODTMNANO.ceil(Extended::NegInf), i128::MIN);
assert_eq!(ODTMNANO.ceil(Extended::PosInf), max_ns + 1);
assert_eq!(ODTMNANO.ceil(Extended::NegInf), i128::MIN);
}
#[test]
fn odtmsecs_epoch() {
assert_eq!(
ODTMSECS.ceil(Extended::Finite(OffsetDateTime::UNIX_EPOCH)),
0
);
assert_eq!(
ODTMSECS.upper(0),
Extended::Finite(OffsetDateTime::UNIX_EPOCH)
);
}
#[test]
fn odtmsecs_subsec_rounds_up() {
let mid = OffsetDateTime::UNIX_EPOCH + Duration::nanoseconds(1);
assert_eq!(ODTMSECS.ceil(Extended::Finite(mid)), 1);
}
#[test]
fn odtmsecs_negative_subsec() {
let half_before = OffsetDateTime::UNIX_EPOCH - Duration::milliseconds(500);
assert_eq!(ODTMSECS.ceil(Extended::Finite(half_before)), 0);
}
#[test]
fn odtmsecs_saturation_extremes() {
let max_s = utc_max_s();
assert_eq!(ODTMSECS.ceil(Extended::PosInf), max_s + 1);
assert_eq!(ODTMSECS.ceil(Extended::NegInf), i64::MIN);
assert_eq!(ODTMSECS.ceil(Extended::PosInf), max_s + 1);
assert_eq!(ODTMSECS.ceil(Extended::NegInf), i64::MIN);
}
proptest! {
#[test]
fn odtmnano_galois_l(o in arb_extended_offset_dt(), b in any::<i128>()) {
prop_assert!(conn_laws::galois_l(&ODTMNANO, o, b));
}
#[test]
fn odtmnano_closure_l(o in arb_extended_offset_dt()) {
prop_assert!(conn_laws::closure_l(&ODTMNANO, o));
}
#[test]
fn odtmnano_kernel_l(b in any::<i128>()) {
prop_assert!(conn_laws::kernel_l(&ODTMNANO, b));
}
#[test]
fn odtmnano_monotone_l(a in arb_extended_offset_dt(), b in arb_extended_offset_dt()) {
prop_assert!(conn_laws::monotone_l(&ODTMNANO, a, b));
}
#[test]
fn odtmnano_idempotent(o in arb_extended_offset_dt()) {
prop_assert!(conn_laws::idempotent(&ODTMNANO, o));
}
#[test]
fn odtmnano_roundtrip_ceil(b in arb_unix_nanos_in_range()) {
prop_assert!(conn_laws::roundtrip_ceil(&ODTMNANO, b));
}
}
proptest! {
#[test]
fn odtmsecs_galois_l(o in arb_extended_offset_dt(), b in any::<i64>()) {
prop_assert!(conn_laws::galois_l(&ODTMSECS, o, b));
}
#[test]
fn odtmsecs_closure_l(o in arb_extended_offset_dt()) {
prop_assert!(conn_laws::closure_l(&ODTMSECS, o));
}
#[test]
fn odtmsecs_kernel_l(b in any::<i64>()) {
prop_assert!(conn_laws::kernel_l(&ODTMSECS, b));
}
#[test]
fn odtmsecs_monotone_l(a in arb_extended_offset_dt(), b in arb_extended_offset_dt()) {
prop_assert!(conn_laws::monotone_l(&ODTMSECS, a, b));
}
#[test]
fn odtmsecs_idempotent(o in arb_extended_offset_dt()) {
prop_assert!(conn_laws::idempotent(&ODTMSECS, o));
}
#[test]
fn odtmsecs_roundtrip_ceil(b in arb_unix_secs_in_range()) {
prop_assert!(conn_laws::roundtrip_ceil(&ODTMSECS, b));
}
}
}