Reputation: 2585
I've tried some similar questions and answers but still not working.
I'm trying to convert 2019-09-30T09:10:32.537244Z
func serverToLocal(date:String) -> Date? {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-ddTHH:mm:ss.SSSSSSZ"
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
let localDate = dateFormatter.date(from: date)
return localDate
}
Above method always return nil. What's wrong with my date format?
Upvotes: 0
Views: 291
Reputation: 438437
Obviously, the key issue is the absence of the quotes around the T
.
But you probably don’t want to instantiate a new date formatter every time you call this routine (because this is a notoriously computationally expensive process). So you should save your date formatter as a property. This way you don’t need to instantiate a new formatter for every string you want to convert to a Date
. Likewise this formatter can be used to convert dates back to strings in the format of 2019-09-30T09:10:32.537244Z
.
As such, contrary to advice provided elsewhere, I would suggest setting the timeZone
of the formatter. It’s not needed when converting from strings to dates (because the date string contains the time zone qualifier), but when going back from dates to strings, you really do need the date formatter to specify the time zone, or else the string representation of a date will be in the device’s local timezone. This way, your formatter still works for both converting strings to dates, as well as back to strings.
Likewise, I’d suggest replacing the Z
with either ZZZZZ
or just X
. Again, this is only critical if the date formatter is also being used to convert dates back to strings (otherwise, the resulting string will have +0000
rather than Z
in it). Bottom line, I find using X
/ZZZZZ
means that I just never have to worry about it, it works when the formatter is converting 2019-09-30T09:10:32.537244Z
to a Date
object, or vice versa.
Thus:
let iso8601DateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSX"
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.locale = Locale(identifier: "en_US_POSIX")
return formatter
}()
func serverToLocal(string: String) -> Date? {
return iso8601DateFormatter.date(from: string)
}
func localToServer(date: Date) -> String {
return iso8601DateFormatter.string(from: date)
}
By the way, you talk about “server” date strings. Are you exchanging data via JSON? If you are parsing strings in JSON with JSONEncoder
and JSONDecoder
, you might use your formatter in the encoder's dateEncodingStrategy
or the decoder's dateDecodingStrategy
. That way, your underlying model objects can just be Date
properties, and the JSON encoder/decoder will take care of converting the date strings in the JSON to Date
objects (and back). And then you don’t need these serverToLocal
and localToServer
methods at all.
FWIW, for more information about quoting the T
, setting the locale, etc., please see Apple’s Technical Q&A 1480. It’s old doc and is focused on Objective-C, but it talks about the concepts outlined here.
Upvotes: 3