122

2004-02-12T15:19:21+00:00在 PHP 中通过获取 ISO 8601 日期字符串(例如,)很容易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

对于如此重要​​的事情来说,这似乎是一个非常冗长的问题。

4

12 回答 12

221

使用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"];
[dateFormatter setCalendar:[NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian]];

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"
dateFormatter.calendar = Calendar(identifier: .gregorian)

let iso8601String = dateFormatter.string(from: Date())
于 2013-04-27T17:20:42.027 回答
70

iOS 10 引入了一个新NSISO8601DateFormatter类来处理这个问题。如果您使用的是 Swift 3,您的代码将是这样的:

let formatter = ISO8601DateFormatter()
let date = formatter.date(from: "2016-08-26T12:39:00Z")
let string = formatter.string(from: Date())
于 2016-08-26T11:39:36.920 回答
29

作为对 maddy 回答的补充,ISO 8601 的时区格式应为“ZZZZZ”(5 倍 Z),而不是单个“Z”(用于 RFC 822 格式)。至少在 iOS 6 上。

(见http://www.unicode.org/reports/tr35/tr35-25.html#Date_Format_Patterns

于 2013-09-04T08:06:58.993 回答
24

一个经常被忽视的问题是 ISO 8601 格式的字符串可能有毫秒,也可能没有。

换句话说,“2016-12-31T23:59:59.9999999”和“2016-12-01T00:00:00”都是合法的,但如果您使用静态类型的日期格式化程序,其中一个将不会被解析.

iOS 10开始,您应该使用ISO8601DateFormatter它来处理 ISO 8601 日期字符串的所有变体。请参见下面的示例:

let date = Date()
var string: String

let formatter = ISO8601DateFormatter()
string = formatter.string(from: date)

let GMT = TimeZone(abbreviation: "GMT")
let options: ISO8601DateFormatOptions = [.withInternetDateTime, .withDashSeparatorInDate, .withColonSeparatorInTime, .withTimeZone]
string = ISO8601DateFormatter.string(from: date, timeZone: GMT, formatOptions: options)

对于iOS 9 及更低版本,请使用以下方法和多个数据格式化程序。

我还没有找到涵盖这两种情况并抽象出这种细微差异的答案。这是解决它的解决方案:

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(fromISO8601String 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(fromISO8601String: dateToString)
// dateTo: 2016-12-31 23:59:59 +0000

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

我还建议查看有关日期格式化程序的Apple 文章。

于 2017-07-07T12:32:06.683 回答
8

因此,请使用 Sam Soffee 在 NSDate 上找到的类别here。将该代码添加到您的项目后,您就可以在 NSDate 上使用单一方法:

- (NSString *)sam_ISO8601String

它不仅是一行,而且比 NSDateFormatter 方法快得多,因为它是用纯 C 编写的。

于 2013-11-13T21:37:44.953 回答
6

Update

From iOS 10, you can just use NSISO8601DateFormatter from Foundation

Original answer

From IS8601, the problems are the representation and time zone

  • ISO 8601 = year-month-day time timezone
  • For date and time, there are basic (YYYYMMDD, hhmmss, ...) and extended format (YYYY-MM-DD, hh:mm:ss, ...)
  • Time zone can be Zulu, offset or GMT
  • Separator for date and time can be space, or T
  • There are week format for date, but it is rarely used
  • Timezone can be a lot of spaces after
  • Second is optional

Here are some valid strings

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

Solutions

NSDateFormatter

So here is the format that I'm using in onmyway133 ISO8601

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

About the Z identifier Date Field Symbol Table

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)

About locale Formatting Data Using the Locale Settings

Locales represent the formatting choices for a particular user, not the user’s preferred language. These are often the same but can be different. For example, a native English speaker who lives in Germany might select English as the language and Germany as the region

About en_US_POSIX Technical Q&A QA1480 NSDateFormatter and Internet Dates

On the other hand, if you're working with fixed-format dates, you should first set the locale of the date formatter to something appropriate for your fixed format. In most cases the best locale to choose is "en_US_POSIX", a locale that's specifically designed to yield US English results regardless of both user and system preferences. "en_US_POSIX" is also invariant in time (if the US, at some point in the future, changes the way it formats dates, "en_US" will change to reflect the new behaviour, but "en_US_POSIX" will not), and between machines ("en_US_POSIX" works the same on iOS as it does on OS X, and as it it does on other platforms).

Interesting related quetions

于 2016-05-06T22:39:42.667 回答
5

Just use NSISO8601DateFormatter from Foundation framework.

let isoDateFormatter = ISO8601DateFormatter()
print("ISO8601 string: \(isoDateFormatter.string(from: Date()))")
// ISO8601 string: 2018-03-21T19:11:46Z

https://developer.apple.com/documentation/foundation/nsiso8601dateformatter?language=objc

于 2018-03-21T19:12:37.240 回答
4

基于此要点:https ://github.com/justinmakaila/NSDate-ISO-8601/blob/master/NSDateISO8601.swift ,可以使用以下方法将 NSDate 转换为 yyyy-MM 格式的 ISO 8601 日期字符串-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"];
    }
于 2016-05-03T00:46:03.920 回答
2

如果您因为相反方式的搜索结果而在这里,这是最简单的解决方案:

let date = ISO8601DateFormatter().date(from: dateString)
于 2021-11-27T21:36:53.510 回答
2

使用 iOS 15,您可以获得 ISO860,如下所示:

let iso8601String = Date.now.ISO8601Format()
于 2021-10-21T09:39:25.863 回答
-1

这有点简单,并将日期放入 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()
于 2016-08-20T07:53:04.937 回答
-1

使用 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
        }
    }
}
于 2017-04-02T14:25:54.423 回答