Swift 5.5 • iOS 15 • Xcode 13 or later
extension Date.ISO8601FormatStyle {
static let iso8601withFractionalSeconds: Self = .init(includingFractionalSeconds: true)
}
extension ParseStrategy where Self == Date.ISO8601FormatStyle {
static var iso8601withFractionalSeconds: Date.ISO8601FormatStyle { .iso8601withFractionalSeconds }
}
extension FormatStyle where Self == Date.ISO8601FormatStyle {
static var iso8601withFractionalSeconds: Date.ISO8601FormatStyle { .iso8601withFractionalSeconds }
}
extension Date {
init(iso8601withFractionalSeconds parseInput: ParseStrategy.ParseInput) throws {
try self.init(parseInput, strategy: .iso8601withFractionalSeconds)
}
var iso8601withFractionalSeconds: String {
formatted(.iso8601withFractionalSeconds)
}
}
extension String {
func iso8601withFractionalSeconds() throws -> Date {
try .init(iso8601withFractionalSeconds: self)
}
}
extension JSONDecoder.DateDecodingStrategy {
static let iso8601withFractionalSeconds = custom {
try .init(iso8601withFractionalSeconds: $0.singleValueContainer().decode(String.self))
}
}
extension JSONEncoder.DateEncodingStrategy {
static let iso8601withFractionalSeconds = custom {
var container = $1.singleValueContainer()
try container.encode($0.iso8601withFractionalSeconds)
}
}
Usage:
let date: Date = .now // "19 Nov 2023 at 11:29 PM"
date.description(with: .current) // "Sunday, 19 November 2023 at 11:29:40 PM Brasilia Standard Time"
let dateString = date.iso8601withFractionalSeconds // "2023-11-20T02:29:40.920Z"
if let date = try? dateString.iso8601withFractionalSeconds() {
date.description(with: .current) // "Sunday, 19 November 2023 at 11:29:40 PM Brasilia Standard Time"
print(date.iso8601withFractionalSeconds) // "2023-11-20T02:29:40.920Z\n"
}
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"
}
let dates: [Date] = [.now]
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601withFractionalSeconds
let data = try! encoder.encode(dates)
print(String(data: data, encoding: .utf8)!) // "["2023-11-20T02:11:29.158Z"]\n"
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601withFractionalSeconds
let decodedDates = try! decoder.decode([Date].self, from: data)
print(decodedDates) // "[2023-11-20 02:11:29 +0000]\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"]