mod sealed {
pub trait Sealed {}
}
pub trait Date: Clone + PartialOrd + sealed::Sealed {
#[doc(hidden)]
fn from_ymd(year: i32, month: u8, day: u8) -> Option<Self>;
#[doc(hidden)]
fn offset_months(self, months: i32) -> Option<Self>;
#[doc(hidden)]
fn offset_days(self, days: i64) -> Option<Self>;
#[doc(hidden)]
fn year(&self) -> i32;
#[doc(hidden)]
fn weekday(&self) -> u8;
}
pub trait Time: Clone + PartialOrd + sealed::Sealed {
#[doc(hidden)]
fn from_hms(h: u32, m: u32, s: u32) -> Option<Self>;
#[doc(hidden)]
fn with_micros(self, ms: u32) -> Option<Self>;
}
pub trait DateTime: Sized + sealed::Sealed {
type TimeZone;
type Date: Date;
type Time: Time;
#[doc(hidden)]
fn new(tz: Self::TimeZone, date: Self::Date, time: Self::Time) -> Self;
#[doc(hidden)]
fn split(self) -> (Self::TimeZone, Self::Date, Self::Time);
#[doc(hidden)]
fn with_offset(self, secs: i64) -> Option<Self>;
#[doc(hidden)]
fn offset_seconds(self, secs: i64) -> Option<Self>;
}
#[cfg(feature = "chrono_0_4")]
mod chrono {
use chrono::{Duration, NaiveDate, NaiveTime, Offset, TimeZone, Timelike};
impl super::sealed::Sealed for NaiveDate {}
impl super::sealed::Sealed for NaiveTime {}
impl<Tz: TimeZone> super::sealed::Sealed for chrono::DateTime<Tz> {}
use super::{Date, DateTime, Time};
#[cfg_attr(docsrs, doc(cfg(feature = "chrono_0_4")))]
impl Date for NaiveDate {
fn from_ymd(year: i32, month: u8, day: u8) -> Option<Self> {
NaiveDate::from_ymd_opt(year, month as u32, day as u32)
}
fn offset_months(self, months: i32) -> Option<Self> {
if months >= 0 {
self.checked_add_months(chrono::Months::new(months as u32))
} else {
self.checked_sub_months(chrono::Months::new(-months as u32))
}
}
fn offset_days(self, days: i64) -> Option<Self> {
self.checked_add_signed(Duration::days(days))
}
fn year(&self) -> i32 {
chrono::Datelike::year(self)
}
fn weekday(&self) -> u8 {
chrono::Datelike::weekday(self).num_days_from_monday() as u8
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "chrono_0_4")))]
impl Time for NaiveTime {
fn from_hms(h: u32, m: u32, s: u32) -> Option<Self> {
NaiveTime::from_hms_opt(h, m, s)
}
fn with_micros(self, ms: u32) -> Option<Self> {
self.with_nanosecond(ms.checked_mul(1_000)?)
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "chrono_0_4")))]
impl<Tz: TimeZone> DateTime for chrono::DateTime<Tz> {
type TimeZone = Tz;
type Date = NaiveDate;
type Time = NaiveTime;
fn new(tz: Self::TimeZone, date: Self::Date, time: Self::Time) -> Self {
let datetime = date.and_time(time);
let offset = tz.offset_from_utc_datetime(&datetime);
Self::from_naive_utc_and_offset(datetime - offset.fix(), offset)
}
fn split(self) -> (Self::TimeZone, Self::Date, Self::Time) {
(self.timezone(), self.date_naive(), self.time())
}
fn with_offset(self, secs: i64) -> Option<Self> {
let offset = self
.timezone()
.offset_from_utc_date(&self.date_naive())
.fix()
.local_minus_utc() as i64;
self.offset_seconds(offset - secs)
}
fn offset_seconds(self, secs: i64) -> Option<Self> {
self.checked_add_signed(Duration::seconds(secs))
}
}
}
#[cfg(feature = "time_0_3")]
mod time {
use super::{Date, DateTime, Time};
impl super::sealed::Sealed for time::Date {}
impl super::sealed::Sealed for time::Time {}
impl super::sealed::Sealed for time::OffsetDateTime {}
#[cfg_attr(docsrs, doc(cfg(feature = "time_0_3")))]
impl Date for time::Date {
fn from_ymd(year: i32, month: u8, day: u8) -> Option<Self> {
time::Date::from_calendar_date(year, time::Month::try_from(month).ok()?, day).ok()
}
fn offset_months(self, months: i32) -> Option<Self> {
let (mut y, mut m, d) = self.to_calendar_date();
y += months / 12;
let mut months = months % 12;
if months < 0 {
months += 12;
y -= 1;
}
let mut m1 = m as u8 + months as u8;
if m1 > 12 {
m1 -= 12;
y += 1;
}
m = time::Month::try_from(m1).ok()?;
let max_day = m.length(y);
let d = d.min(max_day);
time::Date::from_calendar_date(y, m, d).ok()
}
fn offset_days(self, days: i64) -> Option<Self> {
self.checked_add(time::Duration::days(days))
}
fn year(&self) -> i32 {
time::Date::year(*self)
}
fn weekday(&self) -> u8 {
time::Date::weekday(*self).number_days_from_monday()
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "time_0_3")))]
impl Time for time::Time {
fn from_hms(h: u32, m: u32, s: u32) -> Option<Self> {
time::Time::from_hms(
u8::try_from(h).ok()?,
u8::try_from(m).ok()?,
u8::try_from(s).ok()?,
)
.ok()
}
fn with_micros(self, ms: u32) -> Option<Self> {
self.replace_microsecond(ms).ok()
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "time_0_3")))]
impl DateTime for time::OffsetDateTime {
type TimeZone = time::UtcOffset;
type Date = time::Date;
type Time = time::Time;
fn new(tz: Self::TimeZone, date: Self::Date, time: Self::Time) -> Self {
time::PrimitiveDateTime::new(date, time).assume_offset(tz)
}
fn split(self) -> (Self::TimeZone, Self::Date, Self::Time) {
(self.offset(), self.date(), self.time())
}
fn with_offset(self, secs: i64) -> Option<Self> {
let offset = self.offset().whole_seconds() as i64;
self.offset_seconds(offset - secs)
}
fn offset_seconds(self, secs: i64) -> Option<Self> {
self.checked_add(time::Duration::seconds(secs))
}
}
}
#[cfg(feature = "jiff_0_1")]
mod jiff_0_1 {
use jiff::Span;
use jiff_0_1 as jiff;
use super::{Date, DateTime, Time};
impl super::sealed::Sealed for jiff::civil::Date {}
impl super::sealed::Sealed for jiff::civil::Time {}
impl super::sealed::Sealed for jiff::Zoned {}
#[cfg_attr(docsrs, doc(cfg(feature = "jiff_0_1")))]
impl Date for jiff::civil::Date {
fn from_ymd(year: i32, month: u8, day: u8) -> Option<Self> {
jiff::civil::Date::new(year as i16, month as i8, day as i8).ok()
}
fn offset_months(self, months: i32) -> Option<Self> {
self.checked_add(Span::new().months(months)).ok()
}
fn offset_days(self, days: i64) -> Option<Self> {
self.checked_add(Span::new().days(days)).ok()
}
fn year(&self) -> i32 {
jiff::civil::Date::year(*self) as i32
}
fn weekday(&self) -> u8 {
jiff::civil::Date::weekday(*self).to_monday_zero_offset() as u8
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "jiff_0_1")))]
impl Time for jiff::civil::Time {
fn from_hms(h: u32, m: u32, s: u32) -> Option<Self> {
jiff::civil::Time::new(
i8::try_from(h).ok()?,
i8::try_from(m).ok()?,
i8::try_from(s).ok()?,
0,
)
.ok()
}
fn with_micros(self, ms: u32) -> Option<Self> {
jiff::civil::Time::new(
self.hour(),
self.minute(),
self.second(),
i32::try_from(ms).ok()?,
)
.ok()
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "jiff_0_1")))]
impl DateTime for jiff::Zoned {
type TimeZone = jiff::tz::TimeZone;
type Date = jiff::civil::Date;
type Time = jiff::civil::Time;
fn new(tz: Self::TimeZone, date: Self::Date, time: Self::Time) -> Self {
tz.to_ambiguous_zoned(date.to_datetime(time))
.compatible()
.unwrap()
}
fn split(self) -> (Self::TimeZone, Self::Date, Self::Time) {
(self.time_zone().clone(), self.date(), self.time())
}
fn with_offset(self, secs: i64) -> Option<Self> {
let offset = self.time_zone().to_offset(self.timestamp()).0.seconds() as i64;
self.offset_seconds(offset - secs)
}
fn offset_seconds(self, secs: i64) -> Option<Self> {
self.checked_add(jiff::Span::new().seconds(secs)).ok()
}
}
}
#[cfg(feature = "jiff_0_2")]
mod jiff_0_2 {
use jiff::Span;
use jiff_0_2 as jiff;
use super::{Date, DateTime, Time};
impl super::sealed::Sealed for jiff::civil::Date {}
impl super::sealed::Sealed for jiff::civil::Time {}
impl super::sealed::Sealed for jiff::Zoned {}
#[cfg_attr(docsrs, doc(cfg(feature = "jiff_0_1")))]
impl Date for jiff::civil::Date {
fn from_ymd(year: i32, month: u8, day: u8) -> Option<Self> {
jiff::civil::Date::new(year as i16, month as i8, day as i8).ok()
}
fn offset_months(self, months: i32) -> Option<Self> {
self.checked_add(Span::new().months(months)).ok()
}
fn offset_days(self, days: i64) -> Option<Self> {
self.checked_add(Span::new().days(days)).ok()
}
fn year(&self) -> i32 {
jiff::civil::Date::year(*self) as i32
}
fn weekday(&self) -> u8 {
jiff::civil::Date::weekday(*self).to_monday_zero_offset() as u8
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "jiff_0_1")))]
impl Time for jiff::civil::Time {
fn from_hms(h: u32, m: u32, s: u32) -> Option<Self> {
jiff::civil::Time::new(
i8::try_from(h).ok()?,
i8::try_from(m).ok()?,
i8::try_from(s).ok()?,
0,
)
.ok()
}
fn with_micros(self, ms: u32) -> Option<Self> {
jiff::civil::Time::new(
self.hour(),
self.minute(),
self.second(),
i32::try_from(ms).ok()?,
)
.ok()
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "jiff_0_1")))]
impl DateTime for jiff::Zoned {
type TimeZone = jiff::tz::TimeZone;
type Date = jiff::civil::Date;
type Time = jiff::civil::Time;
fn new(tz: Self::TimeZone, date: Self::Date, time: Self::Time) -> Self {
tz.to_ambiguous_zoned(date.to_datetime(time))
.compatible()
.unwrap()
}
fn split(self) -> (Self::TimeZone, Self::Date, Self::Time) {
(self.time_zone().clone(), self.date(), self.time())
}
fn with_offset(self, secs: i64) -> Option<Self> {
let offset = self.time_zone().to_offset(self.timestamp()).seconds() as i64;
self.offset_seconds(offset - secs)
}
fn offset_seconds(self, secs: i64) -> Option<Self> {
self.checked_add(jiff::Span::new().seconds(secs)).ok()
}
}
}