chrono-tz

repository·main·Indexed 42 days ago

https://github.com/chronotope/chrono-tz

Rust library providing IANA timezone implementations for the chrono crate. It allows developers to perform timezone-aware datetime arithmetic, convert between timezones, and handle DST gaps and overlaps using the IANA database. Version 0.11.0 supports no_std environments and provides features to filter the timezone table via regex to reduce binary size.

Tokens
4.4K
Snippets
18
Records
24
Agent score
55%

What's inside chrono-tz

  1. What is Chrono-TZ

    main
    Chrono-TZ is a Rust library that provides implementations of the TimeZone trait for the chrono crate. It uses the IANA database and parse-zoneinfo to generate timezone data via a build script, allowing you to work with specific geographic timezones in your Rust applications.
  2. Enable `no_std` support

    main

    To use chrono-tz in a no_std environment, disable default features for both chrono and chrono-tz in your Cargo.toml.

    For resource-constrained environments like microcontrollers, it is highly recommended to enable Link Time Optimization (LTO) and set an appropriate opt-level to prevent the generated database from overflowing program space.

    [dependencies]
    chrono = { version = "0.4", default-features = false }
    chrono-tz = { version = "0.5", default-features = false }
    [profile.dev]
    opt-level = 2
    lto = true
    
    [profile.release]
    lto = true
  3. Limit the timezone table using `filter-by-regex`

    main

    By default, chrono-tz includes all timezones from the IANA database. To reduce binary size, you can enable the filter-by-regex feature and provide a regular expression via the CHRONO_TZ_TIMEZONE_FILTER environment variable during your build.

    Example: To only include London and all US-based zones: CHRONO_TZ_TIMEZONE_FILTER="(Europe/London|US/.*)" cargo build

    CHRONO_TZ_TIMEZONE_FILTER="(Europe/London|US/.*)" cargo build
  4. Handle timezone gaps and overlaps

    main

    When converting local times to UTC, you may encounter gaps (non-existent times due to DST forward jumps) or overlaps (ambiguous times due to DST backward jumps).

    • Gaps: Use GapInfo::new(&datetime, &timezone) to find the start and end of a gap.
    • Ambiguity: Use .earliest() and .latest() on the result of a local time conversion to resolve ambiguous times.
    // Example of resolving ambiguous time
    use chrono::TimeZone;
    use chrono_tz::Europe::London;
    
    let ambiguous = London.with_ymd_and_hms(2016, 10, 30, 1, 0, 0);
    let earliest = ambiguous.earliest().unwrap();
    let latest = ambiguous.latest().unwrap();
  5. Understand FixedTimespan and TzOffset

    main

    In chrono-tz, timezones are composed of multiple FixedTimespans that change over time (e.g., EST and EDT for US::Eastern).

    • FixedTimespan: Represents a specific offset from UTC and its associated name (e.g., offset: -18000, name: "EST").
    • TzOffset: A combination of a timezone (Tz) and a FixedTimespan. It implements chrono::Offset, allowing it to be used with the chrono crate to provide a FixedOffset via the .fix() method.
  6. Build a zoneinfo Table using TableBuilder

    main

    The TableBuilder is used to coalesce parsed line data into a single Table structure. It handles the relationships between zones, rulesets, and links (e.g., when one zone links to another).

    To use it, create a new builder, iterate through your parsed Line objects using add_line, and finally call build() to obtain the Table.

    Note: The builder enforces data integrity, such as ensuring rulesets exist before they are referenced by zones and preventing duplicate zone or link definitions.

    use parse_zoneinfo::line::{Line, Zone, Link};
    use parse_zoneinfo::table::TableBuilder;
    
    let mut builder = TableBuilder::new();
    
    let zone = "Zone  Pacific/Auckland  11:39:04  -  LMT  1868  Nov  2";
    let link = "Link  Pacific/Auckland  Antarctica/McMurdo";
    
    for line in [zone, link] {
        builder.add_line(Line::new(&line)?).unwrap();
    }
    
    let table = builder.build();
    
    assert!(table.get_zoneset("Pacific/Auckland").is_some());
    assert!(table.get_zoneset("Antarctica/McMurdo").is_some());
    assert!(table.get_zoneset("UTC").is_none());
    # Ok::<(), parse_zoneinfo::line::Error>(())
  7. Parse timezone strings into Tz objects

    main

    You can convert a string representing a timezone (e.g., "Antarctica/South_Pole") into a Tz object using the FromStr trait via .parse().

    use chrono::TimeZone;
    use chrono_tz::Tz;
    use chrono_tz::UTC;
    
    let tz: Tz = "Antarctica/South_Pole".parse().unwrap();
    let dt = tz.ymd(2016, 10, 22).and_hms(12, 0, 0);
    let utc = dt.with_timezone(&UTC);
    assert_eq!(utc.to_string(), "2016-10-21 23:00:00 UTC");
  8. Convert naive datetimes to timezone-aware datetimes

    main

    Use the .from_local_datetime() method on a timezone object to convert a NaiveDateTime into a timezone-aware datetime.

    use chrono::{TimeZone, NaiveDate};
    use chrono_tz::Africa::Johannesburg;
    
    let naive_dt = NaiveDate::from_ymd(2038, 1, 19).and_hms(3, 14, 08);
    let tz_aware = Johannesburg.from_local_datetime(&naive_dt).unwrap();
    assert_eq!(tz_aware.to_string(), "2038-01-19 03:14:08 SAST");
  9. Convert time between timezones

    main

    You can create a time in a specific timezone (e.g., chrono_tz::US::Pacific) and convert it to UTC or another timezone using the .with_timezone() method.

    use chrono::{TimeZone, Utc};
    use chrono_tz::US::Pacific;
    
    let pacific_time = Pacific.ymd(1990, 5, 6).and_hms(12, 30, 45);
    let utc_time = pacific_time.with_timezone(&Utc);
    assert_eq!(utc_time, Utc.ymd(1990, 5, 6).and_hms(19, 30, 45));
  10. Access UTC and DST offsets using OffsetComponents

    main

    To inspect the raw offsets (standard UTC offset and Daylight Savings Time offset) at a specific time, import the OffsetComponents trait and use the .offset() method.

    use chrono::{Duration, TimeZone};
    use chrono_tz::Europe::London;
    use chrono_tz::OffsetComponents;
    
    let london_time = London.ymd(2016, 5, 10).and_hms(12, 0, 0);
    
    // London typically has zero offset from UTC, but has a 1h adjustment forward
    // when summer time is in effect.
    assert_eq!(london_time.offset().base_utc_offset(), Duration::hours(0));
    assert_eq!(london_time.offset().dst_offset(), Duration::hours(1));