RFC 7808

Time Zone Data Distribution Service

๐Ÿ“… published March 2016, from ietf

Time Zone Data Distribution Service ๐ŸŒโœจ

Okay bestie, this one is RFC 7808, published in March 2016 as a Standards Track document by the Internet Engineering Task Force (IETF) ๐Ÿ’…. The authors are M. Douglass (Spherical Cow Group) and C. Daboo (Apple), and it's ISSN 2070-1721. No Obsoletes or Updates line shows up in the title block, so it doesn't replace or change any earlier RFC. It's giving official standards energy ๐ŸŽ€.

Abstract

This doc defines a time zone data distribution service ๐Ÿฆ‹ that lets time zone data and leap-second rules get delivered reliably, securely, and fast to client systems. Those clients can be calendaring and scheduling apps or operating systems, so basically anything that needs to know what time it is ๐Ÿ•ฐ๏ธ๐Ÿ’–.

Status of This Memo

This is an Internet Standards Track document ๐ŸŒธ.

This document is a product of the Internet Engineering Task Force (IETF) ๐Ÿ’œ. It represents the consensus of the IETF community. It got public review and was approved for publication by the Internet Engineering Steering Group (IESG). If you want more info on Internet Standards, check Section 2 of RFC 5741 โœจ.

For the current status of this document, any errata, and how to send feedback, go to http://www.rfc-editor.org/info/rfc7808 ๐Ÿ”—.

Copyright (c) 2016 IETF Trust and the persons identified as the document authors. All rights reserved ๐Ÿ’….

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info) as in effect on the date of publication. Please read these carefully, because they explain your rights and restrictions for this document ๐Ÿ“œ. Code Components pulled out of this document must include Simplified BSD License text, as described in Section 4.e of the Trust Legal Provisions, and they come without warranty, as described in the Simplified BSD License ๐Ÿซถ.

Table of Contents

   1.  Introduction  . . . . . . . . . . . . . . . . . . . . . . . .   4
     1.1.  Conventions . . . . . . . . . . . . . . . . . . . . . . .   4
   2.  Architectural Overview  . . . . . . . . . . . . . . . . . . .   5
   3.  General Considerations  . . . . . . . . . . . . . . . . . . .   7
     3.1.  Time Zone . . . . . . . . . . . . . . . . . . . . . . . .   7
     3.2.  Time Zone Data  . . . . . . . . . . . . . . . . . . . . .   7
     3.3.  Time Zone Metadata  . . . . . . . . . . . . . . . . . . .   7
     3.4.  Time Zone Data Server . . . . . . . . . . . . . . . . . .   7
     3.5.  Observance  . . . . . . . . . . . . . . . . . . . . . . .   7
     3.6.  Time Zone Identifiers . . . . . . . . . . . . . . . . . .   7
     3.7.  Time Zone Aliases . . . . . . . . . . . . . . . . . . . .   8
     3.8.  Time Zone Localized Names . . . . . . . . . . . . . . . .   8
     3.9.  Truncating Time Zones . . . . . . . . . . . . . . . . . .   9
     3.10. Time Zone Versions  . . . . . . . . . . . . . . . . . . .  10
   4.  Time Zone Data Distribution Service Protocol  . . . . . . . .  10
     4.1.  Server Protocol . . . . . . . . . . . . . . . . . . . . .  10
       4.1.1.  Time Zone Queries . . . . . . . . . . . . . . . . . .  11
       4.1.2.  Time Zone Formats . . . . . . . . . . . . . . . . . .  11
       4.1.3.  Time Zone Localization  . . . . . . . . . . . . . . .  12
       4.1.4.  Conditional Time Zone Requests  . . . . . . . . . . .  12
       4.1.5.  Expanded Time Zone Data . . . . . . . . . . . . . . .  14
       4.1.6.  Server Requirements . . . . . . . . . . . . . . . . .  14
       4.1.7.  Error Responses . . . . . . . . . . . . . . . . . . .  14
       4.1.8.  Extensions  . . . . . . . . . . . . . . . . . . . . .  14
     4.2.  Client Guidelines . . . . . . . . . . . . . . . . . . . .  14
       4.2.1.  Discovery . . . . . . . . . . . . . . . . . . . . . .  14
         4.2.1.1.  SRV Service Labels for the Time Zone Data
                   Distribution Service  . . . . . . . . . . . . . .  15
         4.2.1.2.  TXT Records for a Time Zone Data Distribution
                   Service . . . . . . . . . . . . . . . . . . . . .  15
         4.2.1.3.  Well-Known URI for a Time Zone Data Distribution
                   Service . . . . . . . . . . . . . . . . . . . . .  16
           4.2.1.3.1.  Example: Well-Known URI Redirects to Actual
                       Context Path  . . . . . . . . . . . . . . . .  17
       4.2.2.  Synchronization of Time Zones . . . . . . . . . . . .  17
         4.2.2.1.  Initial Synchronization of All Time Zones . . . .  17
         4.2.2.2.  Subsequent Synchronization of All Time Zones  . .  17
         4.2.2.3.  Synchronization with Preexisting Time Zone Data .  18
   5.  Actions . . . . . . . . . . . . . . . . . . . . . . . . . . .  18
     5.1.  "capabilities" Action . . . . . . . . . . . . . . . . . .  18
       5.1.1.  Example: get capabilities . . . . . . . . . . . . . .  19
     5.2.  "list" Action . . . . . . . . . . . . . . . . . . . . . .  21
       5.2.1.  Example: List Time Zone Identifiers . . . . . . . . .  22
     5.3.  "get" Action  . . . . . . . . . . . . . . . . . . . . . .  23
       5.3.1.  Example: Get Time Zone Data . . . . . . . . . . . . .  24
       5.3.2.  Example: Conditional Get Time Zone Data . . . . . . .  25
       5.3.3.  Example: Get Time Zone Data Using a Time Zone Alias .  25
       5.3.4.  Example: Get Truncated Time Zone Data . . . . . . . .  26
       5.3.5.  Example: Request for a Nonexistent Time Zone  . . . .  27
     5.4.  "expand" Action . . . . . . . . . . . . . . . . . . . . .  27
       5.4.1.  Example: Expanded JSON Data Format  . . . . . . . . .  29
     5.5.  "find" Action . . . . . . . . . . . . . . . . . . . . . .  30
       5.5.1.  Example: find action  . . . . . . . . . . . . . . . .  31
     5.6.  "leapseconds" Action  . . . . . . . . . . . . . . . . . .  32
       5.6.1.  Example: Get Leap-Second Information  . . . . . . . .  33
   6.  JSON Definitions  . . . . . . . . . . . . . . . . . . . . . .  34
     6.1.  capabilities Action Response  . . . . . . . . . . . . . .  34
     6.2.  list/find Action Response . . . . . . . . . . . . . . . .  37
     6.3.  expand Action Response  . . . . . . . . . . . . . . . . .  38
     6.4.  leapseconds Action Response . . . . . . . . . . . . . . .  39
   7.  New iCalendar Properties  . . . . . . . . . . . . . . . . . .  40
     7.1.  Time Zone Upper Bound . . . . . . . . . . . . . . . . . .  40
     7.2.  Time Zone Identifier Alias Property . . . . . . . . . . .  41
   8.  Security Considerations . . . . . . . . . . . . . . . . . . .  42
   9.  Privacy Considerations  . . . . . . . . . . . . . . . . . . .  43
   10. IANA Considerations . . . . . . . . . . . . . . . . . . . . .  44
     10.1.  Service Actions Registration . . . . . . . . . . . . . .  45
       10.1.1.  Service Actions Registration Procedure . . . . . . .  45
       10.1.2.  Registration Template for Actions  . . . . . . . . .  46
       10.1.3.  Actions Registry . . . . . . . . . . . . . . . . . .  47
     10.2.  timezone Well-Known URI Registration . . . . . . . . . .  47
     10.3.  Service Name Registrations . . . . . . . . . . . . . . .  47
       10.3.1.  timezone Service Name Registration . . . . . . . . .  47
       10.3.2.  timezones Service Name Registration  . . . . . . . .  48
     10.4.  TZDIST Identifiers Registry  . . . . . . . . . . . . . .  48
       10.4.1.  Registration of invalid-action Error URN . . . . . .  49
       10.4.2.  Registration of invalid-changedsince Error URN . . .  49
       10.4.3.  Registration of tzid-not-found Error URN . . . . . .  50
       10.4.4.  Registration of invalid-format Error URN . . . . . .  50
       10.4.5.  Registration of invalid-start Error URN  . . . . . .  50
       10.4.6.  Registration of invalid-end Error URN  . . . . . . .  51
       10.4.7.  Registration of invalid-pattern Error URN  . . . . .  51
     10.5.  iCalendar Property Registrations . . . . . . . . . . . .  52
   11. References  . . . . . . . . . . . . . . . . . . . . . . . . .  52
     11.1.  Normative References . . . . . . . . . . . . . . . . . .  52
     11.2.  Informative References . . . . . . . . . . . . . . . . .  55
   Acknowledgements  . . . . . . . . . . . . . . . . . . . . . . . .  55
   Authors' Addresses  . . . . . . . . . . . . . . . . . . . . . . .  56

1. Introduction โœจ

Time zone data usually bundles a coordinated universal time (UTC) offset together with daylight saving time (DST) rules ๐Ÿ•. Time zones are typically tied to specific geographic and geopolitical regions ๐Ÿ—บ๏ธ. The UTC offset for a region changes pretty rarely, but DST rules can change often, and sometimes with barely any warning, maybe just hours before a change takes effect ๐Ÿ˜ญ.

Calendaring and scheduling systems, like the ones that use iCalendar [RFC5545], plus operating systems, critically rely on time zone data to figure out the correct local time ๐Ÿ’…. So they need to be kept up to date with changes to that data. Until now, there hasn't been any fast and easy way to do that ๐Ÿ™ˆ. Time zone data often comes as a set of data files that have to be "compiled" into a suitable database format before the client app or OS can use it. For operating systems, those changes often only reach client machines with an OS update, and those updates can be infrequent, so inaccurate time zone data can hang around for a long time ๐Ÿ’€. Some old OS versions stop being supported but are still in use, which means users have to manually "patch" their system to keep up with time zone changes ๐Ÿฅฒ.

Along with time zone data, it's also important to track leap seconds, so there's a mapping between International Atomic Time (TAI) and UTC ๐Ÿงš. Leap seconds can be added (or possibly removed) at various times of the year, in an irregular pattern that's typically decided by precise astronomical observations ๐Ÿ”ญ. Inserting leap seconds into UTC is currently the job of the International Earth Rotation Service ๐ŸŒ.

This specification defines a time zone data distribution service protocol that allows fast, reliable, and accurate delivery of time zone data and leap-second information to client systems ๐Ÿš€๐Ÿ’–. The protocol is based on HTTP [RFC7230] and uses a simple JSON-based API [RFC7159] ๐ŸŽ€.

This specification does not define the source of the time zone data or leap-second information. It's assumed that a reliable and accurate source is available ๐Ÿซถ. One such source is the IANA-hosted time zone database [RFC6557] ๐ŸŒˆ.

1.1. Conventions ๐ŸŽ€

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119] ๐Ÿ’œ.

Unless otherwise indicated, UTC date-time values as specified in [RFC3339] use a "Z" suffix, and not fixed numeric offsets โฐ.

This specification contains examples of HTTP requests and responses. In some cases, extra line breaks were added to the request or response data so it fits the maximum line-length limits of this document ๐Ÿฆ‹.

2. Architectural Overview โœจ

okay so let's look at the big picture ๐Ÿงš The whole process of getting time zone data from its source to your app can be visualized with this diagram:

               ====================  ====================
   (a)         |   Contributors   |  |   Contributors   |
               ====================  ====================
                         |                    |
               ====================  ====================
   (b)         |   Publisher A    |  |   Publisher B    |
               ====================  ====================
                           \           /
                        ====================
   (c)                  |  Root Provider   |
                        ====================
                       /            |       \
                      /             |        \
           ======================   |  ======================
   (d)     | Secondary Provider |   |  | Secondary Provider |
           ======================   |  ======================
             |           |          |              |
             |           |          |              |
        ==========  ==========  ==========      ==========
   (e)  | Client |  | Client |  | Client |      | Client |
        ==========  ==========  ==========      ==========

Figure 1: Time Zone Data Distribution Service Architecture

