use std::ops::Add;
use std::time::{Duration, SystemTime};
fn is_leap_year(year: i64) -> bool {
if year % 400 == 0 {
true
} else if year % 100 == 0 {
false
} else {
year % 4 == 0
}
}
fn year_len_days(year: i64) -> i64 {
if is_leap_year(year) {
366
} else {
365
}
}
#[allow(clippy::missing_panics_doc)]
#[allow(clippy::match_same_arms)]
#[must_use]
pub fn month_len_days(year: i64, month: i64) -> i64 {
match month {
1 => 31,
2 if (year % 400) == 0 => 29,
2 if (year % 100) == 0 => 28,
2 if (year % 4) == 0 => 29,
2 => 28,
3 => 31,
4 => 30,
5 => 31,
6 => 30,
7 => 31,
8 => 31,
9 => 30,
10 => 31,
11 => 30,
12 => 31,
_ => unimplemented!(),
}
}
#[allow(clippy::module_name_repetitions)]
pub struct DateTime {
pub year: i64,
pub month: i64,
pub day: i64,
pub hour: i64,
pub min: i64,
pub sec: i64,
}
impl DateTime {
#[must_use]
pub fn new(epoch_seconds: i64) -> Self {
let mut dt = Self {
year: 1970,
month: 1,
day: 1,
hour: 0,
min: 0,
sec: epoch_seconds,
};
dt.balance();
dt
}
fn balance_month(&mut self) {
let delta_years = if self.month > 12 {
(self.month - 1) / 12
} else {
return;
};
self.year += delta_years;
self.month -= 12 * delta_years;
assert!((1..=12).contains(&self.month));
}
fn balance_day(&mut self) {
self.balance_month();
while self.day > 366 {
self.day -= year_len_days(self.year);
self.year += 1;
}
while self.day > month_len_days(self.year, self.month) {
self.day -= month_len_days(self.year, self.month);
self.month += 1;
self.balance_month();
}
}
fn balance_hour(&mut self) {
let delta_days = if self.hour > 23 {
self.hour / 24
} else {
self.balance_day();
return;
};
self.day += delta_days;
self.hour -= 24 * delta_days;
assert!((0..24).contains(&self.hour));
self.balance_day();
}
fn balance_min(&mut self) {
let delta_hours = if self.min > 59 {
self.min / 60
} else {
self.balance_hour();
return;
};
self.hour += delta_hours;
self.min -= 60 * delta_hours;
assert!((0..60).contains(&self.min));
self.balance_hour();
}
#[allow(clippy::missing_panics_doc)]
pub fn balance(&mut self) {
let delta_mins = if self.sec > 59 {
self.sec / 60
} else {
self.balance_min();
return;
};
self.min += delta_mins;
self.sec -= 60 * delta_mins;
assert!((0..60).contains(&self.sec));
self.balance_min();
}
}
impl Add<Duration> for DateTime {
type Output = DateTime;
fn add(mut self, rhs: Duration) -> Self::Output {
self.sec += i64::try_from(rhs.as_secs()).unwrap();
self.balance();
self
}
}
#[allow(clippy::module_name_repetitions)]
pub trait FormatTime {
fn iso8601_utc(&self) -> String;
}
impl FormatTime for SystemTime {
fn iso8601_utc(&self) -> String {
let epoch_seconds = i64::try_from(
self.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs(),
)
.unwrap();
let dt = DateTime::new(epoch_seconds);
format!(
"{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z",
dt.year, dt.month, dt.day, dt.hour, dt.min, dt.sec
)
}
}