Wie erstellt man einen Datums-Zeitstempel und formatiert ihn als ISO 8601, RFC 3339, UTC-Zeitzone?

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:

  • Jahr, Monat, Tag, als "XXXX-XX-XX"
  • der Buchstabe "T" als Trennzeichen
  • Stunde, Minute, Sekunden, Millisekunden, als "XX:XX:XX.XXX".
  • der Buchstabe "Z" als Zonenbezeichner für Nullpunktverschiebung, auch bekannt als UTC, GMT, Zulu-Zeit.

Bester Fall:

  • Swift-Quellcode, der einfach, kurz und überschaubar ist.
  • Sie brauchen kein zusätzliches Framework, Unterprojekt, Cocoapod, C-Code usw. zu verwenden.

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))
Lösung

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"]

Kommentare (16)

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.

Kommentare (2)

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)

            }
Kommentare (0)