如何生成日期时间戳,使用格式标准为ISO 8601和RFC 3339?
目标是一个看起来像这样的字符串:
"2015-01-01T00:00:00.000Z"
格式:
年、月、日,如“XXXX-XX-XX”
用字母“T”作为分隔符
时,分,秒,毫秒,如“XX:XX:XX. xxx”。
字母“Z”作为零偏移的区域指示符,也就是UTC, GMT, Zulu时间。
最好的情况:
Swift源代码简单,简短,直接。
不需要使用任何额外的框架、子项目、cocoapod、C代码等。
我已经搜索了StackOverflow,谷歌,Apple等,还没有找到一个Swift的答案。
最有前途的类是NSDate, NSDateFormatter, NSTimeZone。
相关问答:如何在iOS上获得ISO 8601日期?
这是我目前为止想到的最好的:
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))
请记住按照Technical Q&A1480中的描述将区域设置为en_US_POSIX。在Swift 3中:
let date = Date()
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"
print(formatter.string(from: date))
问题是,如果您在使用非公历的设备上,年份将不符合RFC3339/ISO8601,除非您指定区域设置以及timeZone和dateFormat字符串。
或者你可以使用ISO8601DateFormatter来让你自己摆脱设置locale和timeZone的麻烦:
let date = Date()
let formatter = ISO8601DateFormatter()
formatter.formatOptions.insert(.withFractionalSeconds) // this is only available effective iOS 11 and macOS 10.13
print(formatter.string(from: date))
对于Swift 2的演绎,请参见此答案的先前修订。
为了进一步赞扬Andrés托雷斯Marroquín和利奥·达布斯,我有一个保留小数秒的版本。我在任何地方都找不到它的文档,但苹果在输入和输出上都将小数秒截断为微秒(3位精度)(即使使用SSSSSSS指定,与Unicode tr35-31相反)。
我应该强调,对于大多数用例来说,这可能不是必需的。在线约会通常不需要毫秒级的精度,如果需要,使用不同的数据格式通常会更好。但有时必须以特定的方式与预先存在的系统进行互操作。
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 {
// create base Date format
var formatted = DateFormatter.iso8601.string(from: self)
// Apple returns millisecond precision. find the range of the decimal portion
if let fractionStart = formatted.range(of: "."),
let fractionEnd = formatted.index(fractionStart.lowerBound, offsetBy: 7, limitedBy: formatted.endIndex) {
let fractionRange = fractionStart.lowerBound..<fractionEnd
// replace the decimal range with our own 6 digit fraction output
let microseconds = self.timeIntervalSince1970 - floor(self.timeIntervalSince1970)
var microsecondsStr = String(format: "%.06f", microseconds)
microsecondsStr.remove(at: microsecondsStr.startIndex)
formatted.replaceSubrange(fractionRange, with: microsecondsStr)
}
return formatted
}
}
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
}
}