Skip to content

format_rfc3339* returns fmt::Error after year 10K, causing to_string() to panic #68

Description

@meng-xu-cs

Related: #54 discusses pre-UNIX_EPOCH timestamps (which is dismissed as not planned). This issue is about the separate upper-bound case and the fact that unsupported values currently surface as panics in common formatting APIs.

Reproduction

use std::time::{Duration, UNIX_EPOCH};

fn main() {
    // 10000-01-01T00:00:00Z
    let t = UNIX_EPOCH + Duration::from_secs(253_402_300_800);

    // Panics:
    let _ = humantime::format_rfc3339(t).to_string();
}

The same panic path also affects:

  • format_rfc3339_seconds
  • format_rfc3339_millis
  • format_rfc3339_micros
  • format_rfc3339_nanos
  • humantime::Timestamp::from(t).to_string()

If the intention is to only supports the representable RFC3339 range
[UNIX_EPOCH, 9999-12-31T23:59:59.999999999Z], then unsupported values
should be rejected through an explicitly fallible API (or at least be checked
before exposing a Display adapter), rather than panicking later during format!.

Expected behavior

Formatting a valid SystemTime should not panic in normal usage.

Actual behavior

Rfc3339Timestamp::fmt does this for timestamps at or after year 10000:

if secs_since_epoch >= 253_402_300_800 {
    return Err(fmt::Error);
}

Because the public API exposes a Display wrapper, common callers such as
.to_string() and format!("{}", ...) panic when that fmt::Error is
returned.

Impact

This makes the public RFC3339 formatting surface crash-prone for ordinary data
that happens to be outside RFC3339's 4-digit year range. Any application that
stringifies future timestamps from external input, databases, or protocol
messages can panic unexpectedly.

The failure is especially surprising because the API accepts a plain
SystemTime, and the docs do not currently call out this panic condition.

Suggested fix

One possible fix is to add a fallible constructor, for example:

pub fn try_format_rfc3339(system_time: SystemTime) -> Result<Rfc3339Timestamp, Error>

and have the existing format_rfc3339* helpers either:

  1. delegate to that checked path and document that they panic for unsupported
    timestamps, or
  2. be deprecated in favor of the fallible API.

More generally, Display::fmt should avoid manufacturing fmt::Error for
value-domain failures. It should only propagate formatter/write failures once a
checked formatter value has already been constructed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions