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 的三个字母缩写

restkit 中的 ios NSDateFormatter

使用 NSDateFormatter 快速格式化日期

NSDateFormatter dateFromString 格式字符串问题