Kacper Cz
Kacper Cz

Reputation: 586

Absolute UTC offset in Swift

My task is to create a string containing UTC offset during DST and during summer time (example: UTC+1UTC+2 or UTC+1UTC+1 if there is no DST for a region). My function looks the following:

extension TimeZone {
    public func utcOffset(for date: Date = Date()) -> String {
        var currentTimeOffest = self.secondsFromGMT(for: date)
        if isDaylightSavingTime() {
            currentTimeOffest -= Int(daylightSavingTimeOffset(for: date))
        }
        let currentInHours = Int(currentTimeOffest / 3_600)
        let hoursSymbol: String = currentInHours > 0 ? "+" : ""

        let daylightOffset = TimeInterval(currentTimeOffest) + self.daylightSavingTimeOffset(for: date)
        let daylightInHours = Int(daylightOffset / 3_600)
        let daylightSymbol: String = daylightInHours > 0 ? "+" : ""

        return "UTC\(hoursSymbol)\(currentInHours)UTC\(daylightSymbol)\(daylightInHours)"
    }
}

It works well and I've written tests for it. All is good but after recent DST changes in multiple countries the tests started failing, even though I pass a specific date to calculate the offset for:

    func testUtcOffset() {
        let date: Date = Date(timeIntervalSince1970: 1_557_482_400) //May 10, 2019 10:00:00 AM

        let warsaw = TimeZone.init(identifier: "Europe/Warsaw")! //eastern hemisphere, with DST
        XCTAssertEqual(warsaw.utcOffset(for: date), "UTC+2UTC+3")

        let shanghai = TimeZone.init(identifier: "Asia/Shanghai")! //eastern hemisphere, without DST
        XCTAssertEqual(shanghai.utcOffset(for: date), "UTC+8UTC+8")

        let barbados = TimeZone.init(identifier: "America/Barbados")! //western hemisphere, without DST
        XCTAssertEqual(barbados.utcOffset(for: date), "UTC-4UTC-4")

        let bermuda = TimeZone.init(identifier: "Atlantic/Bermuda")! //western hemisphere, with DST
        XCTAssertEqual(bermuda.utcOffset(for: date), "UTC-4UTC-3")

        let gmt = TimeZone.init(identifier: "GMT")! //GMT, without DST
        XCTAssertEqual(gmt.utcOffset(for: date), "UTC0UTC0")

        let lisbon = TimeZone.init(identifier: "Europe/Lisbon")! //GMT, with DST
        XCTAssertEqual(lisbon.utcOffset(for: date), "UTC+1UTC+2")
    }

2 weeks ago, the warsaw and lisbon timezones started failing, today bermuda. Any ideas what might be wrong?

Upvotes: 1

Views: 1450

Answers (1)

Matt Johnson-Pint
Matt Johnson-Pint

Reputation: 241890

A few things:

  • In your tests, you have the offsets for Warsaw and Lisbon an hour off. Warsaw is UTC+1 during standard time, and UTC+2 during daylight saving time. Lisbon is UTC+0 during standard time, and UTC+1 during daylight time.

  • From your comment, it seems you're looking for the standard offset and the daylight offset. However, the standard offset isn't necessarily the same as the current offset. The current offset might include daylight saving time, or not.

  • According to these docs, the secondsFromGMT function returns the difference including the daylight adjustment if one is in effect. Thus you should not be adjusting for that yourself.

  • It doesn't seem to make sense to be asking the daylightSavingTimeOffset function for the offset on a date when daylight saving time doesn't apply. You might get better results just using secondsFromGMT for two different dates in the current year. A common approach is to get the offsets for January 1st and July 1st. Whichever is smaller is the standard time, the other is the daylight time. Keep in mind they may be the same if DST is not used, and they will be inverted between northern and southern hemisphere time zones.

    • Even with the above approach, this sort of algorithm ignores a lot of the complexities of time zones. Consider that some time zones have changed their standard time at different points in their history. Such an algorithm might mistake that as a daylight saving time change.
  • A point of concern: Once you have your string generated, such as "UTC+1UTC+2", how will the external API you know from that alone which set of daylight saving time rules to apply? Since daylight saving time starts and stops at different dates and times in different parts of the world, it's likely that the wrong dates could be used when interpreting the offsets.

Upvotes: 2

Related Questions