Wie erzeugt man einen Datums- und Zeitstempel unter Verwendung der Formatstandards für ISO 8601 und RFC 3339?
Das Ziel ist eine Zeichenkette, die wie folgt aussieht:
"2015-01-01T00:00:00.000Z"
Format:
Bester Fall:
Ich habe StackOverflow, Google, Apple, etc. durchsucht und keine Swift-Antwort auf diese Frage gefunden.
Die Klassen, die am vielversprechendsten erscheinen, sind NSDate
, NSDateFormatter
, NSTimeZone
.
Verwandte Fragen und Antworten: https://stackoverflow.com/questions/16254575/how-do-i-get-iso-8601-date-in-ios
Hier ist das Beste, was mir bisher eingefallen ist:
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))
Swift 4 - iOS 11.2.1 oder höher
extension ISO8601DateFormatter {
convenience init(_ formatOptions: Options, timeZone: TimeZone = TimeZone(secondsFromGMT: 0)!) {
self.init()
self.formatOptions = formatOptions
self.timeZone = timeZone
}
}
extension Formatter {
static let iso8601 = ISO8601DateFormatter([.withInternetDateTime, .withFractionalSeconds])
}
extension Date {
var iso8601: String {
return Formatter.iso8601.string(from: self)
}
}
extension String {
var iso8601: Date? {
return Formatter.iso8601.date(from: self)
}
}
Verwendung:
Date().description(with: .current) // Tuesday, February 5, 2019 at 10:35:01 PM Brasilia Summer Time"
let dateString = Date().iso8601 // "2019-02-06T00:35:01.746Z"
if let date = dateString.iso8601 {
date.description(with: .current) // "Tuesday, February 5, 2019 at 10:35:01 PM Brasilia Summer Time"
print(date.iso8601) // "2019-02-06T00:35:01.746Z\n"
}
iOS 9 - Swift 3 oder höher
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
}()
}
Codierbares Protokoll
Wenn Sie dieses Format kodieren und dekodieren müssen, wenn Sie mit dem Codable Protokoll arbeiten, können Sie Ihre eigenen Strategien zur Kodierung/Dekodierung von Daten erstellen:
extension JSONDecoder.DateDecodingStrategy {
static let iso8601withFractionalSeconds = custom {
let container = try $0.singleValueContainer()
let string = try container.decode(String.self)
guard let date = Formatter.iso8601.date(from: string) else {
throw DecodingError.dataCorruptedError(in: container,
debugDescription: "Invalid date: " + string)
}
return date
}
}
und die Kodierungsstrategie
extension JSONEncoder.DateEncodingStrategy {
static let iso8601withFractionalSeconds = custom {
var container = $1.singleValueContainer()
try container.encode(Formatter.iso8601.string(from: $0))
}
}
Spielplatz-Tests
let dates = [Date()] // ["Feb 8, 2019 at 9:48 PM"]
Kodierung
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601withFractionalSeconds
let data = try! encoder.encode(dates)
print(String(data: data, encoding: .utf8)!)
Dekodierung
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601withFractionalSeconds
let decodedDates = try! decoder.decode([Date].self, from: data) // ["Feb 8, 2019 at 9:48 PM"]
Denken Sie daran, das Gebietsschema auf "de_US_POSIX" zu setzen, wie in Technical Q&A1480 beschrieben. In 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))
Das Problem ist, dass das Jahr auf einem Gerät, das einen nicht-gregorianischen Kalender verwendet, nicht mit RFC3339/ISO8601 übereinstimmt, es sei denn, Sie geben sowohl die locale
als auch die timeZone
und die dateFormat
Zeichenfolge an.
Oder Sie können ISO8601DateFormatter
verwenden, um sich die Mühe zu ersparen, locale
und timeZone
selbst zu setzen:
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))
Für die Wiedergabe in Swift 2, siehe frühere Version dieser Antwort.
In meinem Fall muss ich die Spalte DynamoDB - lastUpdated (Unix-Zeitstempel) in Normalzeit konvertieren.
Der ursprüngliche Wert von lastUpdated war: 1460650607601 - umgewandelt in 2016-04-14 16:16:47 +0000 über:
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)
}