Search code examples
datetimerustunix-timestamp

Unix epoch to date String – Slightly wrong month day


I am trying to format SystemTime as a String in Rust without using external crates, but I am facing a bizarre issue regarding the "day" field. Can anyone explain me why the line preceded by the "FIXME" comment yields slightly wrong month days as the time distance from Thu, 01 Jan 1970 00:00:00 GMT increases?

fn time_to_http_date_string(time: &SystemTime) -> String {
    const MINUTE: u64 = 60;
    const HOUR: u64 = 3600;
    const DAY: u64 = 86400;
    const WEEKDAYS: [&str; 7] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
    const MONTH: u64 = 2629743;
    const MONTHS: [&str; 12] = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
    const YEAR: u64 = 31556926;

    let time = time.duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
    let year = 1970 + (time / YEAR);
    let month = MONTHS[(time % YEAR / MONTH) as usize];
    let weekday = WEEKDAYS[((time / DAY + 4) % 7) as usize];

    // FIXME: Slightly wrong as time distance from the UNIX epoch increases.
    let day = (time % MONTH / DAY) + 1;

    let hour = time % DAY / HOUR;
    let minute = time % HOUR / MINUTE;
    let second = time % MINUTE;

    format!(
        "{weekday}, {day:02} {month} {year} {hour:02}:{minute:02}:{second:02} GMT",
        weekday = weekday,
        day = day,
        month = month,
        year = year,
        hour = hour,
        minute = minute,
        second = second,
    )
}

Let me give you more details about this issue; here are some tests I ran this function through:

Unix Timestamp Expected Mine OK
0 Thu, 01 Jan 1970 00:00:00 GMT Thu, 01 Jan 1970 00:00:00 GMT 👍
68169600 Tue, 29 Feb 1972 00:00:00 GMT Tue, 29 Feb 1972 00:00:00 GMT 👍
874540800 Thu, 18 Sep 1997 00:00:00 GMT Thu, 17 Sep 1997 00:00:00 GMT
1052790840 Tue, 13 May 2003 01:54:00 GMT Tue, 11 May 2003 01:54:00 GMT

As you may have already noticed from my test cases, the day is the only wrong field in any of the failing cases. Any ideas about the culprit?

PS: I have already tried to use f64 instead of u64 and to round the results... but it didn't fix all of the test cases.


Solution

  • It turns out I was looking for an impossible solution. It is indeed impossible to perform such a computation without taking into account leap years and the simple fact that months do not share the same duration in days. Even though now I feel a bit dumb about posting this question, I'd like to thank the people who first commented this thread. They pushed me in the right direction.