picolog 1.0.1

Tiny logging implementation for the log crate
Documentation
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; // May 18 2033 03:33:20 UTC+0

        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());
        }
    }
}