Разбор дат rfc3339 с NSDateFormatter в iOS 4.x и MacOS X 10.6: невозможно? - PullRequest
17 голосов
/ 02 декабря 2010

Анализ даты rfc3339 с NSDateFormatter представляется невозможным в общем случае.Я ошибся?[Редактировать 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 ".

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

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

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

Ответы [ 3 ]

11 голосов
/ 12 апреля 2012

- 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];
8 голосов
/ 02 декабря 2010

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

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

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

5 голосов
/ 09 апреля 2014

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