#[must_use]
pub fn parse_http_date(date_str: &str) -> Option<u64> {
let mut parts = date_str.split_whitespace();
let _day_name = parts.next()?;
let day_str = parts.next()?;
let month_str = parts.next()?;
let year_str = parts.next()?;
let time_str = parts.next()?;
let day = day_str.parse::<u64>().ok()?;
let year = year_str.parse::<u64>().ok()?;
let month = match month_str {
"Jan" => 1,
"Feb" => 2,
"Mar" => 3,
"Apr" => 4,
"May" => 5,
"Jun" => 6,
"Jul" => 7,
"Aug" => 8,
"Sep" => 9,
"Oct" => 10,
"Nov" => 11,
"Dec" => 12,
_ => return None,
};
let mut time_parts = time_str.split(':');
let hour = time_parts.next()?.parse::<u64>().ok()?;
let minute = time_parts.next()?.parse::<u64>().ok()?;
let second = time_parts.next()?.parse::<u64>().ok()?;
let mut days = 0u64;
for y in 1970..year {
days += if is_leap_year(y) { 366 } else { 365 };
}
let month_days = if is_leap_year(year) {
[0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
} else {
[0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
};
let month_index = usize::try_from(month).ok()?;
days += month_days
.iter()
.take(month_index)
.skip(1)
.sum::<u64>();
days += day - 1;
Some(days * 86_400 + hour * 3_600 + minute * 60 + second)
}
#[must_use]
pub const fn is_leap_year(year: u64) -> bool {
(year.is_multiple_of(4) && !year.is_multiple_of(100)) || year.is_multiple_of(400)
}
#[cfg(test)]
mod tests {
#![allow(clippy::expect_used)]
use super::{is_leap_year, parse_http_date};
#[test]
fn leap_years() {
assert!(is_leap_year(2000));
assert!(is_leap_year(2004));
assert!(!is_leap_year(1900));
assert!(!is_leap_year(2021));
assert!(is_leap_year(2024));
}
#[test]
fn parses_valid_dates() {
let ts = parse_http_date("Mon, 01 Jun 2026 12:41:36 GMT").expect("parse June 1 2026");
assert_eq!(ts, 1_780_317_696);
let ts2 = parse_http_date("Tue, 02 Jun 2026 00:00:00 GMT").expect("parse June 2 2026");
assert_eq!(ts2, 1_780_358_400);
}
#[test]
fn rejects_invalid_dates() {
assert!(parse_http_date("Mon, 01 Invalid 2026 12:41:36 GMT").is_none());
assert!(parse_http_date("Mon, 01 Jun 2026 12:41 GMT").is_none());
assert!(parse_http_date("").is_none());
}
}