use std::fmt;
use std::time::{SystemTime, UNIX_EPOCH};
pub struct Timestamp {
year: u16,
month: u16,
day: u16,
hours: u8,
minutes: u8,
seconds: u8,
}
impl Timestamp {
pub fn new() -> Self {
let seconds = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("current time before Unix epoch")
.as_secs();
Self::from_unix(seconds)
}
fn from_unix(total_seconds: u64) -> Self {
let total_minutes = total_seconds / 60;
let total_hours = total_minutes / 60;
let seconds = (total_seconds % 60) as u8;
let minutes = (total_minutes % 60) as u8;
let hours = (total_hours % 24) as u8;
let mut month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
let mut day = total_hours / 24;
let mut year = 1970;
let mut month = 0;
loop {
if is_leap_year(year) {
if day < 366 {
break;
}
day -= 366;
} else {
if day < 365 {
break;
}
day -= 365;
}
year += 1;
}
day += 1;
if is_leap_year(year) {
month_lengths[1] = 29;
}
while day > month_lengths[month as usize] {
day -= month_lengths[month as usize];
month += 1;
}
month += 1;
#[allow(clippy::cast_possible_truncation)]
let day = day as u16;
Self {
year,
month,
day,
hours,
minutes,
seconds,
}
}
}
impl fmt::Display for Timestamp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}-{:02}-{:02} {:02}:{:02}:{:02}",
self.year, self.month, self.day, self.hours, self.minutes, self.seconds
)
}
}
fn is_leap_year(year: u16) -> bool {
year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)
}
#[cfg(test)]
mod tests {
use super::Timestamp;
#[test]
fn test() {
let upper_bound = 2000000000;
for u in 0..upper_bound {
let a = Timestamp::from_unix(u);
let b = time::OffsetDateTime::from_unix_timestamp(u as i64).unwrap();
assert_eq!(a.year, b.year() as u16);
assert_eq!(a.month, b.month() as u16);
assert_eq!(a.day, b.day() as u16);
assert_eq!(a.hours, b.hour());
assert_eq!(a.minutes, b.minute());
assert_eq!(a.seconds, b.second());
}
}
}