ISO 8601:唯一值得使用的日期格式
必须的相关 xkcd
如何在您的程序中使用此代码
将源文件添加到您的项目中。
解析
创建一个 ISO 8601 日期格式化器,然后调用 [formatter dateFromString:myString]
。该方法将返回一个 NSDate 或 nil
。
总共有六个解析器方法。实际解析的是 -[ISO8601DateFormatter dateComponentsFromString:timeZone:range:]
方法,其他五个都基于此。
当“outTimeZone
”参数未设置为“NULL
”时,是一个指向“NSTimeZone *变量”。如果字符串指定了时区,则在变量中接收时区对象。如果字符串未指定时区,则接收“nil
”。
当“outRange
”参数未设置为“NULL
”时,是指向“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)的bug。0e355ee 8f8a2c3(感谢,Carl Lindberg)
- 修复了在反解析中DST的bug,其中时间区偏移量是通用的而不是针对正在反解析的日期(#6)。5665132(感谢,Zachary West)9e835c8 0d36057
- 修复了在iOS设备的一些区域中AM/PM(或本地等效)出现的bug(#15)。4bc0a08 3f697b5 249b3df(感谢,Matthias Bauch)
- 修复了另一个NSDateFormatter-rewriting-formats-to-12-hour-in-some-locales的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时,以DST格式格式化日期的bug。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,苹果表示他们将在某个时候弃用。
- 修复了周日期生成中的错误:一次减法可以给出一个负数结果,这因为我的算法实现使用了无符号整数。我已经将其更改为使用有符号整数,所以结果现在是真正的负数。我还添加了一个测试案例。a4075b7a3a1b a3ab5b2ff8f1
0.4版本与0.3版本的更改
0.3版本与0.2版本的更改
- Colin Barrett注意到我在创建时间字符串时使用了
%m
而不是%M
。哦,不。
- Colin还注意到我在
ISO8601DateStringWithTime:
中错误地围绕了?:
。又错了。
0.2版本与0.1版本的更改
- 解析器是新的。它被修改以允许同时使用组成部分,
- 解析器没有改变。
实现细节
解析
在日期之前和之后忽略空白。因此,“ T23 and all's well”为此方法的目的而言是一个有效的日期。是的,T23是一个有效的ISO 8601日期。它表示23:00:00,或晚上11点。)
支持所有ISO 8601的繁琐,除了扩展日期(年数超过4位)。具体来说,您可以使用基于周的日期(2006-W2表示2006年的第2周),序数日期(2006-365表示12月31日),小数分钟(11:30.5等于11:30:30),以及小数秒(11:30:10.5)。支持指定时区的所有方式。
ISO 8601在日期交换的部分留下很多灵活性。希望我选择了一些合理的默认值。例如(请注意我是在2006-02-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。