DateFormatter is used for converting string representation of date and time to a Date type and visa-versa. Something to be aware of is that the conversion loses microseconds precision. This is extremely important if we use these Date values for sorting and therefore ending up with incorrect order. Let’s consider an iOS app which uses API for fetching a list of items and each of the item contains a timestamp used for sorting the list. Often, these timestamps have the ISO8601 format like 2024-09-21T10:32:32.113123Z. Foundation framework has a dedicated formatter for parsing these strings: ISO8601DateFormatter. It is simple to use:
Great, but there is on caveat, it ignores microseconds. Fortunately this can be fixed by manually parsing microseconds and adding the missing precision to the converted Date value. Here is an example, how to do this using an extension.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters extension ISO8601DateFormatter { func microsecondsDate(from dateString: String) -> Date? { guard let millisecondsDate = date(from: dateString) else { return nil } guard let fractionIndex = dateString.lastIndex(of: ".") else { return millisecondsDate } guard let tzIndex = dateString.lastIndex(of: "Z") else { return millisecondsDate } guard let startIndex = dateString.index(fractionIndex, offsetBy: 4, limitedBy: tzIndex) else { return millisecondsDate } // Pad the missing zeros at the end and cut off nanoseconds let microsecondsString = dateString[startIndex..<tzIndex].padding(toLength: 3, withPad: "0", startingAt: 0) guard let microseconds = TimeInterval(microsecondsString) else { return millisecondsDate } return Date(timeIntervalSince1970: millisecondsDate.timeIntervalSince1970 + microseconds / 1_000_000.0) } } view raw ISO8601.swift hosted with ❤ by GitHubThat this code does is first converting the string using the original date(from:) method, followed by manually extracting digits for microseconds by handling cases where there are less than 3 digits or event there are nanoseconds present. Lastly a new Date value is created with the microseconds precision. Here are examples of the output (note that float’s precision comes into play).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters let dateStrings = [ "2024-09-21T10:32:32.113Z", "2024-09-21T10:32:32.1131Z", "2024-09-21T10:32:32.11312Z", "2024-09-21T10:32:32.113123Z", "2024-09-21T10:32:32.1131234Z", "2024-09-21T10:32:32.11312345Z", "2024-09-21T10:32:32.113123456Z" ] let dates = dateStrings.compactMap(formatter.microsecondsDate(from:)) for (string, date) in zip(dateStrings, dates) { print(string, "->", date.timeIntervalSince1970) } /* 2024-09-21T10:32:32.113Z -> 1726914752.113 2024-09-21T10:32:32.1131Z -> 1726914752.1130998 2024-09-21T10:32:32.11312Z -> 1726914752.1131198 2024-09-21T10:32:32.113123Z -> 1726914752.113123 2024-09-21T10:32:32.1131234Z -> 1726914752.113123 2024-09-21T10:32:32.11312345Z -> 1726914752.113123 2024-09-21T10:32:32.113123456Z -> 1726914752.113123 */ view raw ISO8601.swift hosted with ❤ by GitHubIf this was helpful, please let me know on Mastodon@toomasvahter or Twitter @toomasvahter. Feel free to subscribe to RSS feed. Thank you for reading.
Support me on Patreon Donate with Paypal Buy me a coffee