The overall service is giving layers ๐ŸŽ€ Here they are:

  • (a) Contributors: Individuals, governments, or organizations that provide information about time zones to the publishing process. There can be many contributors. Note that this specification does not address how contributions are made ๐Ÿ™ˆ
  • (b) Publishers: Publishers gather info from contributors, figure out how reliable it is, and then use that to generate time zone data. There can be many publishers, and each one can get info from lots of different contributors. Sometimes a publisher chooses to "republish" data from another publisher ๐Ÿ”
  • (c) Root Providers: Servers that get the time zone data from publishers and then provide it to other servers or clients. There can be many root providers. A root provider can choose to supply data from one or more publishers ๐Ÿ‘‘
  • (d) Secondary Providers: Servers that handle the bulk of the requests and take some pressure off the root servers ๐Ÿ’… These are usually simple caches of a root server, placed closer to clients. For example, a big Internet Service Provider (ISP) might set up its own secondary provider so clients on its network can ask that server instead of servers outside the network. Secondary servers cache data and refresh it from the root servers every so often โฐ
  • (e) Clients: Applications, operating systems, and so on, that use time zone data and retrieve it from either a root provider or a secondary provider ๐Ÿ“ฑ

Implementors are allowed to coalesce some of those layers ๐Ÿงฉ For example, a vendor could build the entire service as one monolithic virtual server, with the address embedded in distributed systems. Others might go with multiple layers of providers, lots of secondary servers, and just a few root servers ๐ŸŒˆ

Okay, so this specification only cares about the protocol used to exchange data between providers, and from a provider to a client ๐Ÿฆ‹ It does not define how contributors pass their info to publishers, how publishers vet that info to get trustworthy data, or the format of the data the publishers produce. Those are all out of scope bestie ๐Ÿซถ

3. General Considerations โœจ

This section defines a few terms and explains some key concepts used in this specification ๐Ÿง 

3.1. Time Zone ๐ŸŒ

A time zone is a description of the past and predicted future timekeeping practices of a collection of clocks that are intended to agree โฑ๏ธ

Heads up: the term "time zone" does not mean the everyday idea of a region of the world at a specific UTC offset, possibly tweaked by daylight saving time ๐Ÿ™…โ€โ™€๏ธ For example, the "Central European Time" zone can correspond to several time zones, like Europe/Berlin and Europe/Paris, because subregions have kept time differently in the past.

3.2. Time Zone Data ๐Ÿ’พ

Time zone data is data that defines a single time zone. It includes an identifier, UTC offset values, DST rules, and other info like time zone abbreviations โœจ

3.3. Time Zone Metadata ๐Ÿท๏ธ

Time zone metadata is data that describes extra properties of a time zone that are not included in the time zone data itself. This can be things like the publisher name, a version identifier, aliases, and localized names (more on those below) ๐Ÿ’œ

3.4. Time Zone Data Server ๐Ÿ–ฅ๏ธ

A time zone data server is a server that implements the Time Zone Data Distribution Service Protocol defined by this specification ๐Ÿฆ‹

3.5. Observance ๐Ÿ•ฐ๏ธ

A time zone with varying rules for its UTC offset will have back-to-back periods of time that use different UTC offsets. Each period of time with a constant UTC offset is called an observance ๐ŸŒธ

3.6. Time Zone Identifiers ๐Ÿ†”

Time zone identifiers are unique names associated with each time zone, as defined by publishers. The iCalendar [RFC5545] specification has a TZID property and parameter. Its value is set to the matching time zone identifier and is used to identify time zone data and link time zones to start and end dates in events, and so on ๐Ÿ“…

This specification does not define what format time zone identifiers should use. It's possible that identifiers from different publishers overlap, and a provider might need to tell them apart with some kind of "namespace" prefix that identifies the publisher ๐Ÿ” However, creating a standard (global) naming scheme for time zone identifiers is out of scope for this specification ๐Ÿ™Œ

3.7. Time Zone Aliases ๐ŸŽญ

Time zone aliases map a name onto a time zone identifier. For example, US/Eastern is usually mapped onto America/New_York. When presenting info to users, aliases are typically used interchangeably with time zone identifiers ๐Ÿ‘€

A time zone data distribution service needs to maintain time zone alias mapping info and expose that data to clients. It also needs to let clients query for time zone data using aliases ๐Ÿ”‘ When a server returns time zone data to a client, it returns the data with an identifier that matches the query. But it can also include one or more additional identifiers in the data, as a hint that alternative identifiers are available. For example, a query for US/Eastern could come back with extra identifiers for America/New_York or America/Montreal ๐Ÿ’…

The set of aliases can vary depending on whether the time zone data is truncated (see Section 3.9). For example, a client in the US state of Michigan might see US/Eastern as an alias for America/Detroit, while a client in the US state of New Jersey might see it as an alias for America/New_York. And all three names could be aliases if time zones are truncated to post-2013 data ๐Ÿคฏ

3.8. Time Zone Localized Names ๐ŸŒ

Localized names are names for time zones that can be shown to a user in their own language. Each time zone may have one or more localized names associated with it. Names are typically unique within their own locale, since they might be shown to the user in a list ๐Ÿ“ Localized names are different from the abbreviations commonly used for UTC offsets within a time zone. For example, the time zone America/New_York may have the localized name "Nueva York" in a Spanish locale. That's distinct from the abbreviations "EST" and "EDT", which may or may not have their own localizations ๐Ÿ‡ช๐Ÿ‡ธ

A time zone data distribution service might need to maintain localized name info for one or more chosen languages. It might also need to let clients query for time zone data using localized names ๐Ÿ—ฃ๏ธ

3.9. Truncating Time Zones โœ‚๏ธ

Time zone data can contain info about past and future UTC offsets that isn't relevant to a particular server's intended clients. For example, calendaring and scheduling clients mostly care about time zone data covering a period of about one or two years in the past and onward into the future, since users usually create new events only for the present and future ๐Ÿ“† Time zone data can also hold a lot of "future" info about transitions happening many decades out. Clients may only care about a smaller range going forward, so data past that point might be unnecessary ๐Ÿ™ˆ

To avoid sending unnecessary data, servers can choose to truncate time zone data to a range set by start and end date-time values. They then provide only offsets and rules between those points ๐ŸŽ€ If truncation is done, the server MUST include the ranges it is using in the capabilities action response (see Section 6.1), so clients can take the right action if they need time zone data for times outside those ranges ๐Ÿ’Œ

The truncation points at the start and end of a range are always a UTC date-time value. The start point is "inclusive" to the overall range, and the end point is "exclusive" to it. In other words, the end value is just past the end of the last valid value in the range ๐ŸŽฏ A server will advertise a truncation range for the truncated data it can supply, or it will provide an indicator that it can truncate at any start or end point to produce arbitrary ranges ๐Ÿช„ A server can also advertise that it supplies untruncated data, meaning data that covers the full range of times available from the source publisher ๐ŸŒŸ If there's no indication of truncated data on the server, the server will supply only untruncated data ๐Ÿซถ

When truncating the start of a VTIMEZONE component, the server MUST include exactly one STANDARD or DAYLIGHT subcomponent. That subcomponent needs a DTSTART property value that matches the start point of the truncation range, plus appropriate TZOFFSETFROM and TZOFFSETTO properties that show the correct offset in effect right before and right after the start point of the truncation range ๐Ÿ’– This subcomponent is the first observance defined by the time zone data. It represents the earliest valid date-time covered by the time zone data in the truncated VTIMEZONE component ๐Ÿ•ฐ๏ธ

When truncating the end of a VTIMEZONE component, the server MUST include a TZUNTIL iCalendar property (Section 7.1) in the VTIMEZONE component. That property indicates the end point of the truncation range ๐Ÿ›‘

3.10. Time Zone Versions

okay bestie, time zone data keeps changing ๐Ÿ•ฐ๏ธ so consumers have to stay up to date โœจ. that's why it's so useful to tag each time zone with a specific version number or identifier from whoever published the data ๐Ÿ’…. there are two common models publishers use:

  • a. the "monolithic" model ๐Ÿ“ฆ: all the time zones get published in one go, with a single version number or identifier slapped on the entire data set. a publisher dropping updates a few times a year might use ids like 2015a, 2015b, etc.
  • b. the "incremental" model ๐Ÿงฉ: each time zone gets its own version identifier, so one can update without messing with any of the others. say the first data has version A.1 for time zone A and B.1 for time zone B, and then time zone B changes. when the data is next published, A is still A.1, but B is now B.2. iykyk ๐Ÿฆ‹

a time zone data distribution service needs to make sure the version identifiers from the publisher are available to any client, along with the actual publisher name for each time zone ๐ŸŽ€. that way clients can compare publisher/version details from any server against what they've already cached locally, and only fetch the time zones that actually changed (see Section 4.2.2 for more on how clients synchronize data from the server) ๐Ÿซถ.


4. Time Zone Data Distribution Service Protocol โœจ

4.1. Server Protocol

the time zone data distribution service protocol uses HTTP [RFC7230] to query and deliver time zone data, metadata, and leap-second information ๐Ÿ“ฌ. the interactions with the HTTP server can be broken into a set of "actions" that define the overall function being requested (see Section 5) ๐Ÿ’–. each action targets a specific HTTP resource using the GET method, and different request-URI parameters tweak the behavior as needed.

the HTTP resources used for requests are identified via URI templates [RFC6570] ๐Ÿงš. the overall service has a "context path" request-URI template defined as {/service-prefix}. the client discovers this "root" prefix as per Section 4.2.1. request-URIs that target time zone data directly use the prefix template {/service-prefix,data-prefix}. the second component of that prefix template can add extra path segments so there are alternative ways to "partition" the time zone data. for example, the data might be partitioned by publisher release dates or version identifiers ๐ŸŒˆ. this spec doesn't define any partitions, though, that's left for future extensions ๐Ÿ”ฎ. when the data-prefix variable is empty, the server is expected to return the current version of time zone data it has for all publishers it supports.

