Search code examples
swiftuikitfoundation

"CEST" timezone changes offset to GMT during transition to/from Daylight Saving Time (it shouldn't)


UIKit provides TimeZone.secondsFromGMT() method which simply returns the number of seconds from the GMT time.

The function returns incorrect value as of 27th of Oct, when Europe moved off of the summer time (CEST-> CET). It currently returns 3600, and prior to the 27th of Oct, it returned correctly 7200.

This function should always return constant value of 7200 irrelevant of current timezone (Daylight savings change timezones, not timezone itself). Even if Europe went to the CET time during the winter, CEST time didn’t change (Europe just doesn’t “use” it now).

This behavior breaks my unit tests and therefore correctness of the app.

Effectively this should always pass:

let timeZone = TimeZone(abbreviation: "CEST")! XCTAssertTrue(timeZone.secondsFromGMT() == 7200)

Am I thinking about this incorrectly?


Solution

  • Your misconception is here:

    (Daylight savings change timezones, not timezone itself). Even if Europe went to the CET time during the winter, CEST time didn’t change (Europe just doesn’t “use” it now).

    What you are talking about, are offsets. TimeZone objects are capable of representing offsets, namely by creating one using the init(secondsFromGMT:) initialiser. In most cases though, thinking in terms of timezones, such as Europe/Paris is more useful.

    init(abbreviation:) simply looks up the string you passed in the TimeZone.abbreviationDictionary, and uses the looked-up value as the timezone identifier. With a simple inspection, you will see that in the dictionary, CEST corresponds to the value Europe/Paris.

    The identifier Europe/Paris represents the timezone adopted by France. The TimeZone object created with this identifier encapsulates, among other things, all the historical offset transitions, and the transition rules for future transitions (e.g. "DST ends on the first Sunday on or after 25 Oct").

    In other words, a TimeZone object is not just a number of seconds from GMT. It is more like a mathematical function, mapping points in time to local date times. This function would mostly be continuous, except in those points in time where an offset transition happens.

    If you just want a constant offset of 7200 seconds, do:

    TimeZone(secondsFromGMT: 7200)