Iso8601Dateformatter Doesn't Parse Iso Date String

ISO8601DateFormatter doesn't parse ISO date string

Prior to macOS 10.13 / iOS 11 ISO8601DateFormatter does not support date strings including milliseconds.

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:

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)

ISO8601DateFormatter not converting a string to date

The designated initialiser init() of ISO8601DateFormatter does this:

By default, a formatter is initialized to use the GMT time zone, the RFC 3339 standard format ("yyyy-MM-dd'T'HH:mm:ssZZZZZ"), and the following options: withInternetDateTime, withDashSeparatorInDate, withColonSeparatorInTime, and withTimeZone.

Obviously, your date is not in the format yyyy-MM-dd'T'HH:mm:ssZZZZZ, because it has a milliseconds component.

You just need to add the withFractionalSeconds option:

let dateString = "2020-03-09T11:53:39.474Z"
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [
.withInternetDateTime,
.withFractionalSeconds,
.withColonSeparatorInTime,
.withDashSeparatorInDate,
.withTimeZone]
if let date = formatter.date(from: dateString) {
print(date)
} else {
print("Could not convert date")
}

I personally like to list the options all out to make the code clearer, but you can also do:

formatter.formatOptions.insert(.withFractionalSeconds)

swift 4.1 ISO8601DateFormatter doesn't parse dates passed by MRAID ad content

Just use a regular DateFormatter with the needed dateFormat of yyyy-MM-dd'T'HH:mmZZZZZ and be sure to set the formatter's locale to en_US_POSIX.

If you get date strings in multiple possible formats then try one format, if that returns nil, try the next possible format. Repeat for all the possible formats you have to handle.

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.

Converting ISO 8601-compliant String to java.util.Date

Unfortunately, the time zone formats available to SimpleDateFormat (Java 6 and earlier) are not ISO 8601 compliant. SimpleDateFormat understands time zone strings like "GMT+01:00" or "+0100", the latter according to RFC # 822.

Even if Java 7 added support for time zone descriptors according to ISO 8601, SimpleDateFormat is still not able to properly parse a complete date string, as it has no support for optional parts.

Reformatting your input string using regexp is certainly one possibility, but the replacement rules are not as simple as in your question:

  • Some time zones are not full hours off UTC, so the string does not necessarily end with ":00".
  • ISO8601 allows only the number of hours to be included in the time zone, so "+01" is equivalent to "+01:00"
  • ISO8601 allows the usage of "Z" to indicate UTC instead of "+00:00".

The easier solution is possibly to use the data type converter in JAXB, since JAXB must be able to parse ISO8601 date string according to the XML Schema specification. javax.xml.bind.DatatypeConverter.parseDateTime("2010-01-01T12:00:00Z") will give you a Calendar object and you can simply use getTime() on it, if you need a Date object.

You could probably use Joda-Time as well, but I don't know why you should bother with that (Update 2022; maybe because the entire javax.xml.bind section is missing from Android's javax.xml package).

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


Related Topics



Leave a reply



Submit