如何找到一个时区的所有历史偏移转换日期?
How can I find all the historical offset transition dates for a timezone?
我正在尝试编写一个显示有关给定时区信息的应用程序。它显示该时区的哪些时间段正在观察 DST,哪些时间段不是。最重要的是,它突出显示了时区 不规则变化 的时间,例如英国在二战期间观察 双 夏令时,或者萨摩亚在 2011 年跳过了一天.
为此,我需要获取存储在 TZ 数据库中的所有历史时区偏移转换的列表(我认为每个 macOS/iOS 设备中都有数据库的副本)。更具体地说,“过渡”(类似于 java.time.zone.ZoneOffsetTransition
)由以下 3 件事建模:
Date
发生的时间
- 发生之前的 GMT 偏移秒数
- 发生后的 GMT 偏移秒数
据我从 TimeZone
API docs, there is no built-in method that does this (unlike how java.time
does). The closest method I could find is nextDaylightSavingTimeTransition(after:)
中看到的,但这只告诉我一个转换的转换日期,当给定一个日期时,我也不确定要给出什么日期。
如何获取转换列表?
Asia/Ho_Chi_Minh
的示例输出:
Transition at 1906-06-30T16:53:20Z from 25600 to 25590
Transition at 1911-04-30T16:53:30Z from 25590 to 25200
Transition at 1942-12-31T16:00:00Z from 25200 to 28800
Transition at 1945-03-14T15:00:00Z from 28800 to 32400
Transition at 1945-09-01T15:00:00Z from 32400 to 25200
Transition at 1947-03-31T17:00:00Z from 25200 to 28800
Transition at 1955-06-30T16:00:00Z from 28800 to 25200
Transition at 1959-12-31T16:00:00Z from 25200 to 28800
Transition at 1975-06-12T16:00:00Z from 28800 to 25200
我想出的一个解决方案是,首先将 distantPast
传递给 nextDaylightSavingTimeTransition
:
someTimeZone.nextDaylightSavingTimeTransition(after: .distantPast)
这为您提供了第一个过渡日期。然后做:
// second transition date
someTimeZone.nextDaylightSavingTimeTransition(after:
someTimeZone.nextDaylightSavingTimeTransition(after: .distantPast)
)
// third transition date
someTimeZone.nextDaylightSavingTimeTransition(after:
someTimeZone.nextDaylightSavingTimeTransition(after:
someTimeZone.nextDaylightSavingTimeTransition(after: .distantPast)
)
)
等等。当然,我们会把它放在一个循环中。通过secondsFromGMT(for:)
可以找到过渡前后的偏移量。我们将传递两个仅相差 1 秒的日期。
另请注意,虽然它被命名为 nextDaylightSavingTimeTransition
,但它也为您提供非 DST 转换,这正是您想要的,太棒了!
import Foundation
struct OffsetTransition {
let instant: Date
let offsetBefore: Int
let offsetAfter: Int
}
var date = Date.distantPast
let timeZone = TimeZone(identifier: "Some Time Zone ID")!
let dateFormatter = ISO8601DateFormatter()
dateFormatter.formatOptions = [.withInternetDateTime]
var transitions = [OffsetTransition]()
while date < Date() {
guard let instant = timeZone.nextDaylightSavingTimeTransition(after: date) else {
break
}
let offsetBefore = timeZone.secondsFromGMT(for: instant.addingTimeInterval(-1))
let offsetAfter = timeZone.secondsFromGMT(for: instant)
if offsetBefore == offsetAfter { continue }
transitions.append(.init(instant: instant, offsetBefore: offsetBefore, offsetAfter: offsetAfter))
print("Transition at \(dateFormatter.string(from: instant)) from \(offsetBefore) to \(offsetAfter)")
date = instant
}
注意 if offsetBefore == offsetAfter { continue }
检查。我添加了这个检查,因为没有它,它有时会产生 offsetBefore == offsetAfter
的转换。这可能表明我做错了什么,但这项检查似乎解决了问题...
此代码的输出几乎与使用 Java 的 getTransitions
的类似代码的输出相匹配。但是,我发现这个解决方案还有一个小问题。对于某些时区,例如 Europe/London
,从 local mean time 到标准化偏移量的过渡缺失,但对于其他时区(例如 Asia/Hong_Kong
),它存在。例如,在 Java 中使用 getTransitions
的类似代码将为 Europe/London
生成“在 1847-12-01T00:01:15Z 从 -75 到 0 的转换”,但是 Swift 代码的输出不包括这一行。不过这对我来说不是什么大问题。
我正在尝试编写一个显示有关给定时区信息的应用程序。它显示该时区的哪些时间段正在观察 DST,哪些时间段不是。最重要的是,它突出显示了时区 不规则变化 的时间,例如英国在二战期间观察 双 夏令时,或者萨摩亚在 2011 年跳过了一天.
为此,我需要获取存储在 TZ 数据库中的所有历史时区偏移转换的列表(我认为每个 macOS/iOS 设备中都有数据库的副本)。更具体地说,“过渡”(类似于 java.time.zone.ZoneOffsetTransition
)由以下 3 件事建模:
Date
发生的时间- 发生之前的 GMT 偏移秒数
- 发生后的 GMT 偏移秒数
据我从 TimeZone
API docs, there is no built-in method that does this (unlike how java.time
does). The closest method I could find is nextDaylightSavingTimeTransition(after:)
中看到的,但这只告诉我一个转换的转换日期,当给定一个日期时,我也不确定要给出什么日期。
如何获取转换列表?
Asia/Ho_Chi_Minh
的示例输出:
Transition at 1906-06-30T16:53:20Z from 25600 to 25590
Transition at 1911-04-30T16:53:30Z from 25590 to 25200
Transition at 1942-12-31T16:00:00Z from 25200 to 28800
Transition at 1945-03-14T15:00:00Z from 28800 to 32400
Transition at 1945-09-01T15:00:00Z from 32400 to 25200
Transition at 1947-03-31T17:00:00Z from 25200 to 28800
Transition at 1955-06-30T16:00:00Z from 28800 to 25200
Transition at 1959-12-31T16:00:00Z from 25200 to 28800
Transition at 1975-06-12T16:00:00Z from 28800 to 25200
我想出的一个解决方案是,首先将 distantPast
传递给 nextDaylightSavingTimeTransition
:
someTimeZone.nextDaylightSavingTimeTransition(after: .distantPast)
这为您提供了第一个过渡日期。然后做:
// second transition date
someTimeZone.nextDaylightSavingTimeTransition(after:
someTimeZone.nextDaylightSavingTimeTransition(after: .distantPast)
)
// third transition date
someTimeZone.nextDaylightSavingTimeTransition(after:
someTimeZone.nextDaylightSavingTimeTransition(after:
someTimeZone.nextDaylightSavingTimeTransition(after: .distantPast)
)
)
等等。当然,我们会把它放在一个循环中。通过secondsFromGMT(for:)
可以找到过渡前后的偏移量。我们将传递两个仅相差 1 秒的日期。
另请注意,虽然它被命名为 nextDaylightSavingTimeTransition
,但它也为您提供非 DST 转换,这正是您想要的,太棒了!
import Foundation
struct OffsetTransition {
let instant: Date
let offsetBefore: Int
let offsetAfter: Int
}
var date = Date.distantPast
let timeZone = TimeZone(identifier: "Some Time Zone ID")!
let dateFormatter = ISO8601DateFormatter()
dateFormatter.formatOptions = [.withInternetDateTime]
var transitions = [OffsetTransition]()
while date < Date() {
guard let instant = timeZone.nextDaylightSavingTimeTransition(after: date) else {
break
}
let offsetBefore = timeZone.secondsFromGMT(for: instant.addingTimeInterval(-1))
let offsetAfter = timeZone.secondsFromGMT(for: instant)
if offsetBefore == offsetAfter { continue }
transitions.append(.init(instant: instant, offsetBefore: offsetBefore, offsetAfter: offsetAfter))
print("Transition at \(dateFormatter.string(from: instant)) from \(offsetBefore) to \(offsetAfter)")
date = instant
}
注意 if offsetBefore == offsetAfter { continue }
检查。我添加了这个检查,因为没有它,它有时会产生 offsetBefore == offsetAfter
的转换。这可能表明我做错了什么,但这项检查似乎解决了问题...
此代码的输出几乎与使用 Java 的 getTransitions
的类似代码的输出相匹配。但是,我发现这个解决方案还有一个小问题。对于某些时区,例如 Europe/London
,从 local mean time 到标准化偏移量的过渡缺失,但对于其他时区(例如 Asia/Hong_Kong
),它存在。例如,在 Java 中使用 getTransitions
的类似代码将为 Europe/London
生成“在 1847-12-01T00:01:15Z 从 -75 到 0 的转换”,但是 Swift 代码的输出不包括这一行。不过这对我来说不是什么大问题。