简体   繁体   English

ISO8601DateFormatter不解析ISO日期字符串

[英]ISO8601DateFormatter doesn't parse ISO date string

I'm trying to parse this 我正在尝试解析这个问题

2017-01-23T10:12:31.484Z 2017-01-23T10:12:31.484Z

using native ISO8601DateFormatter class provided by iOS 10 but always fails. 使用iOS 10提供的本机ISO8601DateFormatter类但总是失败。 If the string not contains milliseconds, the Date object is created without problems. 如果字符串不包含毫秒,则创建Date对象时没有问题。

I'm tried this and many options combination but always fails... 我试过这个和许多options组合,但总是失败...

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

Any idea? 任何想法? Thanks! 谢谢!

Prior to macOS 10.13 / iOS 11 ISO8601DateFormatter does not support date strings including milliseconds. 在macOS 10.13 / iOS 11之前, ISO8601DateFormatter不支持日期字符串,包括毫秒。

A workaround is to remove the millisecond part with regular expression. 解决方法是使用正则表达式删除毫秒部分。

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)

In macOS 10.13+ / iOS 11+ a new option is added to support fractional seconds: 在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)

For people that are not ready to go to iOS 11 yet, you can always create your own formatter to handle milliseconds, eg: 对于尚未准备好进入iOS 11的用户,您始终可以创建自己的格式化程序来处理毫秒,例如:

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

It is slightly more elegant than writing a regex to strip out the milliseconds from the input string. 它比写正则表达式从输入字符串中去掉毫秒更优雅。

Maybe this will help to decode slightly different formats: 也许这有助于解码略有不同的格式:

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

From: https://gist.github.com/th3m477/442a0d1da6354dd3b84e3b71df5dca6a 来自: https//gist.github.com/th3m477/442a0d1da6354dd3b84e3b71df5dca6a

I encountered same issue some months ago. 几个月前我遇到过同样的问题。 And here's my solution for reference: 这是我的解决方案供参考:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM