How can I parse / create a date time stamp formatted with fractional seconds UTC timezone (ISO 8601, RFC 3339) in Swift?

后端 未结 12 1719
北恋
北恋 2020-11-21 22:43

How to generate a date time stamp, using the format standards for ISO 8601 and RFC 3339?

The goal is a string that looks like this:

\"2015-01-01T00:0         


        
12条回答
  •  无人及你
    2020-11-21 23:47

    Swift 4 • iOS 11.2.1 or later

    extension ISO8601DateFormatter {
        convenience init(_ formatOptions: Options, timeZone: TimeZone = TimeZone(secondsFromGMT: 0)!) {
            self.init()
            self.formatOptions = formatOptions
            self.timeZone = timeZone
        }
    }
    

    extension Formatter {
        static let iso8601withFractionalSeconds = ISO8601DateFormatter([.withInternetDateTime, .withFractionalSeconds])
    }
    

    extension Date {
        var iso8601withFractionalSeconds: String { return Formatter.iso8601withFractionalSeconds.string(from: self) }
    }
    

    extension String {
        var iso8601withFractionalSeconds: Date? { return Formatter.iso8601withFractionalSeconds.date(from: self) }
    }
    

    Usage:

    Date().description(with: .current)  //  Tuesday, February 5, 2019 at 10:35:01 PM Brasilia Summer Time"
    let dateString = Date().iso8601withFractionalSeconds   //  "2019-02-06T00:35:01.746Z"
    
    if let date = dateString.iso8601withFractionalSeconds {
        date.description(with: .current) // "Tuesday, February 5, 2019 at 10:35:01 PM Brasilia Summer Time"
        print(date.iso8601withFractionalSeconds)           //  "2019-02-06T00:35:01.746Z\n"
    }
    

    iOS 9 • Swift 3 or later

    extension Formatter {
        static let iso8601withFractionalSeconds: 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
        }()
    }
    

    Codable Protocol

    If you need to encode and decode this format when working with Codable protocol you can create your own custom date encoding/decoding strategies:

    extension JSONDecoder.DateDecodingStrategy {
        static let iso8601withFractionalSeconds = custom {
            let container = try $0.singleValueContainer()
            let string = try container.decode(String.self)
            guard let date = Formatter.iso8601withFractionalSeconds.date(from: string) else {
                throw DecodingError.dataCorruptedError(in: container,
                      debugDescription: "Invalid date: " + string)
            }
            return date
        }
    }
    

    and the encoding strategy

    extension JSONEncoder.DateEncodingStrategy {
        static let iso8601withFractionalSeconds = custom {
            var container = $1.singleValueContainer()
            try container.encode(Formatter.iso8601withFractionalSeconds.string(from: $0))
        }
    }
    

    Playground Testing

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

    encoding

    let encoder = JSONEncoder()
    encoder.dateEncodingStrategy = .iso8601withFractionalSeconds
    let data = try! encoder.encode(dates)
    print(String(data: data, encoding: .utf8)!)
    

    decoding

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

提交回复
热议问题