Как получить дату ISO 8601 на iOS?

Достаточно легко получить строку даты ISO 8601 (например, 2004-02-12T15:19:21+00:00) в PHP через date('c'), но как получить ее в Objective-C (iPhone)? Есть ли такой же короткий способ сделать это?

Вот длинный путь, который я нашел для этого:

NSDateFormatter* dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";

NSDate *now = [NSDate date];
NSString *formattedDateString = [dateFormatter stringFromDate:now];
NSLog(@"ISO-8601 date: %@", formattedDateString);

// Output: ISO-8601 date: 2013-04-27T13:27:50-0700

Кажется, что очень сложно найти что-то такое центральное.

Ответ 1

Используйте NSDateFormatter:

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];   
NSLocale *enUSPOSIXLocale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
[dateFormatter setLocale:enUSPOSIXLocale];
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"];

NSDate *now = [NSDate date];
NSString *iso8601String = [dateFormatter stringFromDate:now];

И в Swift:

let dateFormatter = DateFormatter()
let enUSPosixLocale = Locale(identifier: "en_US_POSIX")
dateFormatter.locale = enUSPosixLocale
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"

let iso8601String = dateFormatter.string(from: Date())

Ответ 2

В качестве дополнения к безумному ответу формат часового пояса должен быть "ZZZZZ" (5 раз Z) для ISO 8601 вместо одного "Z" (который предназначен для формата RFC 822). По крайней мере, на iOS 6.

