156 lines
4.8 KiB
Rust
156 lines
4.8 KiB
Rust
|
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<cmp::Ordering> {
|
||
|
Some(self.cmp(other))
|
||
|
}
|
||
|
}
|