Разбор дат rfc3339 с помощью NSDateFormatter в iOS 4.x и MacOS X 10.6: невозможно?

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

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

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


person rgeorge    schedule 01.12.2010    source источник


Ответы (3)


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

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

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

person GoannaGuy    schedule 02.12.2010
comment
Спасибо за указание - я посмотрел его источник, и что вы знаете, вариант 4 (удалить последнее двоеточие) именно то, что он делает. Поскольку это работает для него, я, вероятно, последую его примеру. - person rgeorge; 02.12.2010

– 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];
person sc0rp10n    schedule 12.04.2012
comment
На самом деле мне здесь нечего улучшать, и я хотел поблагодарить автора (sc0rp10n) за указание на то, что dateFromString: не работает с такими датами, а getObjectValue: работает. Это сводило меня с ума, и я собирался попробовать getObjectValue:, чтобы указать диапазон, выпадающий из части часового пояса. Ваше предложение спасло меня. Спасибо. - person John Raggio; 31.08.2012

Символы строкового формата нигде не описаны в документации 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;
}
person gnasher729    schedule 09.04.2014
comment
да, семейство форматов 'X'... было добавлено в версию спецификации, поддерживаемую iOS 7 (tr35-31). 'ZZZZZ' определен как эквивалент 'XXXXX' и доступен в ios6 (определен -25), поэтому для этого конкретного приложения я рекомендую первое. - person rgeorge; 09.04.2014
comment
XXXXX и ZZZZZ более снисходительны, чем RFC 3339, поскольку они допускают секунды в часовом поясе. XXX ближе всего к спецификации (т. е. допускает Z и смещения, например -08:00). - person Doug Richardson; 12.05.2015