Friday, April 29, 2011

RFC 3339 dates and iOS parsing

I am currently developing an iPad client for a Java-based Web app that uses JAX-RS for the Web interface (Jersey is the implementation of JAX-RS being used).

I ran into an interesting problem yesterday regarding the parsing of RFC 3339 dates (example - 2011-04-28T16:28:03.065-07:00) in iOS.  Apple nicely documents how to parse RFC 3339 dates, but leaves out one detail.

There is an issue with the timezone containing the colon ':' character. The character must be removed for the RFC 3339 date to be able to be parsed into an NSDate instance.  I spent some time until I found this as a comment in a date parsing class.  Removing the colon from the timezone portion (i.e. 2011-04-28T16:28:03.065-0700) allows NSDateFormatter to parse the textual representation of the date into an NSDate instance.

I also ran into a cute issue: sometimes the fractional seconds aren't included, most likely because the value is 0.  I had to use two NSDateFormatter instances, one using 'yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSSZ' the other 'yyyy'-'MM'-'dd'T'HH':'mm':'ssZ' for the date without milliseconds.

Here is the final version of the code to parse RFC 3339 date text into an NSDate instance.

    NSMutableString* dateString = [rfc3339DateTimeString mutableCopy];
    NSRange range = [dateString rangeOfString:@":" options:NSBackwardsSearch];
    if (range.location != NSNotFound) {
        // remove the last ':'
        [dateString deleteCharactersInRange:range];
    }
   
    // Convert the RFC 3339 date time string to an NSDate.
    static NSDateFormatter* rfc3339DateFormatter1 = nil;
    static NSDateFormatter* rfc3339DateFormatter2 = nil;
    if (rfc3339DateFormatter1 == nil) {
        rfc3339DateFormatter1 = [[NSDateFormatter alloc] init];
        NSLocale* enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
        [rfc3339DateFormatter1 setLocale:enUSPOSIXLocale];
        [rfc3339DateFormatter1 setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSSZ"];
        [rfc3339DateFormatter1 setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];

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

        [enUSPOSIXLocale release];
    }
   
    NSDate* date = [rfc3339DateFormatter1 dateFromString:dateString];
    if (date == nil) {
        date = [rfc3339DateFormatter2 dateFromString:dateString];
    }
   
    return date;

No comments:

Post a Comment