iOS 11.2.6 DateFormatter.date returns 遵守巴西利亚夏令时的城市无
iOS 11.2.6 DateFormatter.date returns nil for cities that observe Brasília Summer Time
我从头开始创建了一个新的 Single View App 项目,并仅将以下代码添加到 ViewController 的 viewDidLoad
方法中:
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/yy"
if let date = dateFormatter.date(from: "11/20") {
print("got the date: \(date)")
} else {
print("failed getting the date")
}
以上代码在世界上任何地方都有效除了观察Brasília Summer Time (BRST)的城市。
我通过在 设置 > 通用 > 日期和时间 中更改设备上的时区,测试了列出的所有 38 个时区的城市 here。我还确认 BRST 城市在 iOS 11.0 设备上工作正常,但 不 iOS 11.2.6.
另请注意,即使在 iOS 11.2.6 上,我尝试过的几乎所有其他 month/year 组合都可以正常工作。 只有“11/20”和“11/26”似乎失败了。
为什么对于在 iOS 11.2.6 观察 BRST 的城市,此代码返回 nil?
它对那个时区(圣保罗)不起作用的原因是出于该时区特定的考虑,即夏令时。巴西的夏令时从 first Sunday in November 开始,恰好是 2020 年的 11 月 1 日。
默认的时间不存在,因此return为零。
您可以通过更改格式化程序以包含时间并准确查看日期格式化程序何时开始来使用它 returning nil。
曾经运行陷入过类似的困惑;日期和时区很棘手。
如果您使用日期格式化程序向用户显示日期,您通常不应覆盖他们的设备设置,并且您可能希望避免使用固定日期格式,并且应该意识到日期的可能性格式化程序到 return 一个 nil 值(Swift 选项来拯救)。但是如果你将它用于内部日期,比如你的 API,你应该考虑在你的日期格式化程序上设置一个明确的 locale/time 区域,这样这些事情就不会发生,for example:
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
shim 的回答是正确的,但我想提供一些更明确的细节。
2017 年 12 月 15 日,巴西总统米歇尔·特梅尔签署 decree 将夏令时 (DST) 的开始日期改为 11 月的第一个星期日,从 2018 年开始。看起来像 iOS 11.2.6 是第一个采用此更改的 iOS 版本,这就是为什么在以前的 iOS 版本中没有出现此问题的原因。
11 月的 第一个 星期天的时间更改的问题是 11 月的第一个星期日最终可能是(并且 是 在 2020 年和 2026 年的情况下)11 月 1 日。为什么这会出现问题?好吧,当 DST 开始时,巴西 也 有他们的时钟 "spring forward" at midnight(从 11:59pm 到 1:00am)。
为什么 11 月 1 日午夜这个日期会出现问题?好吧,当我们要求 iOS 仅根据用户输入的 month/year 给我们一个日期对象(例如 11/20)时,日期对象默认为该月第一天的午夜。这是有问题的,因为在巴西夏令时从 11 月 1 日午夜开始的年份,技术上该时间不存在。因此,当我们要求 iOS 为我们提供 11/20 的日期对象时,iOS 尝试 return 11/01/2020 @ 00:00,但它是 returning nil 因为那个时候不存在。
因此,从技术上讲,这个问题可能会发生在 任何 时区,该时区遵守可以从每月 1 日午夜开始的夏令时。在浏览了一些国家并查看了他们的 DST 规则后,我只发现巴西允许 DST 在每月的 1 号午夜开始。
为了解决此问题,我有效地执行了以下操作:
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/yy"
let dateFormatterWithTime = DateFormatter()
dateFormatterWithTime.dateFormat = "MM/yy HH:mm"
if let date = dateFormatter.date(from: "11/20") ??
dateFormatterWithTime.date(from: "11/20 03:00") {
print("got the date: \(date)")
} else {
print("failed getting the date")
}
03:00
是未来的任意时间量,应该安全地考虑从午夜开始的夏令时变化。
另一种使其工作的方法是将 DateFormatter
时区设置为 UTC。所以你的代码看起来像:
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/yy"
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
if let date = dateFormatter.date(from: "11/20") {
print("got the date: \(date)")
} else {
print("failed getting the date")
}
通过这样做,代码将打印以下内容:
got the date: 2020-11-01 00:00:00 +0000
我从头开始创建了一个新的 Single View App 项目,并仅将以下代码添加到 ViewController 的 viewDidLoad
方法中:
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/yy"
if let date = dateFormatter.date(from: "11/20") {
print("got the date: \(date)")
} else {
print("failed getting the date")
}
以上代码在世界上任何地方都有效除了观察Brasília Summer Time (BRST)的城市。
我通过在 设置 > 通用 > 日期和时间 中更改设备上的时区,测试了列出的所有 38 个时区的城市 here。我还确认 BRST 城市在 iOS 11.0 设备上工作正常,但 不 iOS 11.2.6.
另请注意,即使在 iOS 11.2.6 上,我尝试过的几乎所有其他 month/year 组合都可以正常工作。 只有“11/20”和“11/26”似乎失败了。
为什么对于在 iOS 11.2.6 观察 BRST 的城市,此代码返回 nil?
它对那个时区(圣保罗)不起作用的原因是出于该时区特定的考虑,即夏令时。巴西的夏令时从 first Sunday in November 开始,恰好是 2020 年的 11 月 1 日。
默认的时间不存在,因此return为零。
您可以通过更改格式化程序以包含时间并准确查看日期格式化程序何时开始来使用它 returning nil。
曾经运行陷入过类似的困惑;日期和时区很棘手。
如果您使用日期格式化程序向用户显示日期,您通常不应覆盖他们的设备设置,并且您可能希望避免使用固定日期格式,并且应该意识到日期的可能性格式化程序到 return 一个 nil 值(Swift 选项来拯救)。但是如果你将它用于内部日期,比如你的 API,你应该考虑在你的日期格式化程序上设置一个明确的 locale/time 区域,这样这些事情就不会发生,for example:
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
shim 的回答是正确的,但我想提供一些更明确的细节。
2017 年 12 月 15 日,巴西总统米歇尔·特梅尔签署 decree 将夏令时 (DST) 的开始日期改为 11 月的第一个星期日,从 2018 年开始。看起来像 iOS 11.2.6 是第一个采用此更改的 iOS 版本,这就是为什么在以前的 iOS 版本中没有出现此问题的原因。
11 月的 第一个 星期天的时间更改的问题是 11 月的第一个星期日最终可能是(并且 是 在 2020 年和 2026 年的情况下)11 月 1 日。为什么这会出现问题?好吧,当 DST 开始时,巴西 也 有他们的时钟 "spring forward" at midnight(从 11:59pm 到 1:00am)。
为什么 11 月 1 日午夜这个日期会出现问题?好吧,当我们要求 iOS 仅根据用户输入的 month/year 给我们一个日期对象(例如 11/20)时,日期对象默认为该月第一天的午夜。这是有问题的,因为在巴西夏令时从 11 月 1 日午夜开始的年份,技术上该时间不存在。因此,当我们要求 iOS 为我们提供 11/20 的日期对象时,iOS 尝试 return 11/01/2020 @ 00:00,但它是 returning nil 因为那个时候不存在。
因此,从技术上讲,这个问题可能会发生在 任何 时区,该时区遵守可以从每月 1 日午夜开始的夏令时。在浏览了一些国家并查看了他们的 DST 规则后,我只发现巴西允许 DST 在每月的 1 号午夜开始。
为了解决此问题,我有效地执行了以下操作:
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/yy"
let dateFormatterWithTime = DateFormatter()
dateFormatterWithTime.dateFormat = "MM/yy HH:mm"
if let date = dateFormatter.date(from: "11/20") ??
dateFormatterWithTime.date(from: "11/20 03:00") {
print("got the date: \(date)")
} else {
print("failed getting the date")
}
03:00
是未来的任意时间量,应该安全地考虑从午夜开始的夏令时变化。
另一种使其工作的方法是将 DateFormatter
时区设置为 UTC。所以你的代码看起来像:
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/yy"
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
if let date = dateFormatter.date(from: "11/20") {
print("got the date: \(date)")
} else {
print("failed getting the date")
}
通过这样做,代码将打印以下内容:
got the date: 2020-11-01 00:00:00 +0000