简体   繁体   English

如何在 iOS 上获得 ISO 8601 日期?

[英]How do I get an ISO 8601 date on iOS?

It's easy enough to get the ISO 8601 date string (for example, 2004-02-12T15:19:21+00:00 ) in PHP via date('c') , but how does one get it in Objective-C (iPhone)?通过date('c')在 PHP 中获取 ISO 8601 日期字符串(例如2004-02-12T15:19:21+00:00 )很容易,但是如何在 ZA5084C5B36F309E58B2629BED5212E (iPhone) 中获取它? Is there a similarly short way to do it?有没有类似的短方法来做到这一点?

Here's the long way I found to do it:这是我发现的很长的路要走:

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

It seems an awful lot of rigmarole for something so central.对于如此重要的事情来说,这似乎是一个非常冗长的问题。

Use NSDateFormatter : 使用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];

And in 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())

iOS 10 introduces a new NSISO8601DateFormatter class to handle just this. iOS 10引入了一个新的NSISO8601DateFormatter类来处理这个问题。 If you're using Swift 3, your code would be something like this: 如果您使用的是Swift 3,您的代码将是这样的:

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

As a complement to maddy's answer, the time zone format should be "ZZZZZ" (5 times Z) for ISO 8601 instead of a single "Z" (which is for RFC 822 format). 作为maddy答案的补充,时区格式应为ISO 8601的“ZZZZZ”(5倍Z),而不是单个“Z”(用于RFC 822格式)。 At least on iOS 6. 至少在iOS 6上。

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

An often overlooked issue is that strings in ISO 8601 format might have milliseconds and might not. 一个经常被忽视的问题是ISO 8601格式的字符串可能有几毫秒而不是。

In other words, both "2016-12-31T23:59:59.9999999" and "2016-12-01T00:00:00" are legit, but if you are using static-typed date formatter, one of them won't be parsed. 换句话说,“2016-12-31T23:59:59.9999999”和“2016-12-01T00:00:00”都是合法的,但如果您使用的是静态类型的日期格式化程序,则其中一个将不会被解析。

Starting from iOS 10 you should use ISO8601DateFormatter that handles all variations of ISO 8601 date strings. iOS 10开始,您应该使用ISO8601DateFormatter来处理ISO 8601日期字符串的所有变体。 See example below: 见下面的例子:

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)

For iOS 9 and below use the following approach with multiple data formatters. 对于iOS 9及更低版本,请使用以下方法和多个数据格式化程序。

I haven't found an answer that covers both cases and abstracts away this subtle difference. 我还没有找到一个涵盖这两个案例的答案,并且摘要了这个微妙的差异。 Here is the solution that addresses it: 以下是解决它的解决方案:

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
    }
}

Usage: 用法:

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

I also recommend checking Apple article about date formatters. 我还建议查看关于日期格式化程序的Apple文章

So use Sam Soffee's category on NSDate found here . 所以在这里使用Sam Soffee的NSDate类别。 With that code added to your project, you can from then on use a single method on NSDate: 将代码添加到项目中后,您可以在NSDate上使用单个方法:

- (NSString *)sam_ISO8601String

Not only is it one line, its much faster than the NSDateFormatter approach, since its written in pure C. 它不仅是一行,它比NSDateFormatter方法快得多,因为它是用纯C.写的。

From IS8601 , the problems are the representation and time zone IS8601开始 ,问题是表示和时区

  • ISO 8601 = year-month-day time timezone 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, ...) 对于日期和时间,有基本(YYYYMMDD,hhmmss,...)和扩展格式(YYYY-MM-DD,hh:mm:ss,...)
  • Time zone can be Zulu, offset or GMT 时区可以是Zulu,偏移或GMT
  • Separator for date and time can be space, or T 日期和时间的分隔符可以是空格或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 NSDateFormatter

So here is the format that I'm using in onmyway133 ISO8601 所以这是我在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标识符日期字段符号表

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 关于locale 设置使用区域设置格式化数据

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 关于en_US_POSIX 技术问答QA1480 NSDateFormatter和Internet日期

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”,这是一种专门用于产生美国英语结果的语言环境,无论用户和系统偏好如何。 "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). “en_US_POSIX”在时间上也是不变的(如果美国在未来的某个时刻改变了格式化日期的方式,“en_US”将改变以反映新行为,但“en_US_POSIX”将不会),以及机器之间( “en_US_POSIX”在iOS上的工作方式与在OS X上的工作方式相同,就像在其他平台上一样。

Interesting related quetions 有趣的相关排队

Just use NSISO8601DateFormatter from Foundation framework. 只需使用Foundation框架中的NSISO8601DateFormatter即可

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 https://developer.apple.com/documentation/foundation/nsiso8601dateformatter?language=objc

Based on this gist: https://github.com/justinmakaila/NSDate-ISO-8601/blob/master/NSDateISO8601.swift , the following method can be used to convert NSDate to ISO 8601 date string in the format of yyyy-MM-dd'T'HH:mm:ss.SSSZ 基于这个要点: 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"];
    }

This is a little bit simpler and puts the date into UTC. 这有点简单,并将日期放入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()

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

let iso8601String = Date.now.ISO8601Format()

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

let date = ISO8601DateFormatter().date(from: dateString)

Using Swift 3.0 and iOS 9.0 使用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
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM