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
, andwithTimeZone
.
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
Firebase Queryequaltovalue with Childkey
Can an iOS App Switch the Device to Silent Mode
How to Access a _Block Variable, After the Block Has Completed
Seed and Maintain Cloudkit Public Database Without Requiring Icloud Login
Fairplay Streaming: Calling Copypixelbufferforitemtime on Avplayeritemvideooutput Returns Null
Incorrect Height Calculation with Draggesture() in Swiftui
"Invalid Predicate: Nil Rhs" for Second Argument in Nspredicate Format
Application Identifier Entitlement Value Has Changed
Get Responseobject on Failure Block Afnetworking 3.0
Following in App Purchase, App Crashing on Startup. Productidentifier=Nil
Set<Nsobject>' Does Not Have a Member Named 'Anyobject." - Xcode 6.3
How to Programmatically Get iOS's Alphanumeric Version String
Detect If iOS App Is Downloaded from Apple's Testflight