Skip to content

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

@meng-xu-cs

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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions