Summary
All three timeflow types currently wrap chrono::Naive* variants, meaning they carry no timezone information. This is a deliberate and valid design choice for many use cases (e.g. storing timestamps that are already normalised to UTC, or working with local wall-clock times where timezone context is implicit).
However, it prevents adoption in distributed systems and web applications where UTC storage with local-time display is the standard pattern. Developers in these contexts must fall back to raw chrono for any timezone-aware operation, negating the library's replacement value.
This issue proposes adding lightweight timezone support as an opt-in feature, preserving the current zero-dependency behaviour by default.
Proposed Approach
Feature flag: timezone
Add an optional dependency on chrono-tz behind a timezone feature flag:
[dependencies]
chrono-tz = { version = "0.10", optional = true }
[features]
timezone = ["chrono-tz"]
New methods on DateTime (available with timezone feature)
// Convert to a specific IANA timezone
datetime.in_timezone("Europe/Paris")? -> ZonedDateTime
datetime.in_timezone("America/New_York")? -> ZonedDateTime
datetime.to_utc() -> ZonedDateTime
// Construct from a timezone-aware value
DateTime::now_utc()? -> DateTime // current time in UTC (not local)
DateTime::now_in("Europe/Paris")? -> DateTime
New ZonedDateTime type
pub struct ZonedDateTime {
inner: chrono::DateTime<chrono_tz::Tz>,
format: String,
}
impl ZonedDateTime {
pub fn timezone(&self) -> &str // "Europe/Paris"
pub fn to_naive(&self) -> DateTime // strip timezone, return local wall time
pub fn to_utc_naive(&self) -> DateTime // convert to UTC, strip timezone
pub fn offset_hours(&self) -> f64 // UTC offset at this instant
}
ZonedDateTime implements Display, Span<DateTimeUnit, i32>, and serde.
Minimal Viable Scope (v1)
If a full ZonedDateTime type is too large for a first iteration, a minimal useful surface would be:
// On DateTime, with the `timezone` feature
datetime.to_utc() -> DateTime // treat self as local time, convert to UTC
datetime.from_utc_to("Europe/Paris")? -> DateTime // treat self as UTC, convert to local
DateTime::now_utc()? -> DateTime // UTC system time
This covers the most common pattern (store UTC, display local) without introducing a new type.
Implementation Notes
- The
timezone feature must not affect the public API or compilation of code that does not use it (#[cfg(feature = "timezone")] guards on all new items).
chrono-tz provides IANA timezone database at compile time — no runtime files needed.
- Invalid timezone strings (e.g.
"Invalid/Zone") should return Err(SpanError) rather than panicking.
ZonedDateTime serialization should include the timezone name: { "datetime": "2024-10-31 06:32:28", "timezone": "Europe/Paris" }.
- Consider whether DST transitions during arithmetic (e.g. adding 1 hour across a DST boundary) should produce the correct wall-clock result or the correct elapsed-time result — document the chosen semantics explicitly.
Acceptance Criteria
Summary
All three
timeflowtypes currently wrapchrono::Naive*variants, meaning they carry no timezone information. This is a deliberate and valid design choice for many use cases (e.g. storing timestamps that are already normalised to UTC, or working with local wall-clock times where timezone context is implicit).However, it prevents adoption in distributed systems and web applications where UTC storage with local-time display is the standard pattern. Developers in these contexts must fall back to raw
chronofor any timezone-aware operation, negating the library's replacement value.This issue proposes adding lightweight timezone support as an opt-in feature, preserving the current zero-dependency behaviour by default.
Proposed Approach
Feature flag:
timezoneAdd an optional dependency on
chrono-tzbehind atimezonefeature flag:New methods on
DateTime(available withtimezonefeature)New
ZonedDateTimetypeZonedDateTimeimplementsDisplay,Span<DateTimeUnit, i32>, and serde.Minimal Viable Scope (v1)
If a full
ZonedDateTimetype is too large for a first iteration, a minimal useful surface would be:This covers the most common pattern (store UTC, display local) without introducing a new type.
Implementation Notes
timezonefeature must not affect the public API or compilation of code that does not use it (#[cfg(feature = "timezone")]guards on all new items).chrono-tzprovides IANA timezone database at compile time — no runtime files needed."Invalid/Zone") should returnErr(SpanError)rather than panicking.ZonedDateTimeserialization should include the timezone name:{ "datetime": "2024-10-31 06:32:28", "timezone": "Europe/Paris" }.Acceptance Criteria
timezonefeature added toCargo.tomlwithchrono-tzas optional dependencyDateTime::now_utc()andDateTime::now_in(tz)implementedDateTime::to_utc()andDateTime::from_utc_to(tz)implemented (minimal v1)SpanError, not a panic#[cfg(feature = "timezone")]timezonefeature produces zero warnings and identical behaviourZonedDateTime(if the full type is implemented)