NSDateFormatter 仍在解析而不是格式不正确
Posted
技术标签:
【中文标题】NSDateFormatter 仍在解析而不是格式不正确【英文标题】:NSDateFormatter still parsing instead having incorrect format 【发布时间】:2014-11-10 09:29:35 【问题描述】:在解析日期时遇到一些问题。我有一系列支持的格式,一旦我从 API 接收到日期(字符串),我会尝试通过格式迭代解析它,直到我得到一个有效的 NSDate 对象。
来自 Xcode Playground 的 sn-p --
let dateString = "02/06/1987" // --> want to parse into this Feb 6, not Jun 2
let dateFormatIncorrect = "dd.MM.yyyy"
let dateFormatCorrect = "MM/dd/yyyy"
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = dateFormatIncorrect
let date = dateFormatter.dateFromString(dateString)! // "Jun 2, 1987, 12:00 AM"
dateFormatter.dateFormat = dateFormatCorrect
let date2 = dateFormatter.dateFromString(dateString)! // "Feb 6, 1987, 12:00 AM"
为什么即使给定字符串的格式明显不正确,它也会解析日期?在文档中找不到关于忽略分隔符的日期格式化程序的任何内容。
我意识到正确的解决方案是从 API 返回固定格式,但想知道这里发生了什么?
谢谢。
【问题讨论】:
@TheParamagneticCroissant 它没有翻转日/月。这两个格式化程序以不同的顺序指定它们。 dMy 和 Mdy。 MM 代表月份,dd 代表月份中的某天。所以 dd.MM.yyyy 是正确的格式 @Fogmeister 哦,是的,没错。 月份和日期的位置是故意不同的。我很好奇为什么即使分隔符不匹配(/ 和 .) 这很有趣。即使使用dateFormatIncorrect = "'aaa'dd'bbb'MM'ccc'yyyy'ddd'"
,日期格式化程序也会对其进行转换。
【参考方案1】:
似乎NSDateFormatter
在解析 日期字符串时非常宽松。
不幸的是,我找不到这方面的参考,但即使有
dateFormatIncorrect = "'aaa'dd'bbb'MM'ccc'yyyy'ddd'"
成功解析日期字符串“02/06/1987”。有一个lenient
属性,
但默认情况下是false
,显式设置没有区别。
作为一种解决方法,您可以将解析后的日期转换回字符串,并且仅当 结果等于原始字符串,日期被接受:
extension NSDateFormatter
func checkedDateFromString(string : String) -> NSDate?
if let date = self.dateFromString(string)
if self.stringFromDate(date) == string
return date
return nil
使用这个自定义扩展,
dateFormatter.checkedDateFromString(dateString)
为不正确的日期格式返回 nil
。
通常,如果您使用固定日期格式,您还应该设置区域设置 到“en_US_POSIX”
dateFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
(请参阅What is the best way to deal with the NSDateFormatter locale "feechur"?)。但是,这对此没有任何影响 特殊问题。
Swift 3 更新:
extension DateFormatter
func checkedDate(from: String) -> Date?
if let date = date(from: from), string(from: date) == from
return date
return nil
【讨论】:
谢谢,虽然不理想,但在更新 API 以解决问题之前,这将起到作用。 @sharoni:不客气。我试图找到任何解释这种行为的文档,但没有成功。您可以考虑向 Apple 提交错误报告。 啊,我想可能是这样。 +1 用于实际测试:D 我可能会使用正则表达式来验证格式。【参考方案2】:这可能与 NSDateFormatter 在使用固定格式时无论如何都会尊重用户设置的事实有关
虽然原则上格式字符串指定了固定格式,但通过 默认的 NSDateFormatter 仍然采用用户的偏好(包括 区域设置)考虑
因此,您的首选项中定义的语言环境可能使用“/”作为分隔符并满足“不正确的格式”。即使情况并非如此,苹果在几个地方指出 NSDateFormatter 可能不会始终如一地行动。所以尝试如下设置一个固定的语言环境,看看是否有帮助
NSLocale *locale = [[NSLocale alloc]
initWithLocaleIdentifier:@"en_US_POSIX"];
[dateFormatter setLocale:locale];
有关详细信息,请参阅以下链接:apple tech note。注意与分隔符直接相关,但可能是相关的。
【讨论】:
你试过了吗?这也是我的第一个想法,但是将语言环境设置为“en_US_POSIX”并没有解决我的测试用例中的问题。 这是我尝试的第一件事,但不幸的是没有运气。【参考方案3】:有类似的问题:
NSDateFormatter returns date object from invalid input
向 Apple 提交了错误报告。 结果:不会被修复,因为更改可能会破坏工作代码,此外它更容错,因此提供了某种便利。
请注意,我们的工程团队已确定此问题 根据所提供的信息按预期行事。
看来 ICU 的 udat_parseCalendar() 非常宽松并且仍然 即使字符串与格式不完全匹配,也能够解析。 我们理解在这些情况下更喜欢格式化程序返回 nil 但是(1)我们没有简单的方法知道输入字符串 与格式不匹配,因为 ICU 允许并且不会抛出 错误和(2)在这些情况下突然返回 nil 几乎 肯定是 bincompat 问题。
在我的情况下,我可以选择修改单元测试并在输入无效的情况下更加宽容,或者进行额外的检查(基于推荐的方法,这是帖子的公认答案)是否生成 NSDate字符串适合输入字符串。
【讨论】:
以上是关于NSDateFormatter 仍在解析而不是格式不正确的主要内容,如果未能解决你的问题,请参考以下文章
使用 NSDateFormatter 从 VBO 文件中解析日期/时间戳
NSDateFormatter 无法解析德语月份 March/June/Jul 的三个字母缩写
NSDateFormatter 无法解析德语月份 March/June/Jul 的三个字母缩写