ISO 8601:值得使用的唯一日期格式
必看的与主题相关的 xkcd
如何在您的程序中使用此代码
将源文件添加到您的项目中。
解析
创建一个 ISO 8601 日期格式化器,然后调用 [formatter dateFromString:myString]
。该方法将返回一个 NSDate 或 nil。
总共有六个解析器方法。实际解析器的方法是 -[ISO8601DateFormatter dateComponentsFromString:timeZone:range:]
。其他五个是基于这个方法。
当“outTimeZone”参数不设置为 NULL 时,“outTimeZone”是一个指向 NSTimeZone *
变量的指针。如果指定了时区,您将在该变量中接收到时区对象。如果指定了字符串没有时区,您将接收 nil。
当“outRange”参数不设置为 NULL 时,“outRange”是一个指向 NSRange
存储的指针。您将在该存储中接收到解析子字符串的范围。
反序列化
创建一个 ISO 8601 日期格式化器,然后调用 [formatter stringFromDate:myDate]
。该方法将返回一个字符串。
格式化器有几个属性控制其行为
- 您可以设置结果的字符串格式。默认情况下,格式化器将生成日历日期字符串;其他选项是周日期和序数日期。
- 您可以设置默认时区;默认情况下,它将使用
[NSTimeZone defaultTimeZone]
。
- 您可以使能严格模式,其中格式化器在字符串强制执行健全性检查。默认情况下,解析器将给予您很大的活动空间。
- 您可以设置是否包含字符串中的时间,如果是,则可以使用什么小时-分钟分隔符(默认
':'
)。
如何测试此代码是否有效
更新于2013年:从基于 make 的测试怪物转换到现代基于 OCUnit 的测试正在进行中。将非常感谢对老旧和新的测试案例的贡献。
使用 'make test' 执行所有测试。如果您只想执行 一些 测试
解析
键入 'make parser-test'。make 将构建测试程序(testparser),然后调用 testparser.sh.py 生成 testparser.sh。然后 make 调用 testparser.sh,它将使用不同的日期调用测试程序。
如果您不想使用我的测试,'make testparser' 将创建测试程序但不运行它。然后您可以自行使用任何想要的日期调用testparser。如果它没有给您期望的结果,请联系我,务必提供输入和输出。
反解析
输入 'make unparser-test'。make 将构建测试程序,然后调用testunparser.sh。该Shell脚本为从1991年到2010年每年的01-01调用每个测试程序,将输出写入文件,然后运行diff -qs将文件(testunparser.out)与包含已知的正确输出的文件(testunparser-expected.out)之间的比较。diff 应报告这些文件是相同的。
包括三个测试程序:unparse-date,unparse-weekdate和unparse-ordinal date。如果您不想使用我的测试,可以单独构建这些测试程序。每个程序接受由ISO 8601指定的日期(使用我的ISO 8601解析器解析),并输出应表示相同日期的字符串。
注意
版本历史
此版本为0.7。从0.6版本的变化
- Cocoa Touch现在正式支持。4bc0a08 5d95233
- 添加了一个Xcode项目,包含静态库目标。c847ddb 4bc0a08
-
stringFromObjectValue:
现在在您传递一个不是NSDate的值时记录到控制台。升级后请检查控制台输出 - 您可能一直有bug而不知道!4e05978
- 在头文件中添加了适当的文档,兼容appledoc。f17713f
- 修复了当输入不是有效的日期字符串时返回无效的日期组件/日期的问题。288f757 93bb9df 0cb6442
- 修复了反解析中的错误,其中时间区域错误(#3)。0e355ee 8f8a2c3(感谢,Carl Lindberg)
- 修复了反解析中的DST错误,其中时间区域偏移量是在一般而是在反解析的日期上计算的(#6)。5665132(感谢,Zachary West) 9e835c8 0d36057
- 修复了在iOS设备上的一些区域设置中AM/PM(或本地等效)出现的问题(#15)。4bc0a08 3f697b5 249b3df(感谢,Matthias Bauch)
- 修复了另一个在特定区域设置中将NSDateFormatter-rewriting-formats-to-12-hour的bug。7c9907f(感谢,Carl Lindberg)
- (iOS)类现在在接收到内存警告的情况下自动清除其时区缓存。您不再需要自己这样做,因此清除缓存的方法不再是公开的。138e186 3224b32 a9b6973 3f3c3b8
- 支持指定秒的分数。1d3194b 0b0b1ea(感谢,Luke Redpath)
- 支持非ASCII时间分隔符字符。47d5035
- 支持时间区域分隔符。8198f1b 3bd0c07 17c15ed(感谢Daniel Tull的建议。)
- 修复了解析器响应传递
nil
的错误。3c12abc 4980ee6
- 修复了(我认为)#5:当不应该应用DST时格式化日期的问题。7368fe8
- 将
stringFromObjectValue:
修复为按照NSFormatter的文档所述的行为。3846f80 9754438
- 添加了基于OCUnit的真实单元测试。c847ddb aa1c2cb daf3cc9 9029991 d92aeb6 b412326(感谢,Luke Redpath) 45af6bd cfedd75 af0b93c 6a7fd88 9e835c8 9e835c8 0d36057 7368fe8 4bc0a08 3f697b5 8f8a2c3 51c1130 e888681 13a872d f49193c 10ed166 d5fdf51 4980ee6 3846f80 288f757 93bb9df daa45fb 2d61bd0 9f35c4a 8f95099 d62777a 138e186 2a63718
- 修复了字符串范围计算中日期部分以'Z'结尾的问题。8f95099 efa095e 808e8c4
- 修复了在严格模式下未经改动的格式被错误地接受的问题。9f35c4a c4e0f15
- 修复了周日期字符串中日期和时间之间没有'T'的问题。7b6fd9f 6a1b66b
- 修复了周日期字符串中的错误时间区域。9f6af7c
- 现在明确拒绝指定间隔(如ISO 8601中所指定的)的字符串,至少目前是这样(#20)。6d539db 51c1130 bf6a2db 6aef760(感谢,Blake Watters)
-
ISO8601DefaultTimeSeparatorCharacter
现在声明为const
。我希望你们中没有人依赖于更改这一点。db9877c
- 将此README升级为Markdown格式。更改记录:103f666 fbd34b2 ebbf65a c0b9609
- 添加了上述xkcd漫画。更改记录:fbd34b2 63ba50a da3ed65
- 进行了些许现代化改进 (#25, #28)。更改记录:cdec3e9 8df32fd bb7c5c9
0.6版本相比0.5版本的变化
- 当未设置为严格解析时,允许在时区指定之前有空格,以提高与NSDate的输出(description)和输入(dateWithString:)的兼容性。更改记录:27603efc8a77
- 修复了格式化工具的问题:我们不再尝试格式化工具。更改记录:83415de9f527
- 修复了新的问题:现在小时已经被正确地填充了零(#4)。更改记录:a5608e189ebe af0c6b397428
- 解决了许多编译器警告。一些修复由Sparks提供。更改记录:8be3d6f7c6f2 78ae31de2170 68dc351e9fdb e7ea87db8621 873f499ae6db
- 现在快75倍。更改记录:6a023812bd2b 05dc35d6b505 3b2225e0ce8c d59720ad015a 10f526956963 297b8dae4095 796be11aa596 cadf0f0c8199 61d2959c6921
- iOS用户现在可以向ISO 8601日期格式化类发出清除其缓存(当接收到内存警告时你应该这么做)。更改记录:2bb1725914b1
- 向日历格式解解析测试工具添加了几个命令行选项。更改记录:c644aadb2b14
- 解析测试工具现在用GMT而不是本地时区显示解析的日期。更改记录:788c1455ecb1
- 添加了一个测试工具来测试包含时间的解解析。更改记录:a89a9a8b3d61
- 现在可以运行Clang静态分析器来执行日期格式器的“分析”。更改记录:b3dd33841f42
- 现在测试工具使用Clang构建。更改记录:0723d3aa6596
0.5版本相比0.4版本的变化
- 改写为使用NSCalendar的NSFormatter子类。
- 将其变成格式化工具使得与本地化绑定更容易使用。
- 使用NSCalendar意味着我们现在不再使用NSCalendarDate,苹果公司表示他们将在某个时刻弃用它。
- 修复了生成周日期的bug:一次减法可能给出一个负数,这在我使用无符号整数实现的算法中是个问题。我已经改变它来使用有符号整数,因此现在结果确实是负的。我还添加了一个针对这个问题的测试用例。
0.4版本相比0.3版本的变化
0.3版本相比0.2版本的变化
- Colin Barrett noticed that I used
%m
instead of %M
when creating the time strings. Oops.
- Colin also noticed that I had the
?:
in ISO8601DateStringWithTime:
the wrong way around. Oops again.
0.2版本相比0.1版本的变化
- 解解析器是新的。它已经被修改以允许同时处理组件。
- 解析器没有改变。
实现细节
解析
日期前的空白和日期后的任何内容将被忽略。因此,“ T23 and all's well”在此次方法中是一个有效的日期。(是的,T23是有效的ISO 8601日期。它表示23:00:00,或11 PM。)
支持除了扩展日期(年数超过4位)之外ISO 8601的所有花哨特性。具体来说,您可以使用基于周的日期(2006-W2表示2006年的第二周),顺序日期(2006-365表示12月31日),小数分钟(11:30.5等于11:30:30),以及小数秒(11:30:10.5)。支持所有指定时区的方法。
ISO 8601给日期交换双方留下了相当多的灵活性。我希望我选择了一些合理的默认值。例如(请注意,我写于2006年2月24日)
- 如果月份或月份和日期缺失,则假定1。 "2006" == "2006-01-01"。
- 如果没有年份或年份和月份缺失,假定当前年份。"--02-01" == "2006-02-01"。 "---28" == "2006-02-28"。
- 在基于周的日期中,如果缺少日期,这个实现返回该周的第一天:2006-W1是2006-01-01,2006-W2是2006-01-08,依此类推。
- 对于没有时间的任何日期,使用该日期的午夜。
- ISO 8601 允许选择 T0 或 T24 表示午夜。这个实现使用 T0。T24 将使你在第二天得到 T0。
- 如果没有指定时区,则使用本地时间(由[NSTimeZone localTimeZone]返回)。
当解析出一个没有世纪但只有年份的日期时,这个实现会添加当前的世纪。
这个实现对超出范围的数字具有容错性。例如,"2005-13-40T24:62:89" 等于 2006-02-10 上午 1:02。请注意,月份(13 > 12)、日期(40 > 31)、小时(24 > 23)、分钟(62 > 59)和秒(89 > 59)都超出了范围。
如上所述,有一个“严格”模式,它执行健全性检查。特别是,日期必须是字符串的全部内容,并且对数字进行范围检查。如果您有任何关于如何使此模式更严格的建议,请在内置加强部分提交改进请求。
反解析
我使用了Rick McCarty的将日历日期转换为周日期的算法,稍作修改。
错误
解析
此方法不会从字符串的任何地方提取日期,而只会在字符串的开始处(或任何前导空白)之后提取日期。有两种解决方案:要么要求你在一个仅包含 ISO 8601 日期的字符串上调用解析器,没有在其前后(对解析来说不好),要么使解析器能够找到一个 ISO 8601 日期作为子字符串。我不会做第一种,除非有个补丁,我可能也不会做第二种。
不支持日期范围(也由 ISO 8601 指定);这个方法只会返回一个日期。要处理范围至少需要另一个方法。
没有方法可以分析一个日期字符串并告诉你它里面有什么(年份、月份、周、天数、序数日等)。请随意提交补丁。
版权
此代码的版权为 2006–2013 Peter Hosey。它受 BSD 许可协议的约束;有关许可证的完整内容,请参阅 LICENSE.txt。