web-dev-qa-db-fra.com

ISO8601DateFormatter n'analyse pas la chaîne de date ISO

J'essaye d'analyser ça

2017-01-23T10: 12: 31.484Z

en utilisant natif ISO8601DateFormatter classe fournie par iOS 10 mais échoue toujours. Si la chaîne ne contient pas de millisecondes, l'objet Date est créé sans problème.

J'ai essayé ceci et beaucoup de combinaisons options mais échoue toujours ...

let formatter = ISO8601DateFormatter()
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.formatOptions = [.withInternetDateTime, .withDashSeparatorInDate, .withColonSeparatorInTime, .withColonSeparatorInTimeZone, .withFullTime]

Une idée? Merci!

21
mhergon

Avant macOS 10.13/iOS 11 ISO8601DateFormatter ne prend pas en charge les chaînes de date, y compris les millisecondes.

Une solution de contournement consiste à supprimer la partie en millisecondes avec une expression régulière.

let isoDateString = "2017-01-23T10:12:31.484Z"
let trimmedIsoString = isoDateString.replacingOccurrences(of: "\\.\\d+", with: "", options: .regularExpression)
let formatter = ISO8601DateFormatter()
let date = formatter.date(from: trimmedIsoString)

Dans macOS 10.13+/iOS 11+, une nouvelle option est ajoutée pour prendre en charge les fractions de seconde:

static var withFractionalSeconds: ISO8601DateFormatter.Options { get }

let isoDateString = "2017-01-23T10:12:31.484Z"
let formatter = ISO8601DateFormatter()
formatter.formatOptions =  [.withInternetDateTime, .withFractionalSeconds]
let date = formatter.date(from: isoDateString)
45
vadian

Pour les personnes qui ne sont pas encore prêtes à passer à iOS 11, vous pouvez toujours créer votre propre formateur pour gérer les millisecondes, par exemple:

extension DateFormatter {
    static var iSO8601DateWithMillisec: DateFormatter {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
        return dateFormatter
    }
}

Usage:

let formater = DateFormatter.iSO8601DateWithMillisec
let date = formater.date(from: "2017-01-23T10:12:31.484Z")!
print(date) // output: 2017-01-23 10:12:31 +0000

Il est légèrement plus élégant que d'écrire une expression régulière pour éliminer les millisecondes de la chaîne d'entrée.

2
Yuchen Zhong

Peut-être que cela aidera à décoder des formats légèrement différents:

extension JSONDecoder {
    enum DateDecodeError: String, Error {
        case invalidDate
    }

    static var bestDateAttemptDecoder: JSONDecoder {
        let decoder = JSONDecoder()
        decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
            let container = try decoder.singleValueContainer()
            if let dateSecs = try? container.decode(Double.self) {
                return Date(timeIntervalSince1970: dateSecs)
            }

            if let dateSecs = try? container.decode(UInt.self) {
                return Date(timeIntervalSince1970: TimeInterval(dateSecs))
            }

            let dateStr = try container.decode(String.self)
            let isoFormatter = ISO8601DateFormatter()
            isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
            if let date = isoFormatter.date(from: dateStr) {
                return date
            }

            isoFormatter.formatOptions = [.withInternetDateTime ]
            if let date = isoFormatter.date(from: dateStr) {
                return date
            }

            log.warning("Cannot decode date");
            throw DateDecodeError.invalidDate
        })

        return decoder
    }
}

De: https://Gist.github.com/th3m477/442a0d1da6354dd3b84e3b71df5dca6a

1
mm282