All URI template variable values, and URI request parameters that contain text values, MUST be encoded using the UTF-8 [RFC3629] character set. All responses MUST return data using the UTF-8 [RFC3629] character set. โš ๏ธ heads up: any / characters, which show up a lot in time zone identifiers, get percent-encoded when they're used in the value of a path segment expansion variable in a URI template (as per Section 3.2.6 of [RFC6570]). so the time zone identifier America/New_York shows up as America%2FNew_York when it's the value for the {/tzid} URI template variable (that one's defined later in this spec) ๐Ÿ—ฝ.

the server gives time zone metadata as a JSON [RFC7159] object ๐Ÿ“‹. clients can request the metadata directly, or query for subsets of it that match specific criteria.

security and privacy considerations for this protocol are covered in detail in Sections 8 and 9, respectively ๐Ÿ”. don't skip those, they're serious business. ๐Ÿ™ˆ

4.1.1. Time Zone Queries

time zone identifiers, aliases, or localized names can all be used to query for time zone data or metadata ๐Ÿ”. each action will spell out the details more explicitly, but in general: if a tzid URI template variable is used, the value can be an identifier or an alias. when the pattern URI query parameter is used, it can be an identifier, an alias, or a localized name โœจ.

4.1.2. Time Zone Formats

the default media type [RFC2046] format for returning time zone data is the iCalendar [RFC5545] data format ๐Ÿ“…. on top of that, iCalendar-in-XML [RFC6321] and iCalendar-in-JSON [RFC7265] representations are available too ๐Ÿ’œ. clients use the Accept header field (see Section 5.3.2 of [RFC7231]) to say which returned data format they prefer. servers tell clients which formats they support through the "capabilities" action response (Section 5.1) ๐ŸŽ€.

4.1.3. Time Zone Localization

as per Section 3.8, time zone data can support localized names ๐ŸŒ. clients use the Accept-Language header field (see Section 5.3.5 of [RFC7231]) to say which language they'd like the localized names in the response to be in ๐Ÿ—ฃ๏ธ.

4.1.4. Conditional Time Zone Requests

when time zone data or metadata changes, it has to get distributed in a timely manner ๐Ÿšจ, because changes to local time offsets can happen within a few days of the time zone data changes being published โณ. usually only a small number of time zones change, while the overall number of time zones can be huge ๐Ÿ“ˆ. so when a client is using more than a few time zones, it's way more efficient to download only the ones that changed (an incremental update) ๐Ÿ˜คโœจ.

clients first request a full list of time zones from the server using a "list" action request (see Section 5.2) ๐Ÿ“. the response includes two things the client caches for later "conditional" (incremental update) requests:

  1. an opaque synchronization token in the synctoken JSON member ๐Ÿช„. this token changes whenever there's a change to any metadata associated with one or more time zones (where the metadata is the information reported in the "list" action response for each time zone).
  2. the HTTP ETag header field value for each time zone returned in the response. the ETag value is returned in the etag JSON member, and it matches the ETag value that would come back from a "get" action request (see Section 5.3) against the corresponding time zone data resource ๐Ÿท๏ธ.

for later updates to cached data, clients can follow this procedure ๐Ÿ’…:

  • a. send a "list" action request with a changedsince URI query parameter set to the last opaque synchronization token the server returned ๐Ÿ“ค. the server will return time zone metadata for only the time zones that have changed since the last request.
  • b. the client caches the new opaque synchronization token from the response, for the next incremental update, along with the returned time zone metadata ๐Ÿ’พ.
  • c. the client checks each time zone's metadata to see if its etag value is different from that of any cached time zone data it has ๐Ÿ”Ž.
  • d. the client uses a "get" action request to update any cached time zone data for the time zones whose ETag value has changed ๐Ÿ”„.

okay so here's the tea โ˜•: time zone metadata will always change when the matching time zone data changes. the reverse isn't true though! a piece of the metadata can change without the time zone data changing ๐Ÿ™ƒ. for example, with a "monolithic" publisher (see Section 3.10), the version identifier in every time zone metadata element changes with each new published revision, but only a small subset of the time zone data actually changes ๐Ÿ’€.

if a client only needs data for one time zone or a small set (say, a clock in a fixed location) ๐Ÿ•ฐ๏ธ, it can use a conditional HTTP request to check whether the time zone data changed and grab the new data if it did. the full details of HTTP conditional requests are in [RFC7232]. here's a quick summary of what a client typically does ๐Ÿ“š:

  • a. when the client retrieves the time zone data with a "get" action (see Section 5.3), the server includes an HTTP ETag header field in the response ๐Ÿท๏ธ.
  • b. the client stores that header field's value along with the request-URI used for the request ๐Ÿ—‚๏ธ.
  • c. when the client wants to check for an update, it sends another "get" action HTTP request to the original request-URI, but this time with an If-None-Match HTTP request header field set to the ETag value from the previous response ๐Ÿ™Œ. if the time zone data hasn't changed, the server returns a 304 (Not Modified) HTTP response ๐Ÿ˜ด. if it has changed, the server returns a normal HTTP success response with the changed data, plus a new ETag header field value โœจ.

clients SHOULD poll for changes, using an appropriate conditional request, at least once a day. ๐ŸŒž a server acting as a secondary provider, caching time zone data from another server, SHOULD poll for changes once per hour. โฐ check Section 8 for the expected client and server behavior around high request rates ๐Ÿšฆ.

4.1.5. Expanded Time Zone Data

working out time zone offsets at a particular moment is honestly a whole mess ๐Ÿ˜ตโ€๐Ÿ’ซ, because the daylight saving time rules can be super complex ๐ŸŒ“. to help with that, the time zone data distribution service has an action that lets clients ask the server to expand a time zone into a set of "observances" over a fixed period of time (see Section 5.4) ๐Ÿช„. each observance describes a UTC onset time and UTC offsets for the prior time and the observance time. together, these give "thin" clients a quick way to find the right UTC offset for any date, without having to do the full time zone expansion themselves ๐Ÿ’…โœจ.

4.1.6. Server Requirements

to keep client implementations simple, servers SHOULD ensure that they provide or cache data for all commonly used time zones, from various publishers. ๐Ÿง that way, a client can be set up to point at a single server and get all the time zone data it needs ๐ŸŽฏ. any server can refresh any of the data from any other server ๐Ÿ”, though the root servers may have the most up-to-date copy of the data ๐Ÿ‘‘.

4.1.7. Error Responses

when the client gets an HTTP error response, the server SHOULD return a JSON "problem details" object in the response body, as per [RFC7807]. ๐Ÿš‘ Every JSON "problem details" object MUST include a type member with a URI value matching the applicable error code (defined for each action in Section 5). ๐Ÿซถ that URI is how the client knows exactly what went wrong ๐Ÿ™ˆ๐Ÿ’–.

4.1.8. Extensions โœจ

okay bestie, so this protocol is built to be extensible ๐Ÿ’– through a standards-based registration mechanism (check out Section 10 for the deets ๐ŸŽ€). The spec authors are literally already thinking about future features ๐Ÿ”ฎ like mapping a geographical location to time zone identifiers, or getting the change history for time zones ๐ŸŒโฐ. So servers MUST return a description of their capabilities ๐Ÿ’…. Why? Because that lets clients figure out whether new features have been installed ๐Ÿ‘€, and if they haven't, the client can fall back on earlier features or switch off some of its own capabilities ๐Ÿซถ. It's giving forward compatibility, no cap ๐Ÿฆ‹.

4.2. Client Guidelines ๐ŸŽ€

4.2.1. Discovery ๐Ÿ”

okay so clients either need to already know where the time zone data distribution service lives ๐Ÿ , or they need to discover it somehow ๐Ÿ•ต๏ธโ€โ™€๏ธ. To use the service, a client needs a Fully Qualified Domain Name (FQDN) ๐ŸŒ, a port ๐Ÿšช, and an HTTP request-URI path ๐Ÿ›ฃ๏ธ. The request-URI path you find through discovery is the "context path" ๐Ÿ’œ for the service itself, and it becomes the value of the "service-prefix" URI template variable when actions get executed (see Section 5) โœจ.

The next subsections describe two discovery methods: DNS SRV records [RFC2782] and an HTTP "well-known" [RFC5785] resource ๐Ÿงš. Alternative mechanisms could also work, like a DHCP server option [RFC2131] ๐ŸŒธ.

4.2.1.1. SRV Service Labels for the Time Zone Data Distribution Service ๐Ÿท๏ธ

[RFC2782] defines a DNS-based service discovery protocol that's been super widely adopted ๐Ÿ‘‘ for finding particular services on a local network and beyond, using SRV RR records ๐Ÿ“ก. It can be used to discover a service's FQDN and port ๐Ÿ’–.

This spec adds two service types for SRV records:

  • timezone: identifies a time zone data distribution server that uses HTTP without Transport Layer Security ([RFC2818]) ๐Ÿ”“.
  • timezones: identifies a time zone data distribution server that uses HTTP with Transport Layer Security ([RFC2818]) ๐Ÿ”’.

Clients MUST honor "TTL", "Priority", and "Weight" values in the SRV records, as described by [RFC2782] ๐Ÿ’…. No skipping that, bestie ๐Ÿ™…โ€โ™€๏ธ.

Example: service record for a server without Transport Layer Security ๐Ÿ”“.

_timezone._tcp SRV 0 1 80 tz.example.com.

Example: service record for a server with Transport Layer Security ๐Ÿ”’.

_timezones._tcp SRV 0 1 443 tz.example.com.

4.2.1.2. TXT Records for a Time Zone Data Distribution Service ๐Ÿ“

okay so when SRV RRs are used to advertise the service, it's super handy to also specify a "context path" in DNS so you can grab it at the same time ๐Ÿ™Œ. To make that happen, this spec uses a TXT RR that follows the syntax in Section 6 of [RFC6763] ๐ŸŽ€ and defines a "path" key for that record. The value of that key MUST be the actual "context path" to the matching service on the server ๐Ÿ’œ.

A site might give TXT records as well as SRV records for each service โœจ. When they're there, clients MUST use the "path" value as the "context path" for the service in HTTP requests ๐Ÿซถ. When they're not there, clients use the ".well-known" URI approach from Section 4.2.1.3 ๐Ÿฆ‹.

As per Section 8, the server MAY require authentication when a client tries to access the path URI given by the TXT RR ๐Ÿ”. (So the server would return a 401 status response to an unauthenticated request from the client, and then send a redirect response once the client has authenticated successfully ๐Ÿ™ˆ.)

Example: text record for a service with Transport Layer Security ๐Ÿ”’.

_timezones._tcp TXT path=/timezones

4.2.1.3. Well-Known URI for a Time Zone Data Distribution Service ๐ŸŒˆ

A "well-known" URI [RFC5785] is registered by this spec for the Time Zone Data Distribution service, "timezone" (see Section 10) ๐ŸŽ€. This URI points to a resource the client can use as the initial "context path" for whatever service it's trying to reach ๐Ÿงšโ€โ™€๏ธ. The server MUST redirect HTTP requests for that resource to the actual "context path" using one of the mechanisms HTTP provides (for example, an appropriate 3xx status response) ๐Ÿ”. Clients MUST handle HTTP redirects on the ".well-known" URI, taking into account the security restrictions on redirects described in Section 8 ๐Ÿ›ก๏ธ. Servers MUST NOT locate the actual time zone data distribution service endpoint at the ".well-known" URI, as per Section 1.1 of [RFC5785] ๐Ÿ™…โ€โ™€๏ธ. The "well-known" URI MUST be present on the server, even when a TXT RR (Section 4.2.1.2) is used in the DNS to specify a "context path" ๐Ÿ’….

Servers SHOULD set an appropriate Cache-Control header field value (as per Section 5.2 of [RFC7234]) in the redirect response, so caching happens when it's needed or when the type of response requires it ๐Ÿง . For example, if the redirect location might change over time, an appropriate "max-age" value would be used โณ.

As per Section 8, the server MAY require authentication when a client tries to access the ".well-known" URI (so the server would return a 401 status response to the unauthenticated request, then return the redirect response after the client authenticates successfully) ๐Ÿ”.

4.2.1.3.1. Example: Well-Known URI Redirects to Actual Context Path ๐Ÿฆ‹

okay so a time zone data distribution server has a "context path" of "/servlet/timezone" ๐Ÿ’œ. The client will use "/.well-known/timezone" as the path for the service after it has found the FQDN and port number, either through an SRV lookup or by the user typing the info in manually โœจ. When the client makes its first HTTP request against "/.well-known/timezone", the server issues an HTTP 301 redirect response with a Location response header field pointing to "/servlet/timezone" ๐Ÿ”. The client then "follows" that redirect to the new resource and keeps making HTTP requests there ๐Ÿƒโ€โ™€๏ธ. It also caches the redirect information, subject to any Cache-Control directive, for use in later requests ๐Ÿง ๐Ÿ“.

4.2.2. Synchronization of Time Zones ๐Ÿ”„

This section talks about possible client synchronization strategies, using the various protocol elements the server provides for that purpose ๐Ÿ’–.

4.2.2.1. Initial Synchronization of All Time Zones ๐ŸŒธ

When a secondary service or a client that wants to cache all time zone data first starts up (or wants a full refresh), it syncs with another server by issuing a "list" action to get all the time zone metadata ๐Ÿ“‹. The client keeps the returned opaque token for later use (see "synctoken" in Section 5.2.1) ๐Ÿ”–. The client stores the metadata for each time zone in the response ๐Ÿ—‚๏ธ. Then the time zone data for each matching time zone can be fetched and stored locally ๐Ÿ’พ. A mapping of aliases to time zones can also be built from the metadata ๐Ÿ—บ๏ธ. Just so you know the size, a typical "list" action response is about 50-100 KB of "pretty printed" JSON data, for a service using the IANA time zone database [RFC6557], as of the time this spec was published ๐Ÿ“ฆ.

4.2.2.2. Subsequent Synchronization of All Time Zones ๐Ÿ”

A secondary service or a client caching all time zones needs to sync with a server periodically ๐Ÿ•ฐ๏ธ. To do that, it issues a "list" action with the "changedsince" URI query parameter set to the opaque token returned by the last sync ๐ŸŽฏ. The client again keeps the new opaque token for later ๐Ÿ”–. It updates its stored time zone metadata using the new values in the response, which only contains metadata for the time zones changed since the last sync โœ๏ธ. It also compares the "etag" value in each time zone's metadata to the ETag header field value for the matching time zone data resource it cached earlier ๐Ÿ‘€. If they're different, it fetches the new time zone data ๐Ÿ“ฅ. Note that if the client sends a "changedsince" value the server doesn't support, all time zone data gets returned, just like when the request had no "changedsince" value at all ๐Ÿซ .

Publishers should keep in mind that "outright" deleting time zone names causes problems for simple clients ๐Ÿ’€, so aliasing a deleted time zone identifier to a suitable alternate one is preferable ๐Ÿซถ.

4.2.2.3. Synchronization with Preexisting Time Zone Data ๐ŸŽ

A client might be pre-provisioned with time zone data from a source other than the time zone data distribution service it's configured to use ๐Ÿ“ฆ. In that case, the client might want to minimize how much time zone data it syncs ๐Ÿƒโ€โ™€๏ธ. It can do an initial "list" action to get all the time zone metadata, then only fetch time zone data for the time zones whose publisher and version details don't match the pre-provisioned data ๐ŸŽฏโœจ.

5. Actions ๐ŸŽฌ

Servers MUST support the following actions ๐Ÿ’…. Each action below shows its details: the request-URI the client targets (in the form of a URI template [RFC6570]) ๐Ÿ”—, a description, the set of allowed query parameters ๐ŸŽ€, the nature of the response ๐Ÿ“จ, and a set of possible error codes for the response (see Section 4.1.7) ๐Ÿšจ.

For any error not covered by the specific error codes defined below, the urn:ietf:params:tzdist:error:invalid-action error code is returned to the client in the JSON "problem details" object ๐Ÿ™ˆ.

The examples in the following subsections presume the timezone context path has been discovered to be "/servlet/timezone" (as in the example in Section 4.2.1.3.1) ๐Ÿ’–.

5.1. "capabilities" Action ๐Ÿงฉ

Name: capabilities

Request-URI Template: {/service-prefix}/capabilities

Description: This action returns the capabilities of the server, so clients can tell if a specific feature has been deployed and/or enabled ๐Ÿ‘‘.

Parameters: None

Response: A JSON object containing a "version" member, an "info" member, and an "actions" member; see Section 6.1 ๐Ÿ“ฆ.

Possible Error Codes: No specific code ๐Ÿซถ.

<think>The system prompt has a directory note about untrusted downloaded files, not relevant here since the user just provided text. No tool needed. Just do the rewrite of part 5 in the Gen Z voice, keeping all details exact.

Key items: section 5.1.1 heading "### 5.1.1. Example: get capabilities" โ€” since the previous part covered 5.1 "capabilities" action, this part starts at 5.1.1 example. Carry on without repeating the 5.1 heading. Heading level: "###" for 5.1.1? The format says "#" for sections, "##" for subsections, "###" below. Section 5.1.1 is third level, so "###". Original has 5.2 as "list" action at level 2 under "5." Section 5 is "# 5. Actions". Since the previous part already did 5 and 5.1, I should use "## 5.2." for list action and "### 5.2.1." for example... Wait, 5.2 is a subsection of 5, so "## 5.2. "list" Action", and 5.2.1 is "### 5.2.1.". The 5.1.1 example is "### 5.1.1." under "## 5.1.". Good.

Note the instruction: "Keep every section... with its number exactly as written". Add flourish allowed but number and name stay. I'll write "### 5.1.1. Example: get capabilities โœจ" etc.

BCP 14 keywords: in this part: "MUST" appears several times: list section: "its value MUST correspond", "server MUST return only", "server MUST return all time zones". "OPTIONAL" in parameters. "MUST NOT occur more than once" for changedsince. "REQUIRED" in get template: "The "tzid" variable value is REQUIRED". Get description: "The response MUST contain an ETag", "server MUST return ... text/calendar". "MAY include". "OPTIONAL, and MUST NOT occur more than once" for start and end (twice in get params each). Careful: keep exact count. Let's enumerate in original get section: "REQUIRED" (1), "MUST contain" (1), "MUST return" (1), "MAY include" (1). Parameters: start: "OPTIONAL, and MUST NOT occur more than once" (OPTIONAL 1, MUST NOT 1). end: same. So get: REQUIRED, MUST, MUST, MAY, OPTIONAL, MUST NOT, OPTIONAL, MUST NOT.

list: MUST (correspond), MUST (return only), MUST (return all), OPTIONAL, MUST NOT. Wait: "If the "changedsince" value is not supported by the server, the server MUST return all time zones" โ€” that's one MUST. So list: MUST, MUST, MUST, OPTIONAL, MUST NOT. Plus the earlier "its value MUST correspond" โ€” counted. Total 3 MUST in description + OPTIONAL + MUST NOT.

Get alias paragraph: "the server will return" โ€” lowercase "will", not a keyword. Keep as is.

Get, 5.3.2: no keywords. Capabilities example: none.

Also the "expand" action etc. in the JSON are in code blocks - keep verbatim. Code block contents must be exact; the 5.1.1 JSON, 5.2.1 JSON, etc. Within code blocks, don't use colour tags. Within code blocks, "MUST" none in those; the 5.3.4 truncated snippet has no keywords.

Be careful: Within code blocks, keep verbatim including "..." lines.

Error code lines: "urn:ietf:params:tzdist:error:invalid-changedsince" โ€” keep as inline code or in a block. Use inline code with the explanation.

Section 5.3 Request-URI Template has code block: "{/service-prefix,data-prefix}/zones{/tzid}{?start,end}". Keep verbatim.

Description of list: "with publisher, version, aliases, and optional localized data." Rewrite in voice but keep facts.

Parts of the 5.1.1 example: "Date: Wed, 4 Jun 2008 09:32:12 GMT" verbatim.

Note "5.3.4" the JSON snippet with "untruncated": false โ€” keep. The text "Note the presence of a "STANDARD" component" โ€” rewrite in voice with quotes preserved.

Don't sign off. No preamble. Output only rewrite. Let's write. Keep emojis, colour tags not wrapping keywords. Don't put colour tags inside backticks.

Keep "Request-URI Template:" field line in code block? Original is indented text, not code. The spec says copy grammars exactly in code blocks. The Name/Request-URI Template are structured; I'll put templates in code blocks. Parameters lists: I'll render as bullets.

Now write it. Keep "Response:" details. For 5.2 Response: "A JSON object containing a "synctoken" member and a "timezones" member; see Section 6.2." Keep Section 6.2 citation.

Ensure "optional" in lowercase not an issueโ€”only capitals matter. "optional localized data" lowercase fine.

Writing now.### 5.1.1. Example: get capabilities โœจ

okay so here's what a capabilities request and response look like in real life ๐Ÿฆ‹

Request <<
GET /servlet/timezone/capabilities HTTP/1.1
Host: tz.example.com
Response <<
HTTP/1.1 200 OK
Date: Wed, 4 Jun 2008 09:32:12 GMT
Content-Type: application/json; charset="utf-8"
Content-Length: xxxx

{
  "version": 1,

  "info": {
    "primary-source": "Olson:2011m",
    "formats": [
      "text/calendar",
      "application/calendar+xml",
      "application/calendar+json"
    ],
    "truncated" : {
      "any": false,
      "ranges": [
        {
          "start": "1970-01-01T00:00:00Z",
          "end": "*"
        },
        {
          "start":"2010-01-01T00:00:00Z",
          "end":"2020-01-01T00:00:00Z"
        }
      ],
      "untruncated": true
    },
    "provider-details": "http://tz.example.com/about.html",
    "contacts": ["mailto:tzs@example.org"]
  },
  "actions": [
    {
      "name": "capabilities",
      "uri-template": "/servlet/timezone/capabilities",
      "parameters": []
    },

    {
      "name": "list",
      "uri-template": "/servlet/timezone/zones{?changedsince}",
      "parameters": [
        {
          "name": "changedsince",
          "required": false,
          "multi": false
        }
      ]
    },

    {
      "name": "get",
      "uri-template": "/servlet/timezone/zones{/tzid}{?start,end}",
      "parameters": [
        {
          "name": "start",
          "required": false,
          "multi": false
        },
        {
          "name": "end",
          "required": false,
          "multi": false
        }
      ]
    },

    {
      "name": "expand",
      "uri-template":
        "/servlet/timezone/zones{/tzid}/observances{?start,end}",
      "parameters": [
        {
          "name": "start",
          "required": true,
          "multi": false
        },
        {
          "name": "end",
          "required": true,
          "multi": false
        }
      ]
    },

    {
      "name": "find",
      "uri-template": "/servlet/timezone/zones{?pattern}",
      "parameters": [
        {
          "name": "pattern",
          "required": true,
          "multi": false
        }
      ]
    },

    {
      "name": "leapseconds",
      "uri-template": "/servlet/timezone/leapseconds",
      "parameters": []
    }
  ]
}

5.2. "list" Action ๐Ÿ’–

Name: list

Request-URI Template:

{/service-prefix,data-prefix}/zones{?changedsince}

Description: this one lists every time zone identifier in summary format ๐Ÿ“‹ Each entry comes with its publisher, version, aliases, and optional localized data. It also hands back an opaque synchronization token for the whole response ๐Ÿ”‘ If the "changedsince" URI query parameter shows up, its value MUST correspond to a synchronization token that was returned earlier. When "changedsince" is used, the server MUST return only the time zones that changed since that token. And if the server doesn't support the "changedsince" value, the server MUST return all time zones, treating the request as if there was no "changedsince" at all. bestie, that's a fallback I'm obsessed with ๐Ÿซถ

Parameters:

  • changedsince: OPTIONAL, and MUST NOT occur more than once.

Response: a JSON object with a "synctoken" member and a "timezones" member. See Section 6.2 ๐ŸŒธ

Possible Error Codes:

  • urn:ietf:params:tzdist:error:invalid-changedsince: the "changedsince" URI query parameter appears more than once. ๐Ÿ˜ญ

5.2.1. Example: List Time Zone Identifiers ๐Ÿงš

here the client asks for the full set of time zone identifiers โœจ

Request <<
GET /servlet/timezone/zones HTTP/1.1
Host: tz.example.com
Response <<
HTTP/1.1 200 OK
Date: Wed, 4 Jun 2008 09:32:12 GMT
Content-Type: application/json; charset="utf-8"
Content-Length: xxxx

{
  "synctoken": "2009-10-11T09:32:11Z",
  "timezones": [
    {
      "tzid": "America/New_York",
      "etag": "123456789-000-111",
      "last-modified": "2009-09-17T01:39:34Z",
      "publisher": "Example.com",
      "version": "2015a",
      "aliases":["US/Eastern"],
      "local-names": [
        {
          "name": "America/New_York",
          "lang": "en_US"
        }
      ]
    },
    ...other time zones...
  ]
}

5.3. "get" Action ๐ŸŽ€

Name: get

Request-URI Template:

{/service-prefix,data-prefix}/zones{/tzid}{?start,end}

The "tzid" variable value is REQUIRED so this action can be told apart from the "list" action. no mix-ups allowed ๐Ÿ’…

Description: this action returns a time zone ๐ŸŒˆ The response MUST contain an ETag response header field that shows the current value of the strong entity tag for the time zone resource.

If there's no Accept HTTP request header field, the server MUST return the time zone data with the "text/calendar" media type ๐Ÿ“…

If the "tzid" variable value is actually a time zone alias, the server will return the matching time zone data and use the alias as the identifier inside that data. The server MAY include one or more "TZID-ALIAS-OF" properties (see Section 7.2) in the time zone data. Those tell you about other identifiers that share the same matching time zone identifier as an alias ๐Ÿฆ‹

Parameters:

  • start=<date-time>: OPTIONAL, and MUST NOT occur more than once. This specifies the inclusive UTC date-time value where the returned time zone data gets truncated at its start ๐ŸŒธ
  • end=<date-time>: OPTIONAL, and MUST NOT occur more than once. This specifies the exclusive UTC date-time value where the returned time zone data gets truncated at its end ๐ŸŒธ

Response: a document with all the requested time zone data in the format that was specified ๐Ÿ“„

Possible Error Codes:

  • urn:ietf:params:tzdist:error:tzid-not-found: no time zone matching the "tzid" path segment value was found. ๐Ÿ™ˆ
  • urn:ietf:params:tzdist:error:invalid-format: the Accept request header field from the client didn't include a media type for time zone data that the server supports. ๐Ÿ’€
  • urn:ietf:params:tzdist:error:invalid-start: the "start" URI query parameter has an incorrect value, or it appears more than once, or it doesn't match one of the fixed truncation range start values advertised in the "capabilities" action response. ๐Ÿ˜ญ
  • urn:ietf:params:tzdist:error:invalid-end: the "end" URI query parameter has an incorrect value, or it appears more than once, or its value is less than or equal to the "start" URI query parameter, or it doesn't match one of the fixed truncation range end values advertised in the "capabilities" action response. ๐Ÿ˜ญ

5.3.1. Example: Get Time Zone Data ๐Ÿ’œ

in this one the client asks for a specific time zone by its identifier ๐Ÿ—ฝ

Request <<
GET /servlet/timezone/zones/America%2FNew_York HTTP/1.1
Host: tz.example.com
Accept:text/calendar
Response <<
HTTP/1.1 200 OK
Date: Wed, 4 Jun 2008 09:32:12 GMT
Content-Type: text/calendar; charset="utf-8"
Content-Length: xxxx
ETag: "123456789-000-111"

BEGIN:VCALENDAR
...
BEGIN:VTIMEZONE
TZID:America/New_York
...
END:VTIMEZONE
END:VCALENDAR

5.3.2. Example: Conditional Get Time Zone Data ๐ŸŒท

this time the client asks for the same zone but adds an If-None-Match header field ๐Ÿ™Œ The value is a previously returned ETag header field, or the value of the "etag" member in a JSON "timezone" object from a "list" action response. Since nothing changed on the server, you get a 304 response back. Iconic caching behaviour ๐Ÿ“

Request <<
GET /servlet/timezone/zones/America%2FNew_York HTTP/1.1
Host: tz.example.com
Accept:text/calendar
If-None-Match: "123456789-000-111"
Response <<
HTTP/1.1 304 Not Modified
Date: Wed, 4 Jun 2008 09:32:12 GMT

5.3.3. Example: Get Time Zone Data Using a Time Zone Alias ๐Ÿฆ‹

here the client asks for a time zone using an aliased identifier ๐Ÿชž The server sends back the time zone data under that identifier, and it lists two aliases too.

Request <<
GET /servlet/timezone/zones/US%2FEastern HTTP/1.1
Host: tz.example.com
Accept:text/calendar
Response <<
HTTP/1.1 200 OK
Date: Wed, 4 Jun 2008 09:32:12 GMT
Content-Type: text/calendar; charset="utf-8"
Content-Length: xxxx
ETag: "123456789-000-111"

BEGIN:VCALENDAR
...
BEGIN:VTIMEZONE
TZID:US/Eastern
TZID-ALIAS-OF:America/New_York
TZID-ALIAS-OF:America/Montreal
...
END:VTIMEZONE
END:VCALENDAR

5.3.4. Example: Get Truncated Time Zone Data โœ‚๏ธ

okay so say the server advertises a "truncated" object in its "capabilities" response that looks like this:

"truncated": {
  "any": false,
  "ranges": [
    {"start": "1970-01-01T00:00:00Z", "end": "*"},
    {"start":"2010-01-01T00:00:00Z", "end":"2020-01-01T00:00:00Z"}
  ],
  "untruncated": false
}

In this example the client asks for a time zone truncated at one of the ranges the server listed ๐ŸŒˆ Look out for the "STANDARD" component. It matches the start point of the truncation range, converted to local time using the UTC offset in effect at the matching UTC time. Also, check the "TZUNTIL" (Section 7.1) iCalendar property in the "VTIMEZONE" component. It tells you the upper bound on how long the time zone data stays valid ๐Ÿง 

Request <<
GET /servlet/timezone/zones/America%2FNew_York
  ?start=2010-01-01T00:00:00Z&end=2020-01-01T00:00:00Z HTTP/1.1
Host: tz.example.com
Accept:text/calendar
Response <<
HTTP/1.1 200 OK
Date: Wed, 4 Jun 2008 09:32:12 GMT
Content-Type: text/calendar; charset="utf-8"
Content-Length: xxxx
ETag: "123456789-000-111"

BEGIN:VCALENDAR
...
BEGIN:VTIMEZONE
TZID:America/New_York
TZUNTIL:20200101T000000Z
BEGIN:STANDARD
DTSTART:20101231T190000
TZNAME:EST
TZOFFSETFROM:-0500
TZOFFSETTO:-0500
END:STANDARD
...
END:VTIMEZONE
END:VCALENDAR

5.3.5. Example: Request for a Nonexistent Time Zone ๐Ÿ‘ป

the client asks for a time zone identifier, but plot twist: no time zone exists with that identifier ๐Ÿ’€

Request <<
GET /servlet/timezone/zones/America%2FPittsburgh HTTP/1.1
Host: tz.example.com
Accept:application/calendar+json
Response <<
HTTP/1.1 404 Not Found
Date: Wed, 4 Jun 2008 09:32:12 GMT
Content-Type: application/problem+json; charset="utf-8"
Content-Language: en
Content-Length: xxxx

{
  "type": "urn:ietf:params:tzdist:error:tzid-not-found",
  "title": "Time zone identifier was not found on this server",
  "status": 404
}

5.4. "expand" Action ๐Ÿฆ‹

Name: expand

Request-URI Template:

{/service-prefix,data-prefix}/zones{/tzid}/observances{?start,end}

The tzid variable value is REQUIRED ๐Ÿ’–, so don't skip it, bestie.

Description: This action expands the time zone you ask for into a list of onset start date/time values (in UTC) and their UTC offsets โœจ. The response MUST contain an ETag response header field that shows the current value of the strong entity tag for the time zone being expanded ๐ŸŽ€.

Parameters:

  • start=<date-time>: REQUIRED, and MUST occur only once. This is the inclusive UTC date-time where your period of interest starts ๐ŸŒธ.
  • end=<date-time>: REQUIRED, and MUST occur only once. This is the exclusive UTC date-time where your period ends. Exclusive means the end value is the date just after your range ๐Ÿ™ˆ. Like, if you only want the year 2014, you'd use a start of "2014-01-01T00:00:00Z" and an end of "2015-01-01T00:00:00Z". Also, an error happens if the end value is less than or equal to the start value ๐Ÿ’€.

Response: You get a JSON object with a "tzid" member and an "observances" member, see Section 6.3 ๐Ÿงš. If the time zone isn't fully defined over the range you asked for (for example, because of truncation), then the server MUST include "start" and/or "end" members in the JSON response, so you know the actual start and end points of the observances you got back ๐Ÿ‘‘. The server MUST also include an expanded observance that represents the time zone information in effect at the start of the returned observance period ๐Ÿ’œ.

Possible Error Codes:

  • urn:ietf:params:tzdist:error:tzid-not-found: No time zone matching the "tzid" path segment value was found ๐Ÿ˜ญ.
  • urn:ietf:params:tzdist:error:invalid-start: The "start" URI query parameter has a wrong value, shows up more than once, is missing, or has a value outside any fixed truncation ranges advertised in the "capabilities" action response ๐Ÿ™…โ€โ™€๏ธ.
  • urn:ietf:params:tzdist:error:invalid-end: The "end" URI query parameter has a wrong value, shows up more than once, has a value less than or equal to the "start" URI query parameter, or has a value outside any fixed truncation ranges advertised in the "capabilities" action response ๐Ÿ’….

5.4.1. Example: Expanded JSON Data Format

Okay, in this example the client asks for a time zone in the expanded form ๐ŸŒˆ.

Request <<
GET /servlet/timezone/zones/America%2FNew_York/observances
 ?start=2008-01-01T00:00:00Z&end=2009-01-01T00:00:00Z HTTP/1.1
Host: tz.example.com
Response <<
HTTP/1.1 200 OK
Date: Mon, 11 Oct 2009 09:32:12 GMT
Content-Type: application/json; charset="utf-8"
Content-Length: xxxx
ETag: "123456789-000-111"

{
  "tzid": "America/New_York",
  "observances": [
    {
      "name": "Standard",
      "onset": "2008-01-01T00:00:00Z",
      "utc-offset-from": -18000,
      "utc-offset-to": -18000
    },
    {
      "name": "Daylight",
      "onset": "2008-03-09T07:00:00Z",
      "utc-offset-from": -18000,
      "utc-offset-to": -14400
    },
    {
      "name": "Standard",
      "onset": "2008-11-02T06:00:00Z",
      "utc-offset-from": -14400,
      "utc-offset-to": -18000
    },
  ]
}

5.5. "find" Action ๐Ÿ”

Name: find

Request-URI Template:

{/service-prefix,data-prefix}/zones{?pattern}

Description: This action lets a client search the time zone data distribution service for a matching identifier, alias, or localized name ๐Ÿฆ‹. It uses a simple "glob" style pattern match against the names the server knows, and the asterisk (*) is the wildcard character โœจ. Pattern-match strings have to be percent-encoded, then decoded when used in the URI query parameter. Here are the options:

  • No wildcard present: an exact text match is done, e.g., "xyz" ๐ŸŽฏ.
  • Wildcard as the first character only: an ends-with text match is done, e.g., "*xyz" ๐ŸŒธ.
  • Wildcard as the last character only: a starts-with text match is done, e.g., "xyz*" ๐Ÿ’–.
  • Wildcard as both the first and last characters: a substring text match is done, e.g., "*xyz*" ๐ŸŒˆ.

Escaping \ and *: If you want to match a * (0x2A) or a \ (0x5C) character in a time zone identifier, you have to "escape" it in the pattern by putting a single \ (0x5C) in front of it ๐Ÿ™ˆ. For example, the pattern "\*Test\\Time\*Zone\*" is used for an exact match against the time zone identifier "*Test\Time*Zone*". An unescaped * MUST NOT appear in the middle of the string, and it MUST result in an error ๐Ÿ’€. An unescaped \ MUST NOT appear anywhere in the string, and it MUST result in an error ๐Ÿ˜ญ.

Also, when matching:

Underscores: Underscore characters (0x5F) in time zone identifiers MUST be mapped to a single space character (0x20) before string comparison. That happens in both the pattern and the time zone identifiers being matched ๐Ÿงš. This is why America/New_York can match a query for *New York* ๐Ÿ‘‘.

Case mapping: ASCII characters in the range 0x41 (A) through 0x5A (Z) MUST be mapped to their lowercase equivalents, in both the pattern and the time zone identifiers being matched ๐Ÿ’œ.

Parameters:

  • pattern=<text>: REQUIRED, and MUST occur only once ๐ŸŽ€.

Response: The response has the same format as the "list" action, with one result object for each successful match, see Section 6.2 โœจ.

Possible Error Codes:

  • urn:ietf:params:tzdist:error:invalid-pattern: The "pattern" URI query parameter has a wrong value, or shows up more than once ๐Ÿ˜ฌ.

5.5.1. Example: find action

In this example, the client asks for data about the time zone "US/Eastern" ๐Ÿ—ฝ.

Request <<
GET /servlet/timezone/zones?pattern=US/Eastern HTTP/1.1
Host: tz.example.com
Response <<
HTTP/1.1 200 OK
Date: Wed, 4 Jun 2008 09:32:12 GMT
Content-Type: application/json; charset="utf-8"
Content-Length: xxxx

{
  "synctoken": "2009-10-11T09:32:11Z",
  "timezones": [
    {
      "tzid": "America/New_York",
      "etag": "123456789-000-111",
      "last-modified": "2009-09-17T01:39:34Z",
      "publisher": "Example.com",
      "version": "2015a",
      "aliases":["US/Eastern"],
      "local-names": [
        {
          "name": "America/New_York",
          "lang": "en_US"
        }
      ]
    },
    {
      "tzid": "America/Detroit",
      "etag": "123456789-999-222",
      "last-modified": "2009-09-17T01:39:34Z",
      "publisher": "Example.com",
      "version": "2015a",
      "aliases":["US/Eastern"],
      "local-names": [
        {
          "name": "America/Detroit",
          "lang": "en_US"
        }
      ]
    },
    ...
  ]
}

5.6. "leapseconds" Action โญ

Name: leapseconds

Request-URI Template:

{/service-prefix,data-prefix}/leapseconds

Description: This action lets a client ask the time zone data distribution service for the current leap-second information the server has ๐ŸŒธ.

Parameters: None

Response: You get a JSON object with an "expires" member, a "publisher" member, a "version" member, and a "leapseconds" member, see Section 6.4 ๐Ÿ’–. The "expires" member tells you the latest date covered by leap-second information ๐Ÿซถ. For example (as in Section 5.6.1), if "expires" is "2014-06-28" and the latest leap-second change was at "2012-07-01", then there are no leap seconds added (or removed) between those two dates, and leap-second info past the "expires" date isn't available yet ๐Ÿ•ฐ๏ธ.

The "leapseconds" member holds a list of JSON objects, and each one has a "utc-offset" member and an "onset" member ๐Ÿง . The "onset" member gives the date (with an implied time of 00:00:00 UTC) when the matching UTC offset from TAI kicks in ๐Ÿฆ‹. Put simply, a leap second is added or removed just before 00:00:00 UTC on the onset date ๐Ÿ’…. When a leap second is added, the "utc-offset" value goes up by one โœจ. When a leap second is removed, the "utc-offset" value goes down by one ๐Ÿ”ฅ.

Possible Error Codes: No specific code.

5.6.1. Example: Get Leap-Second Information

In this example, the client asks the server for the current leap-second information ๐ŸŒˆ.

Request <<
GET /servlet/timezone/leapseconds HTTP/1.1
Host: tz.example.com
Response <<
HTTP/1.1 200 OK
Date: Wed, 4 Jun 2008 09:32:12 GMT
Content-Type: application/json; charset="utf-8"
Content-Length: xxxx

{
  "expires": "2015-12-28",
  "publisher": "Example.com",
  "version": "2015d",
  "leapseconds": [
    {
      "utc-offset": 10,
      "onset": "1972-01-01",
    },
    {
      "utc-offset": 11,
      "onset": "1972-07-01",
    },
    ...
    {
      "utc-offset": 35,
      "onset": "2012-07-01",
    },
    {
      "utc-offset": 36,
      "onset": "2015-07-01",
    }
  ]
}

6. JSON Definitions ๐Ÿงธ

[RFC7159] is where the structure of JSON objects gets defined, using a set of primitive elements ๐Ÿ“š. This specification describes the JSON objects it uses with the following set of rules:

  • OBJECT: represents a JSON object, defined in Section 4 of [RFC7159]. OBJECT is followed by a parenthesized list of MEMBER rule names. If a member rule name has a "?" (0x3F) character in front of it, that member is optional. Otherwise, all members are required. If two or more member rule names are present, separated from each other by a "|" (0x7C) character, then only one of those members MUST be present in the JSON object ๐ŸŒท. JSON object members are unordered, so the order used in the rules doesn't matter ๐Ÿ’ซ.
  • MEMBER: represents a member of a JSON object, defined in Section 4 of [RFC7159]. MEMBER is followed by a rule name, the name of the member, a ":", and then the value. A value can be one of the OBJECT, ARRAY, NUMBER, STRING, or BOOLEAN rules ๐ŸŽ€.
  • ARRAY: represents a JSON array, defined in Section 5 of [RFC7159]. ARRAY is followed by a value (one of OBJECT, ARRAY, NUMBER, STRING, or BOOLEAN), which tells you the type of items used in the array ๐Ÿ“.
  • NUMBER: represents a JSON number, defined in Section 6 of [RFC7159] ๐Ÿ”ข.
  • STRING: represents a JSON string, defined in Section 7 of [RFC7159] ๐Ÿงต.
  • BOOLEAN: represents either of the JSON values "true" or "false", defined in Section 3 of [RFC7159] โœ….
  • Comments: a line starting with a ";" (0x3B) character is a comment ๐Ÿ’ฌ.
Heads up, bestie: clients MUST ignore any unexpected JSON members in responses from the server ๐Ÿ™ˆ. Don't freak out if the server sends extra stuff, just skip it ๐Ÿ’–.

6.1. capabilities Action Response โœจ

okay bestie, this is the rulebook for the JSON doc the server sends back when someone asks for a "capabilities" action ๐ŸŽ€ It's giving "here's everything I can do" energy ๐Ÿ’…

; root object
OBJECT (version, info, actions)

; The version number of the protocol supported - MUST be 1
MEMBER version "version" : NUMBER
; object containing service information
; Only one of primary_source or secondary_source MUST be present
MEMBER info "info" : OBJECT (
  primary_source | secondary_source,
  formats,
  ?truncated,
  ?provider_details,
  ?contacts
)

; The source of the time zone data provided by a "primary" server
MEMBER primary_source "primary-source" : STRING

; The time zone data server from which data is provided by a
; "secondary" server
MEMBER secondary_source "secondary-source" : STRING

; Array of one or more media types for the time zone data formats
; that the server can return
MEMBER formats "formats" : ARRAY STRING

; Present if the server is providing truncated time zone data.  The
; value is an object providing details of the supported truncation
; modes.
MEMBER truncated "truncated" : OBJECT: (
  any,
  ?ranges,
  ?untruncated
)

; Indicates whether the server can truncate time zone data at any
; start or end point.  When set to "true", any start or end point is
; a valid value for use with the "start" and "end" URI query
; parameters in a "get" action request.
MEMBER any "any" : BOOLEAN

; Indicates which ranges of time the server has truncated data for.
; A value from this list may be used with the "start" and "end" URI
; query parameters in a "get" action request.  Not present if "any"
; is set to "true".
MEMBER ranges "ranges" : ARRAY OBJECT (range-start, range-end)

; UTC date-time value (per [RFC3339]) for inclusive start of the
; range, or the single character "*" to indicate a value
; corresponding to the lower bound supplied by the publisher of the
; time zone data
MEMBER range-start "start" : STRING
; UTC date-time value (per [RFC3339]) for exclusive end of the range,
; or the single character "*" to indicate a value corresponding to
; the upper bound supplied by the publisher of the time zone data
MEMBER range-end "end" : STRING

; Indicates whether the server can supply untruncated data.  When
; set to "true", indicates that, in addition to truncated data being
; available, the server can return untruncated data if a "get"
; action request is executed without a "start" or "end" URI query
; parameter.
MEMBER untruncated "untruncated" : BOOLEAN

; A URI where human-readable details about the time zone service
; is available
MEMBER provider_details "provider-details" : STRING

; Array of URIs providing contact details for the server
; administrator
MEMBER contacts "contacts" : ARRAY STRING

; Array of actions supported by the server
MEMBER actions "actions" : ARRAY OBJECT (
  action_name,
  action_params
)

; Name of the action
MEMBER action_name: "name" : STRING

; Array of request-URI query parameters supported by the action
MEMBER action_params: "parameters" ARRAY OBJECT (
  param_name,
  ?param_required,
  ?param_multi,
  ?param_values
)

; Name of the parameter
MEMBER param_name "name" : STRING

; If true, the parameter has to be present in the request-URI
; default is false
MEMBER param_required "required" : BOOLEAN

; If true, the parameter can occur more than once in the request-URI
; default is false
MEMBER param_multi "multi" : BOOLEAN,
; An array that defines the allowed set of values for the parameter
; In the absence of this member, any string value is acceptable
MEMBER param_values "values" ARRAY STRING

The big picture: the root has a version, an info object, and an actions array ๐Ÿฆ‹ Inside info, you get exactly one of primary-source or secondary-source (that's a MUST, no cap ๐Ÿ˜ญ), plus formats, and optional extras like truncated, provider-details, and contacts โœจ

6.2. list/find Action Response โœจ

this one is for the "list" and "find" actions, and honestly it's giving tidy list energy ๐ŸŒธ Here's the JSON shape:

; root object
OBJECT (synctoken, timezones)

; Server-generated opaque token used for synchronizing changes
MEMBER synctoken "synctoken" : STRING

; Array of time zone objects
MEMBER timezones "timezones" : ARRAY OBJECT (
  tzid,
  etag,
  last_modified,
  publisher,
  version,
  ?aliases,
  ?local_names,
)

; Time zone identifier
MEMBER tzid "tzid" : STRING

; Current ETag for the corresponding time zone data resource
MEMBER etag "etag" : STRING

; Date/time when the time zone data was last modified
; UTC date-time value as specified in [RFC3339]
MEMBER last_modified "last-modified" : STRING

; Time zone data publisher
MEMBER publisher "publisher" : STRING

; Current version of the time zone data as defined by the
; publisher
MEMBER version "version" : STRING

; An array that lists the set of time zone aliases available
; for the corresponding time zone
MEMBER aliases "aliases" : ARRAY STRING
; An array that lists the set of localized names available
; for the corresponding time zone
MEMBER local_names "local-names" : ARRAY OBJECT (
  lname, lang, ?pref
)

; Language tag for the language of the associated name
MEMBER: lang "lang" : STRING

; Localized name
MEMBER lname "name" : STRING

; Indicates whether this is the preferred name for the associated
; language default: false
MEMBER pref "pref" : BOOLEAN

6.3. expand Action Response โœจ

this is the "expand" action's JSON, and it's the one where you get the observances ๐Ÿ•ฐ๏ธ Lowkey iconic:

; root object
OBJECT (
  tzid,
  ?start,
  ?end,
  observances
)

; Time zone identifier
MEMBER tzid "tzid" : STRING

; The actual inclusive start point for the returned observances
; if different from the value of the "start" URI query parameter
MEMBER start "start" : STRING

; The actual exclusive end point for the returned observances
; if different from the value of the "end" URI query parameter
MEMBER end "end" : STRING

; Array of time zone objects
MEMBER observances "observances" : ARRAY OBJECT (
  oname,
  ?olocal_names,
  onset,
  utc_offset_from,
  utc_offset_to
)
; Observance name
MEMBER oname "name" : STRING

; Array of localized observance names
MEMBER olocal_names "local-names" : ARRAY STRING

; UTC date-time value (per [RFC3339]) at which the observance takes
; effect
MEMBER onset "onset" : STRING

; The UTC offset in seconds before the start of this observance
MEMBER utc_offset_from "utc-offset-from" : NUMBER

; The UTC offset in seconds at and after the start of this observance
MEMBER utc_offset_to "utc-offset-to" : NUMBER

6.4. leapseconds Action Response โœจ

last one in this batch, the "leapseconds" response ๐Ÿงš It tracks the UTC offset from TAI, and it's giving precise and proud of it ๐Ÿ’œ

; root object
OBJECT (
  expires,
  publisher,
  version,
  leapseconds
)

; Last valid date covered by the data in this response
; full-date value as specified in [RFC3339]
MEMBER expires "expires" : STRING

; Leap-second information publisher
MEMBER publisher "publisher" : STRING

; Current version of the leap-second information as defined by the
; publisher
MEMBER version "version" : STRING

; Array of leap-second objects
MEMBER leapseconds "leapseconds" : ARRAY OBJECT (
  utc_offset,
  onset
)
; The UTC offset from TAI in seconds in effect at and after the
; specified date
MEMBER utc_offset "utc-offset" : NUMBER

; full-date value (per [RFC3339]) at which the new UTC offset takes
; effect, at T00:00:00Z
MEMBER onset "onset" : STRING

7. New iCalendar Properties โœจ

okay so this section adds some brand new iCalendar properties to the mix ๐Ÿฆ‹ They all live in VTIMEZONE components, and they're here to fix gaps that the existing spec didn't cover ๐Ÿ’–

7.1. Time Zone Upper Bound ๐ŸŒˆ

Property Name: TZUNTIL

Purpose: this one specifies an upper bound for how long the data inside a "VTIMEZONE" component stays valid ๐Ÿ•ฐ๏ธ

Value Type: DATE-TIME

Property Parameters: IANA and non-standard property parameters can be specified on this property โœจ

Conformance: this property can be specified zero times or one time within "VTIMEZONE" calendar components ๐Ÿ’…

Description: the value MUST be specified in the UTC time format. ๐Ÿ˜ญ

Here's the tea ๐Ÿ“ Time zone data in a "VTIMEZONE" component might only cover a fixed window. The start of that window is pretty obvious, since it's the earliest observance defined by the "STANDARD" and "DAYLIGHT" subcomponents. The end is trickier ๐Ÿ™ˆ You can't just look at the observance with the latest onset time and call it a day, and [RFC5545] doesn't give you any way to get that upper bound. So this spec introduces TZUNTIL to fill that gap. It's an "exclusive" UTC date-time value that marks the last moment the time zone data is considered valid โญ

This property is also used by time zone data distribution servers to show the truncation range end point of their time zone data (as described in Section 3.9) ๐ŸŽ€

Format Definition: this property is defined by the following notation in ABNF [RFC5234]:

tzuntil      = "TZUNTIL" tzuntilparam ":" date-time CRLF

tzuntilparam = *(";" other-param)

Example: imagine a time zone based on astronomical observations that has well-defined onset times through the year 2025. The first onset in 2026 is only known approximately, though. In that case, the "TZUNTIL" property could be specified like this:

TZUNTIL:20260101T000000Z

7.2. Time Zone Identifier Alias Property ๐Ÿฆ‹

Property Name: TZID-ALIAS-OF

Purpose: this one specifies a time zone identifier for which the main time zone identifier is an alias ๐Ÿ’œ

Value Type: TEXT

Property Parameters: IANA and non-standard property parameters can be specified on this property ๐ŸŒธ

Conformance: this property can be specified zero or more times within "VTIMEZONE" calendar components โœจ

Description: when the "VTIMEZONE" component uses a time zone identifier alias for the TZID property value, the TZID-ALIAS-OF property is used to point to the time zone identifier of the other time zone (see Section 3.7) ๐Ÿ’–

Format Definition: this property is defined by the following notation in ABNF [RFC5234]:

tzid-alias-of    = "TZID-ALIAS-OF" tzidaliasofparam ":"
                        [tzidprefix] text CRLF

tzidaliasofparam = *(";" other-param)

;tzidprefix defined in [RFC5545].

Example: here's what it looks like in action ๐Ÿงš

TZID-ALIAS-OF:America/New_York

8. Security Considerations โœจ

okay bestie, this section is SERIOUS ๐Ÿ˜ญ๐Ÿ” time zone data decides what time your device thinks it is and how calendar invites line up across the world, so getting it from a shady source is a whole disaster. we need a reliable source, no cap ๐Ÿซถ

Servers running a time zone data distribution service MUST support HTTP over Transport Layer Security (TLS) ๐Ÿ”’, as defined by [RFC2818] and [RFC5246], with the best practices in [RFC7525] ๐Ÿ’…. Servers MAY also offer the service over plain HTTP without TLS, but secondary servers MUST use TLS when they fetch data from a primary server. no exceptions, periodt ๐Ÿ’€

Clients SHOULD use Transport Layer Security as defined by [RFC2818], unless they've been specifically configured otherwise ๐ŸŽ€. Clients that are set up to use the TLS-based service MUST NOT fall back to the non-TLS service if the TLS one isn't available ๐Ÿ™…โ€โ™€๏ธ. Also, clients MUST NOT follow HTTP redirects from a TLS service to a non-TLS service ๐Ÿšซ. And when TLS is in use, clients MUST verify the identity of the server using a standard, secure mechanism, like the certificate verification process in [RFC6125] or DANE [RFC6698] ๐Ÿ”โœจ

Heads up: a malicious attacker who can see DNS server data, or who gets spoofed answers cached in a recursive resolver, can potentially make clients connect to any server they pick ๐Ÿ˜ฑ๐Ÿ•ต๏ธโ€โ™€๏ธ

Without a secure DNS option, clients SHOULD check that the target FQDN in the SRV record is the same as the original service domain they queried, or a sub-domain of it ๐Ÿง. A lot of the time, client config gets handled automatically with zero user input, so any mismatch between the original service domain and the target FQDN counts as a failure, and the client MUST NOT attempt to connect to that target server ๐Ÿ›‘. When TLS is being used, the certificate SHOULD include an SRV-ID field as per [RFC4985] that matches the DNS SRV queries clients use for service discovery ๐Ÿชช. If an SRV-ID field is in the certificate, clients MUST match the SRV-ID value against the service type and domain from the DNS SRV request the client made to find the service ๐Ÿ’–

Time zone data servers SHOULD protect themselves from badly implemented or malicious clients by throttling high request rates or frequent requests for big chunks of data ๐Ÿšฆ. Clients can dodge being throttled by using the polling capabilities in Section 4.1.4 ๐Ÿฆ‹. Servers MAY require some form of authentication or authorization of clients (including secondary servers), as per [RFC7235], to limit who gets access or to better spot problematic clients ๐Ÿ”๐Ÿ‘‘


9. Privacy Considerations ๐Ÿซฃ

okay so this one is giving surveillance vibes ๐Ÿ‘€๐Ÿ’œ the kinds of requests a client makes can be used to "fingerprint" specific clients or devices, and that can leak what users are up to ๐Ÿ˜ณ. Like, a client that only downloads time zone data as-needed will basically leak the fact that a user's device moved from one time zone to another, or that the user is getting scheduling messages from someone in a different time zone ๐ŸŒโœˆ๏ธ

Clients need to be aware of how an untrusted server or a network observer could track them, and take precautions like these ๐Ÿง โœจ

  1. Always use TLS to connect to the server. ๐Ÿ”’
  2. Avoid use of TLS session resumption. ๐Ÿ™…โ€โ™€๏ธ
  3. Always fetch and synchronize the entire set of time zone data, so you don't leak which time zones the client actually uses ๐ŸŒธ
  4. Randomize the order in which individual time zones are fetched using the "get" action, when retrieving a set of time zones based on a "list" action response ๐ŸŽฒ
  5. Avoid use of conditional HTTP requests [RFC7232] with the "get" action, so servers can't track clients through client-specific ETag header field values ๐Ÿท๏ธ
  6. Avoid use of cookies in HTTP requests [RFC6265] ๐Ÿช๐Ÿšซ
  7. Avoid use of authenticated HTTP requests. ๐Ÿ™ˆ
  8. When doing periodic polling to check for updates, apply a random offset (positive or negative) to the next poll time, so servers can't identify the client by the specific periodicity of its polling ๐Ÿ•ฐ๏ธ๐ŸŒ€
  9. A server trying to "fingerprint" clients might insert a "fake" time zone into the time zone data, using a unique identifier for each client making a request ๐Ÿ‘ป. The server can then watch for client requests that refer to that "fake" time zone and track each client's activity. It's hard for clients to spot a "fake" time zone since new time zones get added occasionally ๐Ÿ˜ค. One option to mitigate this is for the client to use two time zone data distribution servers from two independent providers that serve time zone data from the same publisher ๐Ÿค. The client can compare the list of time zones from each server (assuming they both have the same version of the time zone data from the common publisher) and detect ones that appear on one server but not the other ๐Ÿ”. Alternatively, the client can check the publisher's data directly to verify the time zones match the set the publisher has ๐Ÿ“šโœ…

Note that some of the above recommendations make the protocol less efficient, since you end up fetching data that might not be relevant to the client ๐ŸŒ๐Ÿ’ญ

An organization can set up a secondary server within its own domain and configure its clients to use that server, which protects the organization's users from being tracked by an untrusted time zone data distribution server ๐Ÿข๐Ÿซถ. Clients can then use more-efficient protocol interactions, free from the concerns above, on the basis that their organization's server is trusted ๐Ÿ˜Œ. When doing this, the secondary server should follow the recommendations for clients (listed in the previous paragraph) so the untrusted server can't learn anything about the organization as a whole ๐Ÿงฑ. Note, however, that client requests to the secondary server are still subject to tracking by a network observer, so clients ought to apply some of the randomization techniques from the list above ๐ŸŽฒ๐Ÿ’…

Servers that want to avoid accidentally storing information that could identify clients can take these precautions ๐Ÿ—‚๏ธโœจ

  1. Avoid logging client request activity, or anonymize information in any logs (e.g., client IP address, client user-agent details, authentication credentials, etc.). ๐Ÿ™ˆ๐Ÿ“
  2. Add an unused HTTP response header to each response with a random amount of data in it (e.g., to pad the overall request size to the nearest power-of-2 or 128-byte boundary), so network traffic analysis can't expose which time zones are being fetched when TLS is in use ๐ŸŽ€๐Ÿ”

10. IANA Considerations ๐Ÿฆ‹

okay this section is all about the paperwork ๐Ÿ“‹๐Ÿ’… This specification defines a new registry of "actions" for the time zone data distribution service protocol ๐ŸŒˆ. It defines a "well-known" URI using the registration procedure and template from Section 5.1 of [RFC5785] ๐Ÿงš, creates two new SRV service label aliases ๐Ÿท๏ธ, and defines one new iCalendar property parameter as per the registration procedure in [RFC5545] ๐Ÿ“…. It also adds a new "TZDIST Identifiers Registry" to the IETF parameters URN sub-namespace as per [RFC3553], for use with protocol related error codes ๐Ÿ”–โœจ

10.1. Service Actions Registration

IANA has created a new top-level category called "Time Zone Data Distribution Service (TZDIST) Parameters" and has put all the registries created herein into that category ๐Ÿ—‚๏ธ๐Ÿ’–

IANA has created a new registry called "TZDIST Service Actions", as defined below ๐ŸŒธ

10.1.1. Service Actions Registration Procedure

This registry uses the "Specification Required" policy defined in [RFC5226], which makes use of a designated expert to review potential registrations ๐Ÿง‘โ€โš–๏ธ

The IETF has created a mailing list, tzdist-service@ietf.org, which is used for public discussion of time zone data distribution service action proposals prior to registration ๐Ÿ’ฌ. The IESG has appointed a designated expert who will monitor the tzdist-service@ietf.org mailing list and review registrations ๐Ÿ‘€

A Standards Track RFC is REQUIRED for changes to actions previously documented in a Standards Track RFC ๐Ÿ“œ. Otherwise, any public specification that satisfies the requirements of [RFC5226] is acceptable ๐Ÿซถ

The registration procedure begins when a completed registration template, as defined below, is sent to tzdist-service@ietf.org and iana@iana.org ๐Ÿ“จ. The designated expert is expected to tell IANA and the submitter whether the registration is approved, approved with minor changes, or rejected with cause, within two weeks โณ. When a registration is rejected with cause, it can be resubmitted if the concerns listed in the cause are addressed ๐Ÿ”. Decisions made by the designated expert can be appealed as per Section 7 of [RFC5226] โš–๏ธ

The designated expert MUST take the following requirements into account when reviewing the registration ๐Ÿ”:

  1. A valid registration template MUST be provided by the submitter, with a clear description of what the action does ๐Ÿ“.
  2. A proposed new action name MUST NOT conflict with any existing registered action name. A conflict includes a name that duplicates an existing one, or that appears to be very similar to an existing one and could be a potential source of confusion ๐Ÿ™…โ€โ™€๏ธ.
  3. A proposed new action MUST NOT exactly duplicate the functionality of any existing actions. In cases where the new action functionality is very close to an existing action, the designated expert SHOULD clarify whether the submitter is aware of the existing action and has an adequate reason for creating a new action with slight differences from an existing one ๐Ÿง.
  4. If a proposed action is an extension to an existing action, the changes MUST NOT conflict with the intent of the existing action, or in a way that could cause interoperability problems for existing deployments of the protocol ๐Ÿค๐Ÿ’ฅ.

The IANA registry contains the name of the action ("Action Name") and a reference to the section of the specification where the action registration template is defined ("Reference") ๐Ÿ“š๐ŸŒท.

10.1.2. Registration Template for Actions

An action is defined by completing the following template ๐Ÿงฉ.

Name: The name of the action.

Request-URI Template: The URI template used in HTTP requests for the action.

Description: A general description of the action, its purpose, etc.

Parameters: A list of allowed request URI query parameters, indicating whether they are "REQUIRED" or "OPTIONAL" and whether they can occur only once or multiple times, together with the expected format of the parameter values ๐ŸŽ€.

Response: The nature of the response to the HTTP request, e.g., what format the response data is in ๐Ÿ“ฆ.

Possible Error Codes: Possible error codes reported in a JSON "problem details" object if an HTTP request fails ๐Ÿšจ.

10.1.3. Actions Registry ๐Ÿ“‹โœจ

okay so this is the actions registry, and it's where the starting lineup lives ๐Ÿ’… here's the initial content, bestie:

                +---------------+------------------------+
                | Action Name   | Reference              |
                +---------------+------------------------+
                | capabilities  | RFC 7808, Section 5.1  |
                | list          | RFC 7808, Section 5.2  |
                | get           | RFC 7808, Section 5.3  |
                | expand        | RFC 7808, Section 5.4  |
                | find          | RFC 7808, Section 5.5  |
                | leapseconds   | RFC 7808, Section 5.6  |
                +---------------+------------------------+

10.2. timezone Well-Known URI Registration ๐ŸŒธ

IANA added this entry to the "Well-Known URIs" [RFC5785] registry, and it's giving very official ๐Ÿซถ

  • URI suffix: timezone
  • Change controller: IESG.
  • Specification document(s): RFC 7808
  • Related information: None.

10.3. Service Name Registrations ๐ŸŽ€

IANA added two new service names to the "Service Name and Transport Protocol Port Number Registry" [RFC6335], as defined below. two whole new names, periodt ๐Ÿ’–

10.3.1. timezone Service Name Registration ๐Ÿฆ‹

  • Service Name: timezone
  • Transport Protocol(s): TCP
  • Assignee: IESG <iesg@ietf.org>
  • Contact: IETF Chair <chair@ietf.org>
  • Description: Time Zone Data Distribution Service - non-TLS
  • Reference: RFC 7808
  • Assignment Note: This is an extension of the http service. Defined TXT keys: path=<context path> (as per Section 6 of [RFC6763]).

10.3.2. timezones Service Name Registration ๐ŸŒˆ

  • Service Name: timezones
  • Transport Protocol(s): TCP
  • Assignee: IESG <iesg@ietf.org>
  • Contact: IETF Chair <chair@ietf.org>
  • Description: Time Zone Data Distribution Service - over TLS
  • Reference: RFC 7808
  • Assignment Note: This is an extension of the https service. Defined TXT keys: path=<context path> (as per Section 6 of [RFC6763]).

10.4. TZDIST Identifiers Registry ๐Ÿ”ฎ

okay so IANA registered a brand new URN sub-namespace inside the IETF URN Sub-namespace for Registered Protocol Parameter Identifiers, which is defined in [RFC3553]. it's a whole new neighborhood ๐Ÿ˜๏ธโœจ

Registrations in this registry follow the "IETF Review" [RFC5226] policy. so it's not a free-for-all, it's a vibe check with actual reviewers ๐Ÿ’…

  • Registry name: TZDIST Identifiers
  • URN prefix: urn:ietf:params:tzdist
  • Specification: RFC 7808
  • Repository: http://www.iana.org/assignments/tzdist-identifiers
  • Index value: Values in this registry are URNs or URN prefixes that start with the prefix urn:ietf:params:tzdist:. Each is registered independently. The prefix urn:ietf:params:tzdist:error: is used to represent specific error codes within the protocol as defined in the list of actions in Section 5 and used in problem reports (Section 4.1.7).

Each registration in the "TZDIST Identifiers" registry requires the following information ๐Ÿ“

  • URN: The complete URN that is used or the prefix for that URN.
  • Description: A summary description for the URN or URN prefix.
  • Specification: A reference to a specification describing the URN or URN prefix.
  • Contact: Email for the person or groups making the registration.
  • Index Value: As described in [RFC3553], URN prefixes that are registered include a description of how the URN is constructed. This is not applicable for specific URNs.

The "TZDIST Identifiers" registry has the initial registrations included in the following sections. here they come ๐Ÿงš

10.4.1. Registration of invalid-action Error URN ๐Ÿšซ

The following URN has been registered in the "tzdist Identifiers" registry.

10.4.2. Registration of invalid-changedsince Error URN ๐Ÿ•ฐ๏ธ

The following URN has been registered in the "tzdist Identifiers" registry.

10.4.3. Registration of tzid-not-found Error URN ๐Ÿ”

The following URN has been registered in the "tzdist Identifiers" registry.

10.4.4. Registration of invalid-format Error URN ๐Ÿงพ

The following URN has been registered in the "tzdist Identifiers" registry.

10.4.5. Registration of invalid-start Error URN โฏ๏ธ

The following URN has been registered in the "tzdist Identifiers" registry.

10.4.6. Registration of invalid-end Error URN โน๏ธ

The following URN has been registered in the "tzdist Identifiers" registry.

10.4.7. Registration of invalid-pattern Error URN ๐Ÿ”

The following URN has been registered in the "tzdist Identifiers" registry.

10.5. iCalendar Property Registrations ๐Ÿ“…

This document defines the following new iCalendar properties, which have been added to the "Properties" registry under "iCalendar Element Registries" [RFC5545]. two brand new properties, bestie ๐Ÿฅน

       +----------------+----------+------------------------+
       | Property       | Status   | Reference              |
       +----------------+----------+------------------------+
       | TZUNTIL        | Current  | RFC 7808, Section 7.1  |
       | TZID-ALIAS-OF  | Current  | RFC 7808, Section 7.2  |
       +----------------+----------+------------------------+

11. References ๐Ÿ“šโœจ

11.1. Normative References

Okay bestie, these are the docs the spec needs you to have read, no cap ๐Ÿ“šโœจ

[RFC2046]  Freed, N. and N. Borenstein, "Multipurpose Internet Mail
           Extensions (MIME) Part Two: Media Types", RFC 2046,
           DOI 10.17487/RFC2046, November 1996,
           <http://www.rfc-editor.org/info/rfc2046>.

[RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
           Requirement Levels", BCP 14, RFC 2119,
           DOI 10.17487/RFC2119, March 1997,
           <http://www.rfc-editor.org/info/rfc2119>.

[RFC2782]  Gulbrandsen, A., Vixie, P., and L. Esibov, "A DNS RR for
           specifying the location of services (DNS SRV)", RFC 2782,
           DOI 10.17487/RFC2782, February 2000,
           <http://www.rfc-editor.org/info/rfc2782>.

[RFC2818]  Rescorla, E., "HTTP Over TLS", RFC 2818,
           DOI 10.17487/RFC2818, May 2000,
           <http://www.rfc-editor.org/info/rfc2818>.

[RFC3339]  Klyne, G. and C. Newman, "Date and Time on the Internet:
           Timestamps", RFC 3339, DOI 10.17487/RFC3339, July 2002,
           <http://www.rfc-editor.org/info/rfc3339>.

[RFC3553]  Mealling, M., Masinter, L., Hardie, T., and G. Klyne, "An
           IETF URN Sub-namespace for Registered Protocol
           Parameters", BCP 73, RFC 3553, DOI 10.17487/RFC3553, June
           2003, <http://www.rfc-editor.org/info/rfc3553>.

[RFC3629]  Yergeau, F., "UTF-8, a transformation format of ISO
           10646", STD 63, RFC 3629, DOI 10.17487/RFC3629, November
           2003, <http://www.rfc-editor.org/info/rfc3629>.

[RFC4985]  Santesson, S., "Internet X.509 Public Key Infrastructure
           Subject Alternative Name for Expression of Service Name",
           RFC 4985, DOI 10.17487/RFC4985, August 2007,
           <http://www.rfc-editor.org/info/rfc4985>.

[RFC5226]  Narten, T. and H. Alvestrand, "Guidelines for Writing an
           IANA Considerations Section in RFCs", BCP 26, RFC 5226,
           DOI 10.17487/RFC5226, May 2008,
           <http://www.rfc-editor.org/info/rfc5226>.

[RFC5234]  Crocker, D., Ed. and P. Overell, "Augmented BNF for Syntax
           Specifications: ABNF", STD 68, RFC 5234,
           DOI 10.17487/RFC5234, January 2008,
           <http://www.rfc-editor.org/info/rfc5234>.

[RFC5246]  Dierks, T. and E. Rescorla, "The Transport Layer Security
           (TLS) Protocol Version 1.2", RFC 5246,
           DOI 10.17487/RFC5246, August 2008,
           <http://www.rfc-editor.org/info/rfc5246>.

[RFC5545]  Desruisseaux, B., Ed., "Internet Calendaring and
           Scheduling Core Object Specification (iCalendar)",
           RFC 5545, DOI 10.17487/RFC5545, September 2009,
           <http://www.rfc-editor.org/info/rfc5545>.

[RFC5785]  Nottingham, M. and E. Hammer-Lahav, "Defining Well-Known
           Uniform Resource Identifiers (URIs)", RFC 5785,
           DOI 10.17487/RFC5785, April 2010,
           <http://www.rfc-editor.org/info/rfc5785>.

[RFC6125]  Saint-Andre, P. and J. Hodges, "Representation and
           Verification of Domain-Based Application Service Identity
           within Internet Public Key Infrastructure Using X.509
           (PKIX) Certificates in the Context of Transport Layer
           Security (TLS)", RFC 6125, DOI 10.17487/RFC6125, March
           2011, <http://www.rfc-editor.org/info/rfc6125>.

[RFC6265]  Barth, A., "HTTP State Management Mechanism", RFC 6265,
           DOI 10.17487/RFC6265, April 2011,
           <http://www.rfc-editor.org/info/rfc6265>.

[RFC6321]  Daboo, C., Douglass, M., and S. Lees, "xCal: The XML
           Format for iCalendar", RFC 6321, DOI 10.17487/RFC6321,
           August 2011, <http://www.rfc-editor.org/info/rfc6321>.

[RFC6335]  Cotton, M., Eggert, L., Touch, J., Westerlund, M., and S.
           Cheshire, "Internet Assigned Numbers Authority (IANA)
           Procedures for the Management of the Service Name and
           Transport Protocol Port Number Registry", BCP 165,
           RFC 6335, DOI 10.17487/RFC6335, August 2011,
           <http://www.rfc-editor.org/info/rfc6335>.

[RFC6557]  Lear, E. and P. Eggert, "Procedures for Maintaining the
           Time Zone Database", BCP 175, RFC 6557,
           DOI 10.17487/RFC6557, February 2012,
           <http://www.rfc-editor.org/info/rfc6557>.

[RFC6570]  Gregorio, J., Fielding, R., Hadley, M., Nottingham, M.,
           and D. Orchard, "URI Template", RFC 6570,
           DOI 10.17487/RFC6570, March 2012,
           <http://www.rfc-editor.org/info/rfc6570>.

[RFC6698]  Hoffman, P. and J. Schlyter, "The DNS-Based Authentication
           of Named Entities (DANE) Transport Layer Security (TLS)
           Protocol: TLSA", RFC 6698, DOI 10.17487/RFC6698, August
           2012, <http://www.rfc-editor.org/info/rfc6698>.

[RFC6763]  Cheshire, S. and M. Krochmal, "DNS-Based Service
           Discovery", RFC 6763, DOI 10.17487/RFC6763, February 2013,
           <http://www.rfc-editor.org/info/rfc6763>.

[RFC7159]  Bray, T., Ed., "The JavaScript Object Notation (JSON) Data
           Interchange Format", RFC 7159, DOI 10.17487/RFC7159, March
           2014, <http://www.rfc-editor.org/info/rfc7159>.

[RFC7230]  Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer
           Protocol (HTTP/1.1): Message Syntax and Routing",
           RFC 7230, DOI 10.17487/RFC7230, June 2014,
           <http://www.rfc-editor.org/info/rfc7230>.

[RFC7231]  Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer
           Protocol (HTTP/1.1): Semantics and Content", RFC 7231,
           DOI 10.17487/RFC7231, June 2014,
           <http://www.rfc-editor.org/info/rfc7231>.

[RFC7232]  Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer
           Protocol (HTTP/1.1): Conditional Requests", RFC 7232,
           DOI 10.17487/RFC7232, June 2014,
           <http://www.rfc-editor.org/info/rfc7232>.

[RFC7234]  Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke,
           Ed., "Hypertext Transfer Protocol (HTTP/1.1): Caching",
           RFC 7234, DOI 10.17487/RFC7234, June 2014,
           <http://www.rfc-editor.org/info/rfc7234>.

[RFC7235]  Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer
           Protocol (HTTP/1.1): Authentication", RFC 7235,
           DOI 10.17487/RFC7235, June 2014,
           <http://www.rfc-editor.org/info/rfc7235>.

[RFC7265]  Kewisch, P., Daboo, C., and M. Douglass, "jCal: The JSON
           Format for iCalendar", RFC 7265, DOI 10.17487/RFC7265, May
           2014, <http://www.rfc-editor.org/info/rfc7265>.

[RFC7525]  Sheffer, Y., Holz, R., and P. Saint-Andre,
           "Recommendations for Secure Use of Transport Layer
           Security (TLS) and Datagram Transport Layer Security
           (DTLS)", BCP 195, RFC 7525, DOI 10.17487/RFC7525, May
           2015, <http://www.rfc-editor.org/info/rfc7525>.

[RFC7807]  Nottingham, M. and E. Wilde, "Problem Details for HTTP
           APIs", RFC 7807, DOI 10.17487/RFC7807, March 2016,
           <http://www.rfc-editor.org/info/rfc7807>.

11.2. Informative References

These ones are more like extra reading for the curious ๐Ÿงš

[RFC2131]  Droms, R., "Dynamic Host Configuration Protocol",
           RFC 2131, DOI 10.17487/RFC2131, March 1997,
           <http://www.rfc-editor.org/info/rfc2131>.

Acknowledgements ๐Ÿซถ

The authors wanna give a huge shoutout to the members of the Calendaring and Scheduling Consortium's Time Zone Technical Committee, and to the participants and chairs of the IETF tzdist working group. ๐Ÿ’– Literally so many people helped make this happen, but these ones really went above and beyond: Steve Allen, Lester Caine, Stephen Colebourne, Tobias Conradi, Steve Crocker, Paul Eggert, Daniel Kahn Gillmor, John Haug, Ciny Joy, Bryan Keller, Barry Leiba, Andrew McMillan, Ken Murchison, Tim Parenti, Arnaud Quillaud, Jose Edvaldo Saraiva, and Dave Thewlis. You all are iconic ๐Ÿ‘‘

This specification started out as work at the Calendaring and Scheduling Consortium, which also supported building and testing implementations of it. ๐ŸŒธ

Authors' Addresses ๐Ÿฆ‹

Here's where you can find the authors ๐Ÿ’…

Michael Douglass
Spherical Cow Group
226 3rd Street
Troy, NY  12180
United States

Email: mdouglass@sphericalcowgroup.com
URI:   http://sphericalcowgroup.com


Cyrus Daboo
Apple Inc.
1 Infinite Loop
Cupertino, CA  95014
United States

Email: cyrus@daboo.name
URI:   http://www.apple.com/
๐Ÿ“ฆ where this page comes from