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:
- delegate to that checked path and document that they panic for unsupported
timestamps, or
- 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.
Related: #54 discusses pre-
UNIX_EPOCHtimestamps (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
The same panic path also affects:
format_rfc3339_secondsformat_rfc3339_millisformat_rfc3339_microsformat_rfc3339_nanoshumantime::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 valuesshould be rejected through an explicitly fallible API (or at least be checked
before exposing a
Displayadapter), rather than panicking later duringformat!.Expected behavior
Formatting a valid
SystemTimeshould not panic in normal usage.Actual behavior
Rfc3339Timestamp::fmtdoes this for timestamps at or after year 10000:Because the public API exposes a
Displaywrapper, common callers such as.to_string()andformat!("{}", ...)panic when thatfmt::Errorisreturned.
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:
and have the existing
format_rfc3339*helpers either:timestamps, or
More generally,
Display::fmtshould avoid manufacturingfmt::Errorforvalue-domain failures. It should only propagate formatter/write failures once a
checked formatter value has already been constructed.