How to make ISO 8601 date in Swift

Issue #479

From ISO8601 spec, the problems are the representation and time zone

1
2
3
4
5
6
7
ISO 8601 = year-month-day time timezone
For date and time, there are basic (YYYYMMDD, hhmmss, ...) and extended format (YYYY-MM-DD, hh:mm:ss, ...)
Time zone can be Zulu, offset or GMT
Separator for date and time can be space, or T
There are week format for date, but it is rarely used
Timezone can be a lot of spaces after
Second is optional

Here are some valid strings

1
2
3
4
5
2016-04-08T10:25:30Z
2016-04-08 11:25:30+0100
2016-04-08 202530GMT+1000
20160408 08:25:30-02:00
2016-04-08 11:25:30 +0100

Solutions

Use NSDateFormatter and normalize the date string.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public var stringToDateFormatter: DateFormatter = {
let formatter = Foundation.DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "yyyyMMdd HHmmssZ"

return formatter
}()

public func date(string: String) -> Date? {
var basicString = string

if let regex = try? NSRegularExpression(pattern: "[0-9]{4}-[0-9]{2}-[0-9]{2}", options: []),
let result = regex.firstMatch(in: string, options: .anchored, range: NSMakeRange(0, string.characters.count)) {
basicString = (basicString as NSString).replacingOccurrences(of: "-", with: "", options: [], range: result.range)
}

basicString = basicString
.replacingOccurrences(of: ":", with: "")
.replacingOccurrences(of: "GMT", with: "")
.replacingOccurrences(of: "T", with: " ")
.replacingOccurrences(of: ",", with: ".")

return stringToDateFormatter.date(from: basicString)
?? stringToDateMillisecondsFormatter.date(from: basicString)
}

So here is the format that I’m using in my ISO8601

1
2
3
let formatter = NSDateFormatter()
formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
formatter.dateFormat = "yyyyMMdd HHmmssZ"

About the Z identifier Date Field Symbol Table

Z: The ISO8601 basic format with hours, minutes and optional seconds fields. The format is equivalent to RFC 822 zone format (when optional seconds field is absent)

About locale Formatting Data Using the Locale Settings

Locales represent the formatting choices for a particular user, not the user’s preferred language. These are often the same but can be different. For example, a native English speaker who lives in Germany might select English as the language and Germany as the region

About en_US_POSIX Technical Q&A QA1480 NSDateFormatter and Internet Dates

On the other hand, if you’re working with fixed-format dates, you should first set the locale of the date formatter to something appropriate for your fixed format. In most cases the best locale to choose is “en_US_POSIX”, a locale that’s specifically designed to yield US English results regardless of both user and system preferences.

“en_US_POSIX” is also invariant in time (if the US, at some point in the future, changes the way it formats dates, “en_US” will change to reflect the new behaviour, but “en_US_POSIX” will not), and between machines (“en_US_POSIX” works the same on iOS as it does on OS X, and as it it does on other platforms).

NSISO8601DateFormatter from iOS 10

From iOS 10, we can use NSISO8601DateFormatter

The NSISO8601DateFormatter class generates and parses string representations of dates following the ISO 8601 standard. Use this class to create ISO 8601 representations of dates and create dates from text strings in ISO 8601 format.

Code

Comments