0

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?

enter image description here

Zin Win Htet
  • 2,448
  • 4
  • 32
  • 54
  • Possible duplicate of [Convert server UTC time to local time and vice-versa](https://stackoverflow.com/questions/42803349/swift-3-0-convert-server-utc-time-to-local-time-and-vice-versa) – Daniel Oct 02 '19 at 04:24
  • Quote the `T`. `"yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ"`. And there's no need to set the formatter's `timeZone` in this case because the string provides its own time zone information. – rmaddy Oct 02 '19 at 04:26
  • @Daniel, I've seen that post and other posts several times but still not working. – Zin Win Htet Oct 02 '19 at 04:26
  • @rmaddy, Quoting T still nil – Zin Win Htet Oct 02 '19 at 04:32
  • 1
    Replace the `timeZone` line with `dateFormatter.locale = Locale(identifier: "en_US_POSIX")`. – rmaddy Oct 02 '19 at 04:38
  • still not working – Zin Win Htet Oct 02 '19 at 04:44
  • Then your string isn't what you believe it is. Your code works just fine for me with the two changes I stated. I tested with the string you posted in your question. Perhaps your actual string has some white space / newlines in it. – rmaddy Oct 02 '19 at 04:55
  • @rmaddy, check my post. I've edited. My string is correct. – Zin Win Htet Oct 02 '19 at 05:10
  • Add the following line to your method: `print(Array(date.unicodeScalars).map { $0.value })` and post the output. This will verify if there are any unexpected characters. It should give `[50, 48, 49, 57, 45, 48, 57, 45, 51, 48, 84, 48, 57, 58, 49, 48, 58, 51, 50, 46, 53, 51, 55, 50, 52, 52, 90]` for the string `"2019-09-30T09:10:32.537244Z"` – rmaddy Oct 02 '19 at 05:18
  • the Array is the same, but something is seriously wrong. It is still being nil. May be because of Simulator? I have no idea. – Zin Win Htet Oct 02 '19 at 07:34

1 Answers1

3

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.

Rob
  • 415,655
  • 72
  • 787
  • 1,044