Средства форматирования даты

Существует два основных метода, которые Вы используете, чтобы создать строковое представление даты и проанализировать строку для получения объекта даты использование средства форматирования даты —dateFromString: и stringFromDate: соответственно. Можно также использовать getObjectValue:forString:range:error: при необходимости в большем количестве управления диапазоном строки, Вы хотите проанализировать.

Существует много атрибутов, которые можно получить и установить на средстве форматирования даты. При представлении информации пользователю необходимо обычно просто использовать NSDateFormatter константы стиля для указания предопределенных наборов атрибутов, определяющих, как отформатированная дата выведена на экран. Если необходимо генерировать представление даты в точном формате, однако, необходимо использовать строку формата.

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

Во всех случаях необходимо полагать что значение по умолчанию средств форматирования к использованию локали пользователя (currentLocale) наложенный с предпочтительными настройками пользователя. Если Вы хотите использовать локаль пользователя, но без их отдельных настроек, можно получить ID локали от текущей пользовательской локали (localeIdentifier) и сделайте новую "стандартную” локаль с который, затем установите стандартную локаль как средство форматирования locale.

Используйте стили средства форматирования для подарить датам и времени предпочтения пользователя

NSDateFormatter упрощает для Вас форматировать дату с помощью настроек пользователь, сконфигурированный в Международной предпочтительной панели в Установках системы. NSDateFormatter константы стиля —NSDateFormatterNoStyle, NSDateFormatterShortStyle, NSDateFormatterMediumStyle, NSDateFormatterLongStyle, и NSDateFormatterFullStyle— укажите наборы атрибутов, определяющих, как дата выведена на экран согласно предпочтениям пользователя.

Вы указываете стиль компонентов даты и времени средства форматирования даты независимо с помощью setDateStyle: и setTimeStyle: соответственно. Перечисление 1 иллюстрирует, как можно отформатировать дату с помощью стилей средства форматирования. Заметьте использование NSDateFormatterNoStyle подавить компонент времени и привести к строке, содержащей просто дату.

Перечисление 1  , Форматирующее дату с помощью стилей средства форматирования

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
[dateFormatter setTimeStyle:NSDateFormatterNoStyle];
 
NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:162000];
 
NSString *formattedDateString = [dateFormatter stringFromDate:date];
NSLog(@"formattedDateString: %@", formattedDateString);
// Output for locale en_US: "formattedDateString: Jan 2, 2001".

Используйте строки формата для указания пользовательских форматов

Существует вообще говоря две ситуации, в которых необходимо использовать пользовательские форматы:

  1. Для строк фиксированного формата, как интернет-даты.

  2. Для видимых пользователем элементов, не соответствующих ни одного из существующих стилей

Фиксированные форматы

Для указания пользовательского фиксированного формата для средства форматирования даты Вы используете setDateFormat:. Строка формата использует образцы формата из Технического стандарта № 35 Unicode. Версия стандарта меняется в зависимости от выпуска операционной системы:

Несмотря на то, что в принципе строка формата указывает фиксированный формат по умолчанию NSDateFormatter все еще берет предпочтения пользователя (включая установку локали) во внимание. Необходимо рассмотреть следующие моменты при использовании строк формата:

  • NSDateFormatter обрабатывает числа в строке, которую Вы анализируете, как будто они были в выбранном календаре пользователя. Например, если пользователь выбирает буддистский календарь, анализируя год 2010 доходы NSDate объект в 1467 в Григорианском календаре. (Для больше о различных calendrical системах и как использовать их, см. Руководство по программированию Даты и времени.)

  • В iOS пользователь может переопределить AM/PM по умолчанию по сравнению с 24-часовым временем, установив. Это может вызвать NSDateFormatter для перезаписи строки формата, Вы устанавливаете.

Отметьте с форматом строки формата Unicode, необходимо включить буквенный текст в строку формата между апострофами ('').

Следующий пример иллюстрирует использование строки формата для генерации строки:

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd 'at' HH:mm"];
 
NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:162000];
 
NSString *formattedDateString = [dateFormatter stringFromDate:date];
NSLog(@"formattedDateString: %@", formattedDateString);
// For US English, the output may be:
// formattedDateString: 2001-01-02 at 13:00

Существует две вещи отметить об этом примере:

  1. Это использует yyyy указать компонент года. Частая ошибка состоит в том, чтобы использовать YYYY. yyyy указывает календарный год тогда как YYYY указывает год (“Недели Года”), используемый в календаре недели года ISO. В большинстве случаев, yyyy и YYYY приведите к тому же числу, однако они могут отличаться. Обычно необходимо использовать календарный год.

  2. Представление времени может быть 13:00. В iOS, однако, если пользователь переключил 24-часовое Время на Прочь, время может быть 1:00 pm.

