解析rfc3339与iOS 4.x和MacOS X 10.6中的NSDateFormatter约会:不可能?

时间:2010-12-01 23:16:45

标签: cocoa macos ios nsdateformatter rfc3339

在一般情况下,使用NSDateFormatter解析rfc3339日期似乎是不可能的。我错了吗? [编辑2年后:现在有办法!见下文和脚注。]

一个不特别具有可塑性的网络服务正在为我提供日期:

2009-12-31T00:00:00-06:00

符合Rfc3339,他们正在使用的jaxb库的默认输出。注意冒号,当偏移量不是文字“z”时,rfc3339 需要

time-numoffset  = ("+" / "-") time-hour ":" time-minute
time-offset     = "Z" / time-numoffset

我想将这些解析为NSDates。

NSDateFormatter需要Unicode指定语法中的模式,它为“PDT”,“ - 0800”,“GMT-08:00”但不是“-08:00”等时区提供日期字段符号。

谷歌搜索和其他类似的SO问题只生成日期格式,如

[myDateParser setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"];
/* or: */ [myDateParser setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"];

后者需要字面上的“Z”,前者坚持要么没有结肠,要么存在“GMT”。但是,它们似乎在ios 4.x之前工作(可能完全丢弃tz偏移量;我的数据不清楚。)

此时我的选择很遗憾:

  • 发现一些未记录的格式说明符,或者将NSDateFormatter放入的一些奇怪的模式,它将接受流浪冒号:longshot,可能不存在。 [脚注]
  • 说服我的服务发布者将所有日期转换为祖鲁时间并指定'Z':具有政治挑战性。
  • 编写我自己的NSFormatter子类或研究好的旧strptime_l:work。 :)
  • 字符串 - 操纵我的输入并删除最后一个冒号:脆弱和丑陋,但可能是阻力最小的路径。

我是否准确理解了这种情况,当前的NSDateFormatter严格遵循unicode而没有扩展;并且unicode格式不足以完全描述rfc3339的日期?

[脚注] 三年后我回到这个小小的附录:Unicode和Apple已经将这个功能添加到格式字符串中,从iOS6 / OSX10.8开始。将The latest revision as of this writingits immediate predecessor进行比较,并注意添加5“Z”,这会产生类似“-08:00”的区域格式。因此,如果您能够放弃对5.x / 10.7的弃用支持,那么有一种新的正确方法可以做到这一点。我将离开之前的答案,因为当需要向后兼容时,它仍然是最好的方法。

3 个答案:

答案 0 :(得分:11)

- getObjectValue:forString:range:error:实际上可以正确解析RFC3339日期。我不知道为什么 - dateWithString:不能:

// RFC3339 date formatting
NSString *dateString = @"2012-04-11T18:34:19+00:00";
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";

NSDate *date;
NSError *error;
[formatter getObjectValue:&date forString:dateString range:nil error:&error];

答案 1 :(得分:8)

Cocoa中的日期字符串解析可能很麻烦,特别是如果您必须处理基于.NET的Web服务生成的日期。

我建议查看Michael Waterfall在NSDate上的NSDate + InternetDateTime类别,作为他在github上的MWFeedParser项目的一部分。它很适合我解析你描述的日期格式。

https://github.com/mwaterfall/MWFeedParser/

答案 2 :(得分:5)

Apple文档中没有记录字符串格式字符。相反,在某些文档中隐藏了一个指向

的Unicode标准的链接

http://www.unicode.org/reports/tr35/tr35-31/tr35-dates.html#Date_Format_Patterns

使用该链接的信息非常简单:

- (NSDate*)dateFromRFC3339String:(NSString*)aString
{
    static NSDateFormatter* sRFC3339DateFormatter = nil;
    static NSDateFormatter* sRFC3339DateFormatterSubSeconds = nil;
    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{
        NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];

        sRFC3339DateFormatter = [[NSDateFormatter alloc] init];
        [sRFC3339DateFormatter setLocale:enUSPOSIXLocale];
        [sRFC3339DateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ssXXXXX"];
        [sRFC3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];

        sRFC3339DateFormatterSubSeconds = [[NSDateFormatter alloc] init];
        [sRFC3339DateFormatterSubSeconds setLocale:enUSPOSIXLocale];
        [sRFC3339DateFormatterSubSeconds setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSSSSSXXXXX"];
        [sRFC3339DateFormatterSubSeconds setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
    });

    NSDate* date = [sRFC3339DateFormatter dateFromString:aString];
    if (date == nil)
        date = [sRFC3339DateFormatterSubSeconds dateFromString:aString];

    return date;
}