(см. http://www.unicode.org/reports/tr35/tr35-25.html#Date_Format_Patterns)

Ответ 3

iOS 10 представляет новый класс NSISO8601DateFormatter для обработки именно этого. Если вы используете Swift 3, ваш код будет примерно таким:

let formatter = ISO8601DateFormatter()
let date = formatter.date(from: "2016-08-26T12:39:00Z")
let string = formatter.string(from: Date())

Ответ 4

Так что используйте категорию Сэма Сори на NSDate, которую нашли здесь. Когда этот код добавлен в ваш проект, вы можете использовать один метод в NSDate:

- (NSString *)sam_ISO8601String

Это не только одна строка, но и намного быстрее, чем подход NSDateFormatter, поскольку она написана в чистом C.

Ответ 5

Из IS8601 проблемы представляют собой представление и часовой пояс

  • ISO 8601 = year-month-day time timezone
  • Для даты и времени существуют основные (YYYYMMDD, hhmmss,...) и расширенный формат (YYYY-MM-DD, hh: mm: ss,...)
  • Часовой пояс может быть Zulu, offset или GMT
  • Сепаратор для даты и времени может быть пробелом или T
  • Существует формат недели для даты, но он редко используется
  • Часовой пояс может быть много пробелов после
  • Второе необязательно

Вот несколько правильных строк

2016-04-08T10:25:30Z
2016-04-08 11:25:30+0100
2016-04-08 202530GMT+1000
20160408 08:25:30-02:00
2016-04-08 11:25:30     +0100

Решения

  • Разбирайте шаг за шагом, например soffes ISO8601
  • Преобразовать в основной формат, например onmyway133 ISO8601

NSDateFormatter

Итак, вот формат, который я использую в onmyway133 ISO8601

let formatter = NSDateFormatter()
formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
formatter.dateFormat = "yyyyMMdd HHmmssZ"

О идентификаторе Z Таблица символов поля даты

Z: The ISO8601 basic format with hours, minutes and optional seconds fields. The format is equivalent to RFC 822 zone format (when optional seconds field is absent)

О locale Форматирование данных с использованием настроек локали

Локалины представляют варианты форматирования для конкретного пользователя, а не для предпочитаемого пользователем языка. Они часто одни и те же, но могут быть разными. Например, говорящий на родном английском языке, который живет в Германии, может выбрать английский как язык и Германию как регион.

О en_US_POSIX Технические вопросы Q & QA1480 NSDateFormatter и интернет-даты

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

Интересные связанные вопросы

Ответ 6

Основываясь на этом принципе: https://github.com/justinmakaila/NSDate-ISO-8601/blob/master/NSDateISO8601.swift, следующий метод может использоваться для преобразования строки даты NSDate в ISO 8601 в формате yyyy -MM-dd'T'HH: mm: ss.SSSZ

-(NSString *)getISO8601String
    {
        static NSDateFormatter *formatter = nil;
        if (!formatter)
        {
            formatter = [[NSDateFormatter alloc] init];
            [formatter setLocale: [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
            formatter.timeZone = [NSTimeZone timeZoneWithAbbreviation: @"UTC"];
            [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS"];

        }
        NSString *iso8601String = [formatter stringFromDate: self];
        return [iso8601String stringByAppendingString: @"Z"];
    }

Ответ 7

Часто забываемая проблема заключается в том, что строки в формате ISO 8601 могут иметь миллисекунды и не могут.

Иными словами, оба "2016-12-31T23: 59: 59.9999999" и "2016-12-01T00: 00: 00" являются законными, но если вы используете форматировщик даты в статическом типе, один из них выиграл ' t анализируется.

Я не нашел ответа, который охватывает как случаи, так и абстракции от этой тонкой разницы. Вот решение, которое обращается к нему:

extension DateFormatter {

    static let iso8601DateFormatter: DateFormatter = {
        let enUSPOSIXLocale = Locale(identifier: "en_US_POSIX")
        let iso8601DateFormatter = DateFormatter()
        iso8601DateFormatter.locale = enUSPOSIXLocale
        iso8601DateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
        iso8601DateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
        return iso8601DateFormatter
    }()

    static let iso8601WithoutMillisecondsDateFormatter: DateFormatter = {
        let enUSPOSIXLocale = Locale(identifier: "en_US_POSIX")
        let iso8601DateFormatter = DateFormatter()
        iso8601DateFormatter.locale = enUSPOSIXLocale
        iso8601DateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'"
        iso8601DateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
        return iso8601DateFormatter
    }()

    static func date(fromISO8601StringWithOptionalMilliseconds string: String) -> Date? {
        if let dateWithMilliseconds = iso8601DateFormatter.date(from: string) {
            return dateWithMilliseconds
        }

        if let dateWithoutMilliseconds = iso8601WithoutMillisecondsDateFormatter.date(from: string) {
            return dateWithoutMilliseconds
        }

        return nil
    }
}

Применение:

let dateToString = "2016-12-31T23:59:59.9999999"
let dateTo = DateFormatter.date(fromISO8601StringWithOptionalMilliseconds: dateToString)
// dateTo: 2016-12-31 23:59:59 +0000

let dateFromString = "2016-12-01T00:00:00"
let dateFrom = DateFormatter.date(fromISO8601StringWithOptionalMilliseconds: dateFromString)
// dateFrom: 2016-12-01 00:00:00 +0000

Я также рекомендую проверить статью Apple о форматах даты.

Ответ 8

Это немного проще и помещает дату в UTC.

extension NSDate
{
    func iso8601() -> String
    {
        let dateFormatter = NSDateFormatter()
        dateFormatter.timeZone = NSTimeZone(name: "UTC")
        let iso8601String = dateFormatter.stringFromDate(NSDate())
        return iso8601String
    }
}

let date = NSDate().iso8601()

Ответ 9

Использование Swift 3.0 и iOS 9.0

extension Date {
    private static let jsonDateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.timeZone = TimeZone(identifier: "UTC")!
        return formatter
    }()

    var IOS8601String: String {
        get {
            return Date.jsonDateFormatter.string(from: self)
        }
    }

    init?(fromIOS8601 dateString: String) {
        if let d = Date.jsonDateFormatter.date(from: dateString) {
            self.init(timeInterval: 0, since:d)
        } else {
            return nil
        }
    }
}