Пользовательские форматы для видимых пользователем дат

Для отображения даты, содержащей определенный набор элементов Вы используете dateFormatFromTemplate:options:locale:]. Метод генерирует строку формата с компонентами даты, которые Вы хотите использовать, но с корректной пунктуацией и порядком, подходящим для пользователя (т.е. настроенный для локали и предпочтений пользователя). Вы тогда используете строку формата для создания средства форматирования.

Например, для создания средства форматирования для отображения сегодняшнего дневного имени, день и месяц с помощью текущей локали, Вы записали бы:

NSString *formatString = [NSDateFormatter dateFormatFromTemplate:@"EdMMM" options:0
                                          locale:[NSLocale currentLocale]];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:formatString];
 
NSString *todayString = [dateFormatter stringFromDate:[NSDate date]];
NSLog(@"todayString: %@", todayString);

Для понимания потребности в этом рассмотрите ситуацию, где Вы хотите вывести на экран дневное имя, день и месяц. Вы не можете создать это представление даты с помощью стилей средства форматирования (нет никакого стиля, опускающего год). Ни один, тем не менее, не может Вы легко и последовательно создавать представление правильно с помощью строк формата. Несмотря на то, что на первый взгляд это может казаться прямым, существует сложность: пользователь из США обычно ожидал бы даты в форме, “понедельник, Ян 3”, тогда как пользователь из Великобритании будет обычно ожидать даты в форме “понедельник 31 Ян”.

Следующий пример иллюстрирует тезис:

NSLocale *usLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
NSString *usFormatString = [NSDateFormatter dateFormatFromTemplate:@"EdMMM" options:0 locale:usLocale];
NSLog(@"usFormatterString: %@", usFormatString);
// Output: usFormatterString: EEE, MMM d.
 
NSLocale *gbLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_GB"];
NSString *gbFormatString = [NSDateFormatter dateFormatFromTemplate:@"EdMMM" options:0 locale:gbLocale];
NSLog(@"gbFormatterString: %@", gbFormatString);
// Output: gbFormatterString: EEE d MMM.

Парсинг строк даты

В дополнение к методам, наследованным от NSFormatter (такой как getObjectValue:forString:errorDescription:), NSDateFormatter добавляет dateFromString: и getObjectValue:forString:range:error:. Эти методы упрощают для Вас использовать NSDateFormatter возразите непосредственно в коде и упростите форматировать даты в строки более сложные и более удобные способы, чем NSString форматирование позволяет.

getObjectValue:forString:range:error: метод позволяет Вам указывать поддиапазон строки, которая будет проанализирована, и это возвращает диапазон строки, фактически проанализированной (в случае отказа, это указывает, где отказ произошел). Это также возвращается NSError объект, который может содержать более богатую информацию, чем строка отказа, возвращенная getObjectValue:forString:errorDescription: метод наследовался от NSFormatter.

Если Вы работаете с датами фиксированного формата, необходимо сначала установить локаль средства форматирования даты к чему-то подходящему для фиксированного формата. В большинстве случаев лучшая локаль для выбора en_US_POSIX, локаль это специально предназначено для получения английских результатов США и независимо от пользовательских настроек и независимо от установок системы. en_US_POSIX является также инвариантным своевременно (если США, в некоторый момент в будущем, изменяют способ, которым это форматирует даты, en_US изменится для отражения нового поведения, но en_US_POSIX не будет), и между платформами (en_US_POSIX работает то же над iPhone OS, как это делает на OS X, и как это делает на других платформах).

Как только Вы установили en_US_POSIX как локаль средства форматирования даты, можно тогда установить строку формата даты, и средство форматирования даты будет последовательно вести себя для всех пользователей.

Перечисление 2 показывает, как использовать NSDateFormatter для обеих из ролей, описанных выше. Сначала это создает a en_US_POSIX средство форматирования даты для парсинга входящей строки даты RFC 3339, с помощью фиксированной строки формата даты и UTC как часовой пояс. Затем, это создает стандартное средство форматирования даты для рендеринга даты как строки для отображения пользователю.

Перечисление 2  , Анализирующее разовый датой RFC 3339

