4211. IANA time zone database allows links to refer to links

Section: 30.11.2.1 [time.zone.db.tzdb] Status: New Submitter: Jonathan Wakely Opened: 2025-02-22 Last modified: 2025-02-24

Priority: Not Prioritized

View all issues with New status.

Discussion:

Since October 2022 the IANA Time Zone Database's tzfile data format allows a Link to refer to another Link, instead of referring directly to a Zone. The zic(8) man page says:

The TARGET field should appear as the NAME field in some zone line or as the LINK-NAME field in some link line. The LINK-NAME field is used as an alternative name for that zone; it has the same syntax as a zone line's NAME field. Links can chain together, although the behavior is unspecified if a chain of one or more links does not terminate in a Zone name.
Because chains of links were introduced to tzfile after chrono::tzdb was standardized, C++ does not properly support chains of links.

The time_zone_link::target() member function is required to return "The name of the time_zone for which this time_zone_link provides an alternative name." This doesn't allow returning the name of another time_zone_link, which implies that the implementation should flatten a series of links so that the target is a time_zone. However, this discards information which is present in the tzdb, so that the information exposed to a C++ program does not preserve the original structure of a chain of links.

The standard could be adjusted to support chains of links by allowing time_zone_link::target() to refer to another link, and then altering the algorithm used by tzdb::locate_zone(string_view) to find a time_zone from a name that might be a zone or a link.

Proposed resolution:

This wording is relative to N5001.

  1. Modify 30.11.2.1 [time.zone.db.tzdb] as indicated:
    
    const time_zone* locate_zone(string_view tz_name) const;
    

    -2- Returns:

    1. (2.1) — If zones contains an element tz for which tz.name() == tz_name, a pointer to tz;
    2. (2.2) — otherwise, if links contains an element tz_l for which tz_l.name() == tz_name, then a pointer to the element tz of zones for which tz.name() == tz_l.target() the result of locate_zone(tz_l.target()). .

    [Note 1: A time_zone_link specifies an alternative name for a time_zone. — end note]

    -3- Throws: If a const time_zone* cannot be found as described in the Returns: element, throws a runtime_error.

    [Note 2: On non-exceptional return, the return value is always a pointer to a valid time_zone. — end note]

    -?- Remarks: If both zones and links contain elements that match tz_name then it is unspecified whether &tz or locate_zone(tz_l.target()) is returned.

    [Drafting note: This gives flexibility how to implement the lookup in locate_zone.]
  2. Modify 30.11.9.1 [time.zone.link.overview] as indicated:

    -1- A time_zone_link specifies an alternative name for a time_zone. time_zone_links are constructed when the time zone database is initialized. The name of a time_zone_link can be used as an argument to tzdb::locate_zone (30.11.2.1 [time.zone.db.tzdb]). A time_zone_link can refer directly to a time_zone, or to another time_zone_link, forming a chain of links.

  3. Modify 30.11.9.2 [time.zone.link.members] as indicated:
    
    string_view name() const noexcept;
    
    -1- Returns: The alternative name for the time zone.
    
    string_view target() const noexcept;
    

    -2- Returns: The name of the time_zone for which this time_zone_link provides an alternative name or the name of another time_zone_link.

    [Note 1: tzdb::locate_zone follows a chain of links formed when a link's target is the name of a time_zone_link, throwing an exception if the chain of links does not terminate in a time_zone. — end note]