use alloc::{fmt, format, string::String};
use serde::{Deserialize, Serialize};
const fn is_leap_year(year: u16) -> bool {
(year.is_multiple_of(4) && !year.is_multiple_of(100)) || year.is_multiple_of(400)
}
const DAYS_IN_MONTH: [[u16; 12]; 2] = [
[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], ];
#[derive(Debug, PartialEq, Ord, PartialOrd, Eq, Clone, Copy, Default, Serialize, Deserialize)]
pub struct Date {
pub year: u16,
pub month: u8,
pub day: u8,
pub hour: u8,
pub minute: u8,
pub second: u8,
}
impl Date {
pub fn new(year: u16, month: u8, day: u8) -> Date {
Date { year, month, day, hour: 0, minute: 0, second: 0 }
}
pub fn new_full(year: u16, month: u8, day: u8, hour: u8, minute: u8, second: u8) -> Date {
Date { year, month, day, hour, minute, second }
}
pub fn from_time(time: i64) -> Date {
let mut date = Date::default();
date.set_time(time);
date
}
pub fn set_time(&mut self, time: i64) {
let mut days = time / 86_400_000;
let mut ms_remaining = time % 86_400_000;
if ms_remaining < 0 {
ms_remaining += 86_400_000;
days -= 1;
}
let mut year = 1970;
loop {
let year_days = if is_leap_year(year) { 366 } else { 365 };
if days < year_days {
break;
}
days -= year_days;
year += 1;
}
let leap = is_leap_year(year) as usize;
let mut month = 0;
while days >= DAYS_IN_MONTH[leap][month] as i64 {
days -= DAYS_IN_MONTH[leap][month] as i64;
month += 1;
}
self.year = year;
self.month = (month + 1) as u8;
self.day = (days + 1) as u8;
self.hour = (ms_remaining / 3_600_000) as u8;
self.minute = ((ms_remaining % 3_600_000) / 60_000) as u8;
self.second = ((ms_remaining % 60_000) / 1_000) as u8;
}
pub fn get_time(&self) -> i64 {
let mut days = 0;
for y in 1970..self.year {
days += if is_leap_year(y) { 366 } else { 365 };
}
let leap = is_leap_year(self.year) as usize;
for m in 0..(self.month as usize - 1) {
days += DAYS_IN_MONTH[leap][m % 12] as i64;
}
days += self.day as i64 - 1;
days * 86_400_000
+ (self.hour as i64 * 3_600_000)
+ (self.minute as i64 * 60_000)
+ (self.second as i64 * 1_000)
}
pub fn to_iso_string(&self) -> String {
format!(
"{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.000Z",
self.year, self.month, self.day, self.hour, self.minute, self.second
)
}
}
impl From<&str> for Date {
fn from(s: &str) -> Date {
if let Ok(ms) = s.parse::<i64>() {
return Date::from_time(ms);
}
let mut date = Date::default();
let year = s[0..4].parse().unwrap();
let month = s[5..7].parse().unwrap();
let day = s[8..10].parse().unwrap();
let mut hour = 0;
let mut minute = 0;
let mut second = 0;
if s.len() >= 19 {
hour = s[11..13].parse().unwrap();
minute = s[14..16].parse().unwrap();
second = s[17..19].parse().unwrap();
}
date.year = year;
date.month = month;
date.day = day;
date.hour = hour;
date.minute = minute;
date.second = second;
date
}
}
impl fmt::Display for Date {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:04}{:02}{:02}", self.year, self.month + 1, self.day)
}
}