Skip to main content

smbcloud_ascapi_frontend/
time.rs

1//! Timestamp helpers for comparing against App Store Connect's expiry
2//! strings without a date-parsing dependency.
3
4/// Whether an ISO-8601 UTC timestamp is in the past.
5///
6/// Lexicographic comparison is sound for the fixed-width UTC timestamps
7/// App Store Connect returns, and avoids a date-parsing dependency for a
8/// field that is only ever compared and displayed.
9pub fn is_expired(expiration_date: &str, now: &str) -> bool {
10    expiration_date < now
11}
12
13/// Current UTC time as `YYYY-MM-DDTHH:MM:SS`, for comparison against
14/// Apple's expiry strings.
15pub fn now_iso8601() -> String {
16    let secs = std::time::SystemTime::now()
17        .duration_since(std::time::UNIX_EPOCH)
18        .unwrap_or_default()
19        .as_secs() as i64;
20
21    // days_from_civil, inverted. Shifting the era to March makes the leap
22    // day the last day of the year, which removes every special case.
23    let days = secs.div_euclid(86_400);
24    let secs_of_day = secs.rem_euclid(86_400);
25    let z = days + 719_468;
26    let era = z.div_euclid(146_097);
27    let doe = z.rem_euclid(146_097);
28    let yoe = (doe - doe / 1460 + doe / 36_524 - doe / 146_096) / 365;
29    let y = yoe + era * 400;
30    let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
31    let mp = (5 * doy + 2) / 153;
32    let d = doy - (153 * mp + 2) / 5 + 1;
33    let m = if mp < 10 { mp + 3 } else { mp - 9 };
34    let y = if m <= 2 { y + 1 } else { y };
35
36    format!(
37        "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}",
38        y,
39        m,
40        d,
41        secs_of_day / 3600,
42        (secs_of_day % 3600) / 60,
43        secs_of_day % 60
44    )
45}