在我们的应用程序中,存在从字符串创建日期的问题,但只能通过非常特定的组合来重现。不幸的是,无法从遇到该问题的用户那里获取该信息,因此我决定尝试所有可能的组合:
import Foundation
var dateOnlyDateFormatter: (String, String) -> DateFormatter = { timeZoneS, localeS in
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
formatter.timeZone = TimeZone(identifier: timeZoneS)
formatter.locale = Locale(identifier: localeS)
return formatter
}
let date = "2022-05-27"
let time = "06:15"
for timeZone in TimeZone.knownTimeZoneIdentifiers {
for locale in Locale.availableIdentifiers {
let dateFormatter = dateOnlyDateFormatter(timeZone, locale)
let printDate = dateFormatter.date(from: date)
if printDate == nil {
print("TimeZone: \(timeZone), Locale: \(locale)")
}
}
}
结果:
TimeZone: America/Asuncion, Locale: ar_SA
TimeZone: America/Asuncion, Locale: en_SA
我不太确定处理此问题的最佳方法是什么。显然,我们的 BE 可以使用特定的区域设置返回日期,例如 en_US_POSIX
,但我对此几乎没有控制权,因为我是一个更大的旧系统的一部分。有人遇到过这样的问题吗?
最佳答案
如果您阅读 DateFormatter
文档的“Working With Fixed Format Date Representations ”部分,您会发现:
For most fixed formats, you should also set the locale property to a POSIX locale ("en_US_POSIX"), and set the timeZone property to UTC.
您可能应该遵循此处的建议...但这可能就是为什么 SA 和巴拉圭时区产生 nil 的原因。
在该部分的更下方,有一个指向 technical Q&A 的链接。其中对此进行了更详细的解释。与您的问题最相关的部分是:
A user can change their calendar (using System Preferences > Language & Region > Calendar on OS X, or Settings > General > International > Calendar on iOS). In that case NSDateFormatter will treat the numbers in the string you parse as if they were in the user's chosen calendar. For example, if the user selects the Buddhist calendar, parsing the year "2010" will yield an NSDate in 1467, because the year 2010 on the Buddhist calendar was the year 1467 on the (Gregorian) calendar that we use day-to-day.
在区域设置 SA 中,日期字符串的数字似乎使用 Islamic Calendar 进行解释。 。查看使用 en_SA 和 America/New_York 格式化的今天的日期。
let dateFormatter = dateOnlyDateFormatter("America/New_York", "en_SA")
let printDate = dateFormatter.string(from: .init())
print(printDate)
// 1443-10-26
另请查看 en_SA 和 America/New_York 解析的非零日期
let dateFormatter = dateOnlyDateFormatter("America/New_York", "en_SA")
let printDate = dateFormatter.date(from: date)
print(printDate)
// 2583-10-05 04:00:00 +0000
请注意,10-05 是 2583 年的第一个星期日(请参阅 this calendar )。如果巴拉圭仍然使用the same DST rules就像现在在 2583 年所做的那样,那么这意味着在 2583-10-05 00:00:00 存在 DST 间隙过渡,从 DST 周期开始。从 00:00:00 开始的小时将被跳过,因此 00:00:00 将不存在。
仅解析日期时,DateFormatter
会尝试将时间分量设置为格式化程序时区中的 00:00:00,但 00:00:00 不存在,因此解析失败。
无论如何,设置dateFormat
后,只需将locale
设置为posix,将timeZone
设置为UTC即可。
关于ios - DateFormatter 返回 nil 以及 TimeZone 和 Locale 的特定组合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72406774/