- (NSString *)userVisibleDateTimeStringForRFC3339DateTimeString:(NSString *)rfc3339DateTimeString {
    /*
      Returns a user-visible date time string that corresponds to the specified
      RFC 3339 date time string. Note that this does not handle all possible
      RFC 3339 date time strings, just one of the most common styles.
     */
 
    NSDateFormatter *rfc3339DateFormatter = [[NSDateFormatter alloc] init];
    NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
 
    [rfc3339DateFormatter setLocale:enUSPOSIXLocale];
    [rfc3339DateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"];
    [rfc3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
 
    // Convert the RFC 3339 date time string to an NSDate.
    NSDate *date = [rfc3339DateFormatter dateFromString:rfc3339DateTimeString];
 
    NSString *userVisibleDateTimeString;
    if (date != nil) {
        // Convert the date object to a user-visible date string.
        NSDateFormatter *userVisibleDateFormatter = [[NSDateFormatter alloc] init];
        assert(userVisibleDateFormatter != nil);
 
        [userVisibleDateFormatter setDateStyle:NSDateFormatterShortStyle];
        [userVisibleDateFormatter setTimeStyle:NSDateFormatterShortStyle];
 
        userVisibleDateTimeString = [userVisibleDateFormatter stringFromDate:date];
    }
    return userVisibleDateTimeString;
}

Средства форматирования кэша для эффективности

Создание средства форматирования даты не является дешевой работой. Если Вы, вероятно, будете часто использовать средство форматирования, обычно более эффективно кэшировать единственный экземпляр, чем создать и избавиться от многократных экземпляров. Один подход должен использовать a static переменная.

Перечисление 3 повторно реализует метод, который, как показывают в Перечислении 2, держался за средства форматирования даты для последующего повторного использования.

Перечисление 3  , Анализирующее RFC 3339 разовое датой использование кэшируемого средства форматирования

static NSDateFormatter *sUserVisibleDateFormatter = nil;
 
- (NSString *)userVisibleDateTimeStringForRFC3339DateTimeString:(NSString *)rfc3339DateTimeString {
    /*
      Returns a user-visible date time string that corresponds to the specified
      RFC 3339 date time string. Note that this does not handle all possible
      RFC 3339 date time strings, just one of the most common styles.
     */
 
    // If the date formatters aren't already set up, create them and cache them for reuse.
    static NSDateFormatter *sRFC3339DateFormatter = nil;
    if (sRFC3339DateFormatter == nil) {
        sRFC3339DateFormatter = [[NSDateFormatter alloc] init];
        NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
 
        [sRFC3339DateFormatter setLocale:enUSPOSIXLocale];
        [sRFC3339DateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"];
        [sRFC3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
    }
 
    // Convert the RFC 3339 date time string to an NSDate.
    NSDate *date = [rfc3339DateFormatter dateFromString:rfc3339DateTimeString];
 
    NSString *userVisibleDateTimeString;
    if (date != nil) {
        if (sUserVisibleDateFormatter == nil) {
            sUserVisibleDateFormatter = [[NSDateFormatter alloc] init];
            [sUserVisibleDateFormatter setDateStyle:NSDateFormatterShortStyle];
            [sUserVisibleDateFormatter setTimeStyle:NSDateFormatterShortStyle];
        }
        // Convert the date object to a user-visible date string.
        userVisibleDateTimeString = [sUserVisibleDateFormatter stringFromDate:date];
    }
    return userVisibleDateTimeString;
}

Если Вы кэшируете средства форматирования даты (или любые другие объекты, зависящие от текущей локали пользователя), необходимо подписаться на NSCurrentLocaleDidChangeNotification уведомление и обновление Ваши кэшируемые объекты, когда изменяется текущая локаль. Код в Перечислении 3 определяет sUserVisibleDateFormatter за пределами метода так, чтобы другой код, не показанный, мог обновить его по мере необходимости. Напротив, sRFC3339DateFormatter определяется в методе, потому что проектом это не зависит от настроек локали пользователя.

Рассмотрите функции Unix для фиксированного формата, нелокализованных дат

Для даты и времена в фиксированном, нелокализованном формате, которые, как всегда гарантируют, будут использовать тот же календарь, это может иногда быть проще и более эффективным использовать функции стандартной библиотеки для C strptime_l и strftime_l.

Знайте, что библиотека C также имеет идею текущей локали. Для гарантии фиксированного формата даты необходимо передать NULL как loc параметр этих подпрограмм. Это заставляет их использовать локаль POSIX (также известный как локаль C), который эквивалентен Какао en_US_POSIX локаль, как проиллюстрировано в этом примере.

struct tm  sometime;
const char *formatString = "%Y-%m-%d %H:%M:%S %z";
(void) strptime_l("2005-07-01 12:00:00 -0700", formatString, &sometime, NULL);
NSLog(@"NSDate is %@", [NSDate dateWithTimeIntervalSince1970: mktime(&sometime)]);
// Output: NSDate is 2005-07-01 12:00:00 -0700