How to Get an Iso 8601 Date on iOS

How do I get an ISO 8601 date on iOS?

Use NSDateFormatter:

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];   
NSLocale *enUSPOSIXLocale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
[dateFormatter setLocale:enUSPOSIXLocale];
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"];
[dateFormatter setCalendar:[NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian]];

NSDate *now = [NSDate date];
NSString *iso8601String = [dateFormatter stringFromDate:now];

And in Swift:

let dateFormatter = DateFormatter()
let enUSPosixLocale = Locale(identifier: "en_US_POSIX")
dateFormatter.locale = enUSPosixLocale
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
dateFormatter.calendar = Calendar(identifier: .gregorian)

let iso8601String = dateFormatter.string(from: Date())

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.

Converting ISO8601 Date Format to local time (iOS)

2015-03-20T20:00:00-07:00 is 8pm Pacific Daylight Time.

If you're representing 1pm PDT, that's either

  • 2015-03-20T13:00:00-07:00

or represent that in "Zulu" (i.e. GMT/UTC)

  • 2015-03-20T20:00:00Z

When working with a web service, the latter is the common convention for ISO 8601 dates. Then, when you present it to the user, you present it to them in their local timezone (using a NSDateFormatter with its default timeZone setting.


Note, when using NSDateFormatter to prepare ISO 8601 dates, you will want to ensure that you specify a locale of en_US_POSIX as outlined in Technical Q&A QA1480. When designing app for US audience this isn't critical, but it's best practice in case the user is not using a gregorian calendar on their device.

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

change date format iso-8601 to custom format

When you are decoding a Date the decoder expects an UNIX timestamp (a Double) by default, this is what the error message tells you.

However you can indeed decode an ISO8601 string as Date if you add the decoder.dateDecodingStrategy = .iso8601 but this decodes only standard ISO8601 strings without milliseconds.

There are two options:

  1. Add a formatted dateDecodingStrategy with a DateFormatter.

    let dateFormatter = DateFormatter()
    dateFormatter.locale = Locale(identifier: "en_US_POSIX")
    dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
    let decoder = JSONDecoder()
    decoder.dateDecodingStrategy = .formatted(dateFormatter)
    try decoder.decode(...
  2. Declare timestamp as

    let timestamp: String

    and convert the string back and forth with two formatters or two date formats in dateString.



Related Topics



Leave a reply



Submit