Swift:如何创builddate时间戳和格式为ISO 8601,RFC 3339,UTC时区?

如何使用ISO 8601和RFC 3339的格式标准生成date时间戳?

目标是一个看起来像这样的string:

"2015-01-01T00:00:00.000Z" 

格式:

  • 年,月,日,如“XXXX-XX-XX”
  • 字母“T”作为分隔符
  • 小时,分,秒,毫秒,如“XX:XX:XX.XXX”。
  • 字母“Z”作为零偏移,aka UTC,GMT,祖鲁时间的区域指示符。

最佳案例:

  • Swift源代码简单,简洁,直接。
  • 无需使用任何额外的框架,子项目,cocoapod,C代码等。

我search了StackOverflow,谷歌,苹果等,并没有find一个迅速的答案。

看起来最有希望的类是NSDateNSDateFormatterNSTimeZone

相关问答: 如何在iOS中获得ISO 8601的date?

这是迄今为止我所做的最好的:

 var now = NSDate() var formatter = NSDateFormatter() formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0) println(formatter.stringFromDate(now)) 

Xcode 8.2•Swift 3.0.2

 extension Formatter { static let iso8601: DateFormatter = { let formatter = DateFormatter() formatter.calendar = Calendar(identifier: .iso8601) formatter.locale = Locale(identifier: "en_US_POSIX") formatter.timeZone = TimeZone(secondsFromGMT: 0) formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX" return formatter }() } extension Date { var iso8601: String { return Formatter.iso8601.string(from: self) } } extension String { var dateFromISO8601: Date? { return Formatter.iso8601.date(from: self) // "Mar 22, 2017, 10:22 AM" } } 

用法:

 let stringFromDate = Date().iso8601 // "2017-03-22T13:22:13.933Z" if let dateFromString = stringFromDate.dateFromISO8601 { print(dateFromString.iso8601) // "2017-03-22T13:22:13.933Z" } 

在这里输入图像描述

请记住按照Technical Q&A1480中的描述将语言环境设置为en_US_POSIX 。 在Swift 3:

 let date = Date() let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ" formatter.timeZone = TimeZone(secondsFromGMT: 0) formatter.locale = Locale(identifier: "en_US_POSIX") print(formatter.string(from: date)) 

如果你不需要毫秒,在iOS 10和MacOS 10.12中,你也可以使用ISO8601DateFormatter 。 这让你摆脱了设置locale和时区的杂草:

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

或者,在Swift 2:

 let date = NSDate() let formatter = NSDateFormatter() formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ" formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0) formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") print(formatter.stringFromDate(date)) 

问题是,如果您使用的是非格里历日历,那么除非您指定locale以及timeZonedateFormatstring,否则年度将不符合RFC3339 / ISO8601。

如果你想使用ISO8601DateFormatter()和一个来自Rails 4 + JSON feed的date(当然不需要millis),你需要在formatter上设置一些选项,否则它的date(from: string)函数将返回nil。 以下是我正在使用的:

 extension Date { init(dateString:String) { self = Date.iso8601Formatter.date(from: dateString)! } static let iso8601Formatter: ISO8601DateFormatter = { let formatter = ISO8601DateFormatter() formatter.formatOptions = [.withFullDate, .withTime, .withDashSeparatorInDate, .withColonSeparatorInTime] return formatter }() } 

下面是使用选项verse而不是在操场屏幕截图的结果:

在这里输入图像描述

为了进一步赞扬AndrésTorresMarroquín和Leo Dabus,我有一个保留小数秒的版本。 我无法在任何地方find它,但是Apple似乎在input和输出上都截断了3位精度的小数秒(即使使用SSSSSSS指定,与Unicode tr35-31相反)。

我应该强调,对于大多数使用情况,这可能不是必需的。 另请注意,它使用整数模数来重新创buildstring。 Double.truncatingRemainder()将上下舍入值。

Xcode 8/9和Swift 3.0-3.2

 extension Date { struct Formatter { static let iso8601: DateFormatter = { let formatter = DateFormatter() formatter.calendar = Calendar(identifier: .iso8601) formatter.locale = Locale(identifier: "en_US_POSIX") formatter.timeZone = TimeZone(identifier: "UTC") formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXXXX" return formatter }() } var iso8601: String { var data = Formatter.iso8601.string(from: self) if let fractionStart = data.range(of: "."), let fractionEnd = data.index(fractionStart.lowerBound, offsetBy: 7, limitedBy: data.endIndex) { let fractionRange = fractionStart.lowerBound..<fractionEnd let intVal = Int64(1000000 * self.timeIntervalSince1970) let newFraction = String(format: ".%06d", intVal % 1000000) data.replaceSubrange(fractionRange, with: newFraction) } return data } } extension String { var dateFromISO8601: Date? { guard let parsedDate = Date.Formatter.iso8601.date(from: self) else { return nil } var preliminaryDate = Date(timeIntervalSinceReferenceDate: floor(parsedDate.timeIntervalSinceReferenceDate)) if let fractionStart = self.range(of: "."), let fractionEnd = self.index(fractionStart.lowerBound, offsetBy: 7, limitedBy: self.endIndex) { let fractionRange = fractionStart.lowerBound..<fractionEnd let fractionStr = self.substring(with: fractionRange) if var fraction = Double(fractionStr) { fraction = Double(floor(1000000*fraction)/1000000) preliminaryDate.addTimeInterval(fraction) } } return preliminaryDate } } 

在将来,格式可能需要改变,这可能是一个小的头痛,在应用程序中的每个地方都有date.dateFromISO8601调用。 使用一个类和协议来包装实现,更改date时间格式调用在一个地方会更简单。 如果可能,使用RFC3339,它是一个更完整的表示。 DateFormatProtocol和DateFormat非常适合dependency injection。

 class AppDelegate: UIResponder, UIApplicationDelegate { internal static let rfc3339DateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ" internal static let localeEnUsPosix = "en_US_POSIX" } import Foundation protocol DateFormatProtocol { func format(date: NSDate) -> String func parse(date: String) -> NSDate? } import Foundation class DateFormat: DateFormatProtocol { func format(date: NSDate) -> String { return date.rfc3339 } func parse(date: String) -> NSDate? { return date.rfc3339 } } extension NSDate { struct Formatter { static let rfc3339: NSDateFormatter = { let formatter = NSDateFormatter() formatter.calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierISO8601) formatter.locale = NSLocale(localeIdentifier: AppDelegate.localeEnUsPosix) formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0) formatter.dateFormat = rfc3339DateFormat return formatter }() } var rfc3339: String { return Formatter.rfc3339.stringFromDate(self) } } extension String { var rfc3339: NSDate? { return NSDate.Formatter.rfc3339.dateFromString(self) } } class DependencyService: DependencyServiceProtocol { private var dateFormat: DateFormatProtocol? func setDateFormat(dateFormat: DateFormatProtocol) { self.dateFormat = dateFormat } func getDateFormat() -> DateFormatProtocol { if let dateFormatObject = dateFormat { return dateFormatObject } else { let dateFormatObject = DateFormat() dateFormat = dateFormatObject return dateFormatObject } } } 

在我的情况下,我必须将DynamoDB – lastUpdated列(Unix时间戳)转换为正常时间。

lastUpdated的初始值是:1460650607601 – 转换为2016-04-14 16:16:47 +0000通过:

  if let lastUpdated : String = userObject.lastUpdated { let epocTime = NSTimeInterval(lastUpdated)! / 1000 // convert it from milliseconds dividing it by 1000 let unixTimestamp = NSDate(timeIntervalSince1970: epocTime) //convert unix timestamp to Date let dateFormatter = NSDateFormatter() dateFormatter.timeZone = NSTimeZone() dateFormatter.locale = NSLocale.currentLocale() // NSLocale(localeIdentifier: "en_US_POSIX") dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ" dateFormatter.dateFromString(String(unixTimestamp)) let updatedTimeStamp = unixTimestamp print(updatedTimeStamp) } 

为了补充Leo Dabus的版本,我添加了对编写Swift和Objective-C的项目的支持,还增加了对可选毫秒的支持,可能不是最好的,但是你会得到这样一个观点:

Xcode 8和Swift 3

 extension Date { struct Formatter { static let iso8601: DateFormatter = { let formatter = DateFormatter() formatter.calendar = Calendar(identifier: .iso8601) formatter.locale = Locale(identifier: "en_US_POSIX") formatter.timeZone = TimeZone(secondsFromGMT: 0) formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX" return formatter }() } var iso8601: String { return Formatter.iso8601.string(from: self) } } extension String { var dateFromISO8601: Date? { var data = self if self.range(of: ".") == nil { // Case where the string doesn't contain the optional milliseconds data = data.replacingOccurrences(of: "Z", with: ".000000Z") } return Date.Formatter.iso8601.date(from: data) } } extension NSString { var dateFromISO8601: Date? { return (self as String).dateFromISO8601 } } 

有一个新的ISO8601DateFormatter类让你用一行创build一个string。 为了向后兼容,我使用了一个旧的C库。 我希望这对某个人有用。

Swift 3.0

 extension Date { var iso8601: String { if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { return ISO8601DateFormatter.string(from: self, timeZone: TimeZone.current, formatOptions: .withInternetDateTime) } else { var buffer = [CChar](repeating: 0, count: 25) var time = time_t(self.timeIntervalSince1970) strftime_l(&buffer, buffer.count, "%FT%T%z", localtime(&time), nil) return String(cString: buffer) } } }