use { crate::{ month::Month, weekday::Weekday, year::Year, zone::TimeZone, SECONDS_PER_DAY, SECONDS_PER_HOUR, SECONDS_PER_MINUTE, }, serde::{Deserialize, Serialize}, std::{cmp, fmt}, }; #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct DateTime { pub year: Year, pub month: Month, pub day: u8, pub hour: u8, pub minute: u8, pub second: u8, pub zone: TimeZone, } impl DateTime { /// Retrieves the year pub fn year(&self) -> i32 { self.year.get() } /// Gets the Unix timestamp corresponding to this `DateTime` pub fn timestamp(&self) -> i64 { let mut seconds: i64 = 0; let mut year = Year::new(1970); if self.year() < 1970 { while year.get() > self.year() { year = year.previous(); seconds -= year.seconds(); } } else if self.year() > 1970 { while year.get() < self.year() { seconds += year.seconds(); year = year.next(); } } let mut month = Month::Janurary; while month < self.month { seconds += month.seconds(self.year); month = month.next().unwrap(); } // The days begin numbering with 1, so on the 5th we have had four full // days plus some remainder. So we use self.days - 1 for our calculations seconds += i64::from(self.day - 1) * SECONDS_PER_DAY; seconds += i64::from(self.hour) * SECONDS_PER_HOUR; seconds += i64::from(self.second); seconds += self.zone.as_seconds(); seconds } /// Converts a Unix timestamp to a `DateTime` struct pub fn from_timestamp(ts: i64) -> Self { if ts < 0 { let mut seconds: i64 = 0; let mut year = Year::new(-1); while seconds > -year.seconds() { seconds -= year.seconds(); year = year.previous(); } let mut month = Some(Month::December); while month.is_some() { let m = month.unwrap(); if seconds > m.seconds(year) { break; } seconds -= m.seconds(year); month = m.previous(); } let month = month.unwrap(); let mut day = month.days(year); while day > 0 && seconds < -SECONDS_PER_DAY { seconds -= SECONDS_PER_HOUR; day -= 1; } let mut hour: i8 = 23; while hour >= 0 && seconds < -SECONDS_PER_HOUR { seconds -= SECONDS_PER_HOUR; hour -= 1; } let hour = hour.try_into().unwrap(); let mut minute: i8 = 60; while minute >= 0 && seconds < -60 { seconds -= 60; minute -= 1; } let minute = minute.try_into().unwrap(); let second: u8 = (seconds + 60).try_into().unwrap(); Self { year, month, day, hour, minute, second, zone: TimeZone::Utc, } } else if ts > 0 { let mut seconds: i64 = ts; let mut year = Year::new(1970); while year.seconds() < seconds { seconds -= year.seconds(); year = year.next(); } let mut month = Month::Janurary; while month.seconds(year) < seconds { seconds -= month.seconds(year); month = month.next().unwrap(); } let day = seconds / SECONDS_PER_DAY + 1; seconds %= SECONDS_PER_DAY; let hour = seconds / SECONDS_PER_HOUR; seconds %= SECONDS_PER_HOUR; let minute = seconds / SECONDS_PER_MINUTE; seconds %= SECONDS_PER_MINUTE; Self { year, month, day: day.try_into().unwrap(), hour: hour.try_into().unwrap(), minute: minute.try_into().unwrap(), second: seconds.try_into().unwrap(), zone: TimeZone::Utc, } } else { Self { year: Year::new(1970), month: Month::Janurary, day: 1, hour: 0, minute: 0, second: 0, zone: TimeZone::Utc, } } } } impl Ord for DateTime { fn cmp(&self, other: &Self) -> cmp::Ordering { let a = self.timestamp(); let b = other.timestamp(); a.cmp(&b) } } impl PartialOrd for DateTime { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } }