ISO8601DateFormatter不解析ISO日期字符串

时间:2017-01-25 09:19:40

标签: ios swift date swift3 ios10

我正在尝试解析此

  

2017-01-23T10:12:31.484Z

使用ISO8601DateFormatter提供的原生iOS 10类,但总是失败。 如果字符串不包含毫秒,则创建Date对象时没有问题。

我尝试了这个和许多options组合,但总是失败......

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

有什么想法吗? 谢谢!

4 个答案:

答案 0 :(得分:41)

在macOS 10.13 / iOS 11之前ISO8601DateFormatter不支持日期字符串,包括毫秒。

解决方法是使用正则表达式删除毫秒部分。

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)

在macOS 10.13+ / iOS 11+中添加了一个新选项以支持小数秒:

  

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)

答案 1 :(得分:0)

几个月前,我遇到了同样的问题。这是我的参考解决方案:

// *****************************************
// MARK: - Formatter extension
// *****************************************
extension Formatter {
    static let iso8601: ISO8601DateFormatter = {
        let formatter = ISO8601DateFormatter()
        formatter.timeZone = TimeZone.current 
        formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
        return formatter
    }()
    static let iso8601NoSecond: ISO8601DateFormatter = {
        let formatter = ISO8601DateFormatter()
        formatter.timeZone = TimeZone.current 
        formatter.formatOptions = [.withInternetDateTime]
        return formatter
    }()
}

// *****************************************
// MARK: - ISO8601 helper
// *****************************************
    func getDateFrom(DateString8601 dateString:String) -> Date?
    {
        if let date = Formatter.iso8601.date(from: dateString)  {
            return date
        }
        if let date = Formatter.iso8601NoSecond.date(from: dateString)  {
            return date
        }
        return nil
    }

// *****************************************
// usage
// *****************************************
    let d = getDateFrom(DateString8601: "2017-01-23T10:12:31.484Z")
    print("2017-01-23T10:12:31.484Z millis= ", d?.timeIntervalSinceReferenceDate)

    let d2 = getDateFrom(DateString8601: "2017-01-23T10:12:31Z")
    print("2017-01-23T10:12:31Z millis= ", d2?.timeIntervalSinceReferenceDate)


// *****************************************
// result
// *****************************************
2017-01-23T10:12:31.484Z millis=  Optional(506859151.48399997)
2017-01-23T10:12:31Z millis=  Optional(506859151.0)

答案 2 :(得分:0)

对于尚未准备好使用iOS 11的用户,您始终可以创建自己的格式化程序来处理毫秒,例如:

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

答案 3 :(得分:-1)

也许这将有助于解码略有不同的格式:

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

发件人:https://gist.github.com/th3m477/442a0d1da6354dd3b84e3b71df5dca6a