Разбор rfc3339 дат с помощью NSDateFormatter в iOS 4.х и macOS х 10,6: невозможно?

разбор даты rfc3339 с помощью NSDateFormatter представляется невозможным в общем случае. Я ошибаюсь? [Edit 2 лет спустя: теперь есть способ! См. ниже, сноска.]

не особенно податливый веб-сервис кормит меня датами, как:

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

Rfc3339 совместимый, вывод по умолчанию библиотеки jaxb, которую они используют. Обратите внимание на двоеточие, которое rfc3339 требует когда смещение не является литералом "z":

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

Я хочу разобрать их на NSDates.

NSDateFormatter хочет шаблоны в синтаксисе, указанном Unicode, который предлагает символы поля даты для часовых поясов, таких как" PDT"," -0800"," GMT-08:00", но не"-08:00".

Googling и другие подобные вопросы 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: работы. :)
  • string-манипулируйте моим входом и очистите последнюю двоеточие: хрупкий и уродливый, но вероятный путь наименьшего сопротивления.

Я точно понял ситуацию, что текущий NSDateFormatter следует unicode строго без расширений; и форматы unicode недостаточны для полного описания rfc3339 дата?

[сноска] Я возвращаюсь к этому три года спустя, чтобы добавить небольшое добавление: Unicode и Apple добавили эту функцию в строки формата, начиная с iOS6/OSX10.8. Сравнить последняя редакция на момент написания этой статьи С его непосредственный предшественник, и обратите внимание на добавление 5 " Z "s, которое дает формат зоны, такой как"-08:00". Так что, если вы можете уйти с поддержкой канавы для 5.х/10.7, есть новый правильный способ сделать это. Я уйду. предыдущий ответ стоит, так как это все еще лучший подход, когда требуется обратная совместимость.

3 ответов


синтаксический анализ строки даты в Cocoa может быть болью, особенно если вам приходится иметь дело с датами, генерируемыми веб-службами на основе .NET.

Я бы предложил взглянуть на категорию Nsdate + InternetDateTime, которую Майкл Вотерфолл имеет на NSDate в рамках своего проекта MWFeedParser на github. Это хорошо сработало для меня, анализируя именно тот формат даты, который вы описываете.

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


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

символы строкового формата нигде не задокументированы в документации 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;
}