Parsing a Iso8601 String to Date in Swift

Parsing a ISO8601 String to Date in Swift

Your date format is incorrect, you need to take into account the milliseconds.

let dateFormatter = DateFormatter()

dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"

let updatedAtStr = "2016-06-05T16:56:57.019+01:00"
let updatedAt = dateFormatter.date(from: updatedAtStr) // "Jun 5, 2016, 4:56 PM"

Just to clarify, the addition of .SSS in the date format is what fixes the problem.

Swift ISO8601 format to Date

You can specify ISO8601 date formate to the NSDateFormatter to get Date:

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyyMMdd'T'HHmmssZ"
print(dateFormatter.date(from: dateString)) //2018-02-07 12:46:00 +0000

Swift: Converting ISO8601 to Date

As @Martin has mentioned in the comments your date doesn't have the fractional seconds at the end of the string.
Here's the right format:

df.dateFormat = "yyyy-MM-dd'T'HH:mm:SSxxxxx"

or remove the withFractionalSeconds from formatOptions array of isoDateFormatter, like this:

isoDateFormatter.formatOptions = [
.withFullDate,
.withFullTime,
.withDashSeparatorInDate]

Update: As @Leo has mentioned in the comments the use of Z for time zone's format is wrong here, it needs to be xxxxx instead.

convert ISO8601 String to reformatted date string(Swift)

ISO8601 has several different options, including a timezone. It appears that by default the ISO8601DateFormatter expects a timezone indicator in the string. You can disable this behaviour by using custom options like so:

let pulledDate = "2017-06-16T13:38:34.601767"
var dateFormatter = ISO8601DateFormatter()
dateFormatter.formatOptions = [.withYear, .withMonth, .withDay, .withTime, .withDashSeparatorInDate, .withColonSeparatorInTime]
let date = dateFormatter.date(from: pulledDate)

If you want to know what are the default options, just run this code in a playground:

let dateFormatter = ISO8601DateFormatter()
let options = dateFormatter.formatOptions
options.contains(.withYear)
options.contains(.withMonth)
options.contains(.withWeekOfYear)
options.contains(.withDay)
options.contains(.withTime)
options.contains(.withTimeZone)
options.contains(.withSpaceBetweenDateAndTime)
options.contains(.withDashSeparatorInDate)
options.contains(.withColonSeparatorInTime)
options.contains(.withColonSeparatorInTimeZone)
options.contains(.withFullDate)
options.contains(.withFullTime)
options.contains(.withInternetDateTime)

Of course, if your string doesn't contain a timezone, the date formatter will still interpret it in a timezone using its timeZone property, which – according to the documentation – defaults to GMT.

Remember to change it before using the formatter if you want to interpret your date in a different timezone:

dateFormatter.timeZone = TimeZone(identifier: "Europe/Paris")

Parse ISO8601 in swift

You didn't set the timezone of the output date, so it's defaulting to your systems' timezone rather than UTC (which is what you were expecting).

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

Swift 4 • iOS 11.2.1 or later

extension ISO8601DateFormatter {
convenience init(_ formatOptions: Options) {
self.init()
self.formatOptions = formatOptions
}
}


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"]

Sample Image



Related Topics



Leave a reply



Submit