1

I'm hitting a webservice that is returning a string in the following format:

"yyyy-MM-dd'T'HH:mmZ"

It's very close to the standard UTC format, but just without the ss at the end. My issue is that my dateFormatter is always returning nil...and I have tried to make sure that locale and everything else is setup properly.

Here is an example of an actual string:

2019-12-26T00:00Z

Here is the code that creates the DF:

extension DateFormatter {
    @objc static func defaultDateFormat(_ format: String) -> DateFormatter {
        let formatter = DateFormatter()
        formatter.locale = Locale(identifier: "US")
        formatter.dateFormat = format
        return formatter
    }

    func date(from string: String?) -> Date? {
        if let string = string {
            return self.date(from: string)
        } else {
            return nil
        }
    }

    func string(fromOptional date: Date?) -> String? {
        if let date = date {
            return self.string(from: date)
        } else {
            return nil
        }
    }

    func string(fromOptional date: Date?, defaultStr: String) -> String {
        return self.string(fromOptional: date) ?? defaultStr
    }
}

let df = DateFormatter.defaultDateFormat("yyyy-MM-dd'T'HH:mmZ")
let date: Date? = df.date(from: __dateString__) // always nil
3
  • 1
    Also it's advised to set the time zone because of problems with daylight saving time changes. Commented Oct 15, 2019 at 22:40
  • 1
    By the way, there is no locale US. Commented Oct 15, 2019 at 22:41
  • thanks! I've changed that but am still seeing nil everytime. Any thoughts? Commented Oct 15, 2019 at 22:45

1 Answer 1

4

A few observations:

  1. You want to use a locale of en_US_POSIX, an invariant locale that always works for ISO 8601 / RFC 3339 date strings. See Technical Q&A 1480.

  2. If you want to use this formatter to convert a date back to a string like 2019-12-26T00:00Z, you will want to:

    • Use X (or ZZZZZ or XXXXX) in your format string, not Z (see the “zone” section in the table at Date Format Patterns and you’ll see how these various time zone patterns, e.g. Z, ZZZZZ, and X, are interpreted); and
    • Set the timeZone of the formatter to use Zulu/GMT/UTC.

Thus:

@objc static func defaultDateFormat(_ format: String) -> DateFormatter {
    let formatter = DateFormatter()
    formatter.locale = Locale(identifier: "en_US_POSIX")
    formatter.timeZone = TimeZone(secondsFromGMT: 0)
    formatter.dateFormat = format
    return formatter
}

And

let df = DateFormatter.defaultDateFormat("yyyy-MM-dd'T'HH:mmX")
let date = df.date(from: "2019-12-26T00:00Z")

Or

let string = df.string(from: Date())
Sign up to request clarification or add additional context in comments.

3 Comments

Amazing! Can you help me understand the utility of 'X' and setting the timeZone as such?
Try it with Z and see what you get when you convert from date back to string. You’d get something like 2019-10-15T22:57+0000, which is not what you want. See the “zone” section in the table at Date Format Strings and you’ll see how Z, ZZZZZ, and X are interpreted.
Re the setting of the timeZone, try removing that line and again see what happens when you generate a string from a date. You’ll get a string in your local timezone (e.g. without timezone, if I generate a string on my computer, it says 2019-10-15T16:02-07:00), whereas your server is likely assuming it will be in Zulu/GMT/UTC, designated with the Z, not -07:00 (or whatever your local timezone will be).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.