문제

Parsing a rfc3339 date with NSDateFormatter appears to be impossible, in the general case. Am I wrong? [Edit 2 years later: there is now a way! See below and footnote.]

A not-especially-malleable web service is feeding me dates like:

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

Rfc3339 compliant, default output of the jaxb library they're using. Note the colon, which rfc3339 requires when the offset isn't a literal "z":

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

I want to parse these into NSDates.

NSDateFormatter wants patterns in the syntax specified by Unicode, which offers date field symbols for timezones like "PDT", "-0800", "GMT-08:00" but not "-08:00".

Googling, and other similar SO questions, produces only date formats like

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

The latter of which requires a literal "Z", and the former insists either the absence of a colon or presence of a "GMT". However, they appeared to work before ios 4.x (possibly by discarding the tz offset completely; my data aren't clear.)

My options at this point are a sorry lot:

  • discover some undocumented format specifier, or some strange mode to put NSDateFormatter into, that will accept the stray colon: longshot, likely nonexistent. [footnote]
  • persuade my service publisher to turn all dates into zulu time and specify 'Z': politically challenging.
  • write my own NSFormatter subclass or research good old strptime_l: work. :)
  • string-manipulate my input and strip the last colon: brittle and ugly, but the likely path of least resistance.

Have I understood the situation accurately, that current NSDateFormatter follows unicode strictly without extensions; and the unicode formats are insufficient to fully describe an rfc3339 date?

[FOOTNOTE] I come back to this three years later to tack on a small addendum: Unicode and Apple have added this feature to the format strings, as of iOS6/OSX10.8. Compare The latest revision as of this writing with its immediate predecessor, and note the addition of 5 "Z"s, which yields a zone format like "-08:00". So if you can get away with ditching support for 5.x/10.7, there's a new right way to do it. I'll leave the previous answer stand, as it's still the best approach when backward compatibility is required.

도움이 되었습니까?

해결책

Date string parsing in Cocoa can be a pain, especially if you have to deal with dates generated by .NET based web services.

I'd suggest looking at the NSDate+InternetDateTime category that Michael Waterfall has on NSDate as part of his MWFeedParser project on github. It's worked well for me parsing exactly the format of date you describe.

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

다른 팁

– getObjectValue:forString:range:error: can actually parse RFC3339 dates correctly. I have no idea why - dateWithString: cannot:

// 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];

The string format characters are documented nowhere in the Apple documentation. Instead there is a link hidden deep in some document pointing to the Unicode standard at

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

Using the information at that link it's quite simple:

- (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;
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top