it-swarm.com.de

Wie erstellt man einen Datumszeitstempel und ein Format als ISO 8601, RFC 3339, UTC-Zeitzone?

Wie erzeugt man einen Datumszeitstempel unter Verwendung der Formatstandards für ISO 8601 und RFC 3339 ?

Das Ziel ist eine Zeichenfolge, die folgendermaßen aussieht:

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

Format:

  • jahr, Monat, Tag als "XXXX-XX-XX"
  • der Buchstabe "T" als Trennzeichen
  • stunde, Minute, Sekunde, Millisekunden als "XX: XX: XX.XXX".
  • der Buchstabe "Z" als Zonenbezeichner für Nullpunktverschiebung, UTC, GMT, Zulu-Zeit.

I'm besten fall:

  • Schneller Quellcode, der einfach, kurz und unkompliziert ist.
  • Keine Verwendung zusätzlicher Frameworks, Teilprojekte, Cocoapods, C-Codes usw.

Ich habe StackOverflow, Google, Apple usw. gesucht und keine schnelle Antwort darauf gefunden. 

Die vielversprechendsten Klassen sind NSDate , NSDateFormatter , NSTimeZone .

Verwandte Fragen und Antworten: Wie erhalte ich ein ISO 8601-Datum in iOS?

Hier ist das Beste, was ich bisher gefunden habe:

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))
145

Xcode 9 • Swift 4 • iOS 11 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)
    }
}

Verwendungszweck:

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 bei der Arbeit mit Codable .__ codieren und decodieren müssen. Protokoll können Sie Ihre eigenen benutzerdefinierten Strategien zur Datumsverschlüsselung dekodierung 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))
    }
}

Spielplatzprüfung

let dates = [Date()]   // ["Feb 8, 2019 at 9:48 PM"]

codierung

let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601withFractionalSeconds
let data = try! encoder.encode(dates)
String(data: data, encoding: .utf8)! // ["2019-02-08T23:46:12.985Z"]\n"

dekodierung

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601withFractionalSeconds
let decodedDates = try! decoder.decode([Date].self, from: data)  // ["Feb 8, 2019 at 9:48 PM"]

/ - enter image description here

315
Leo Dabus

Denken Sie daran, das Gebietsschema auf en_US_POSIX zu setzen, wie in Technische Fragen und Antworten auf 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))

Wenn Sie sich auf einem Gerät befinden, das einen nicht gregorianischen Kalender verwendet, stimmt das Jahr nicht mit RFC3339/ISO8601 überein, es sei denn, Sie geben die Variable locale sowie die Zeichenfolge timeZone und dateFormat an.

Oder Sie können ISO8601DateFormatter verwenden, um das Unkraut der Einstellung locale und timeZone selbst zu verlassen:

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

Zur Swift-2-Wiedergabe siehe vorherige Überarbeitung dieser Antwort .

24
Rob

Wenn Sie die ISO8601DateFormatter() mit einem Datum aus einem JSON-Feed von Rails 4+ verwenden möchten (und natürlich keine Millis benötigen), müssen Sie einige Optionen für das Formatierungsprogramm festlegen, damit es richtig funktioniert. Andernfalls wird die Funktion date(from: string) zurückgegeben Null. Hier ist was ich verwende:

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

Hier ist das Ergebnis der Verwendung der Optionen, die nicht in einem Spielplatz-Screenshot enthalten sind:

 enter image description here

19
Matt Long

Als weiteres Kompliment an Andrés Torres Marroquín und Leo Dabus habe ich eine Version, die Sekundenbruchteile bewahrt. Ich kann es nirgendwo dokumentiert finden, aber Apple schneidet Sekunden und Sekunden auf die Mikrosekunde (3-stellige Genauigkeit) ab (auch wenn sie mit SSSSSSS im Gegensatz zu Unicode tr35-31 angegeben wurde).

Ich sollte betonen, dass dies ist wahrscheinlich für die meisten Anwendungsfälle nicht notwendigDatumsangaben online erfordern normalerweise keine Millisekundengenauigkeit, und wenn dies der Fall ist, ist es oft besser, ein anderes Datenformat zu verwenden. Aber manchmal muss man auf eine bestimmte Weise mit einem bereits bestehenden System zusammenarbeiten.

Xcode 8/9 und 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
    }
}
4
Eli Burke

Es gibt eine neue Klasse ISO8601DateFormatter, mit der Sie eine Zeichenfolge mit nur einer Zeile erstellen können. Zur Abwärtskompatibilität habe ich eine alte C-Bibliothek verwendet. Ich hoffe das ist nützlich für jemanden.

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)
        }
    }
}
3
Thomas Szabo

In der Zukunft muss das Format möglicherweise geändert werden. Dies kann ein kleiner Kopfschmerz mit Datum sein. Verwenden Sie eine Klasse und ein Protokoll, um die Implementierung zu umschließen. Das Ändern des Aufrufs des Datumszeitformats an einer Stelle wird einfacher. Verwenden Sie RFC3339, wenn möglich, eine vollständigere Darstellung. DateFormatProtocol und DateFormat eignen sich hervorragend für die Injektion von Abhängigkeiten.

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

}
3
Gary Davies

In meinem Fall muss ich die DynamoDB-lastUpdated-Spalte (Unix-Zeitstempel) in Normalzeit konvertieren. 

Der Anfangswert von lastUpdated war: 1460650607601 - konvertiert bis 2016-04-14 16:16:47 +0000 via: 

   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)

            }
3
ioopl

Verwendet ISO8601DateFormatter unter iOS 10 oder neuer.

Verwendet DateFormatter unter iOS 9 oder älter.

Schnell 4

protocol DateFormatterProtocol {
    func string(from date: Date) -> String
    func date(from string: String) -> Date?
}

extension DateFormatter: DateFormatterProtocol {}

@available(iOS 10.0, *)
extension ISO8601DateFormatter: DateFormatterProtocol {}

struct DateFormatterShared {
    static let iso8601: DateFormatterProtocol = {
        if #available(iOS 10, *) {
            return ISO8601DateFormatter()
        } else {
            // iOS 9
            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
        }
    }()
}
2
neoneye

Um die Version von Leo Dabus zu ergänzen, habe ich Unterstützung für Projekte hinzugefügt, die mit Swift und Objective-C geschrieben wurden. Außerdem wurde Unterstützung für die optionalen Millisekunden hinzugefügt.

Xcode 8 und 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
    }
}

Ohne einige manuelle String-Masken oder TimeFormatters  

import Foundation

struct DateISO: Codable {
    var date: Date
}

extension Date{
    var isoString: String {
        let encoder = JSONEncoder()
        encoder.dateEncodingStrategy = .iso8601
        guard let data = try? encoder.encode(DateISO(date: self)),
        let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as?  [String: String]
            else { return "" }
        return json?.first?.value ?? ""
    }
}

let dateString = Date().isoString
0
Dmitrii Z