use crate::{ timezone::{Utc, UtcOffset}, Date, Month, Time, TimeZone, UnixTimestamp, Year, }; use core::{cmp::Ordering, fmt::Display, hash::Hash}; #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct NaiveDateTime { date: Date, time: Time, } #[derive(Copy, Clone, Eq, Debug)] pub struct DateTime { utc_datetime: NaiveDateTime, timezone: Tz, } impl DateTime { // TODO unix epoch constant // TODO docs pub fn from_utc(utc_datetime: NaiveDateTime, timezone: Tz) -> Self { Self { utc_datetime, timezone, } } pub fn offset(&self) -> UtcOffset { let utc = DateTime::::from_utc(self.utc_datetime, Utc); self.timezone.utc_offset(utc) } pub fn timezone(&self) -> &Tz { &self.timezone } pub fn naive_utc(&self) -> NaiveDateTime { self.utc_datetime } } impl NaiveDateTime { // TODO docs #[must_use] pub const fn new(date: Date, time: Time) -> Self { Self { date, time } } #[must_use] pub const fn date(self) -> Date { self.date } #[must_use] pub const fn time(self) -> Time { self.time } #[must_use] pub const fn year(self) -> Year { self.date.year() } #[must_use] pub const fn month(self) -> Month { self.date.month() } #[must_use] pub const fn day(self) -> u8 { self.date.day() } #[must_use] pub const fn hour(self) -> u8 { self.time.hour() } #[must_use] pub const fn minute(self) -> u8 { self.time.minute() } #[must_use] pub const fn second(self) -> u8 { self.time.second() } #[must_use] pub const fn millisecond(self) -> u16 { self.time.millisecond() } #[must_use] pub const fn microsecond(self) -> u32 { self.time.microsecond() } #[must_use] pub const fn nanosecond(self) -> u32 { self.time.nanosecond() } #[must_use] pub fn add_seconds_overflowing(self, seconds: i64) -> (Self, bool) { let timestamp: UnixTimestamp = self.into(); let (timestamp, overflow) = timestamp.add_seconds_overflowing(seconds); let datetime: NaiveDateTime = timestamp.into(); (datetime, overflow) } #[must_use] pub fn add_nanoseconds_overflowing(self, nanoseconds: i64) -> (Self, bool) { let timestamp: UnixTimestamp = self.into(); let (timestamp, overflow) = timestamp.add_nanoseconds_overflowing(nanoseconds); let datetime: NaiveDateTime = timestamp.into(); (datetime, overflow) } } impl PartialOrd for NaiveDateTime { fn partial_cmp(&self, other: &Self) -> Option { let date_ordering = self.date.cmp(&other.date); let time_ordering = self.time.cmp(&other.time); if date_ordering != Ordering::Equal { Some(date_ordering) } else if time_ordering != Ordering::Equal { Some(time_ordering) } else { Some(Ordering::Equal) } } } impl Ord for NaiveDateTime { fn cmp(&self, other: &Self) -> Ordering { let date_ordering = self.date.cmp(&other.date); let time_ordering = self.time.cmp(&other.time); if date_ordering != Ordering::Equal { date_ordering } else if time_ordering != Ordering::Equal { time_ordering } else { Ordering::Equal } } } // TODO think harder about the fact that we don't consider timezone (how will UtcOffset work) impl PartialEq> for DateTime { fn eq(&self, other: &DateTime) -> bool { self.utc_datetime == other.utc_datetime } } impl Hash for DateTime { fn hash(&self, state: &mut H) { self.utc_datetime.hash(state); } } impl PartialOrd> for DateTime { fn partial_cmp(&self, other: &DateTime) -> Option { self.utc_datetime.partial_cmp(&other.utc_datetime) } } impl Ord for DateTime { fn cmp(&self, other: &Self) -> Ordering { self.utc_datetime.cmp(&other.utc_datetime) } } impl Display for NaiveDateTime { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{} {}", self.date, self.time) } } impl Display for DateTime { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{} {}", self.utc_datetime, self.timezone) } } impl From for NaiveDateTime { fn from(timestamp: UnixTimestamp) -> Self { const UNIX_EPOCH_DAYS_AFTER_CE: i64 = Date::UNIX_EPOCH.days_after_common_era(); let days_after_unix_epoch = timestamp.seconds_since_unix_epoch() / 86_400; let days_after_ce = days_after_unix_epoch + UNIX_EPOCH_DAYS_AFTER_CE as i64; let date = Date::from_days_after_common_era(days_after_ce); let seconds_after_midnight = timestamp.seconds_since_unix_epoch() % 86_400; let nanoseconds = timestamp.nanosecond(); let time = Time::MIDNIGHT .add_seconds(seconds_after_midnight as isize) .add_nanoseconds(nanoseconds as isize); Self::new(date, time) } }