Swift - Converting JSON Date to Swift Compatible Date

Swift - Converting JSON date to Swift compatible date

The issue is that str and date2 two are not the same date format. str format is "yyyy-MM-dd'T'HH:mm:ssZ" while date2 format is "yyyy-MM-dd'T'HH:mm:ss.SSSZ". Besides that you should always set your dateFormatter's locale to "en_US_POSIX" when parsing fixed-format dates:

let date2 = "2015-05-15T21:58:00.066Z"

let dateFormatter = DateFormatter()
dateFormatter.locale = .init(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
if let date = dateFormatter.date(from: date2) {
print(date) // "2015-05-15 21:58:00 +0000"
}

How do I format JSON Date String with Swift?

Use the following string format to convert a server string into a Date

dateFor.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"

Convert string to date in Swift

  • Convert the ISO8601 string to date

      let isoDate = "2016-04-14T10:44:00+0000"

    let dateFormatter = DateFormatter()
    dateFormatter.locale = Locale(identifier: "en_US_POSIX") // set locale to reliable US_POSIX
    dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
    let date = dateFormatter.date(from:isoDate)!
  • Get the date components for year, month, day and hour from the date

      let calendar = Calendar.current
    let components = calendar.dateComponents([.year, .month, .day, .hour], from: date)
  • Finally create a new Date object and strip minutes and seconds

      let finalDate = calendar.date(from:components)

Consider also the convenience formatter ISO8601DateFormatter introduced in iOS 10 / macOS 10.12:

let isoDate = "2016-04-14T10:44:00+0000"

let dateFormatter = ISO8601DateFormatter()
let date = dateFormatter.date(from:isoDate)!

Convert JavaScript date to Swift JSON timeIntervalSinceReferenceDate format

The default Swift JSON encoding outputs a value which is the number of seconds that have passed since ReferenceDate. https://developer.apple.com/documentation/foundation/jsonencoder/2895363-dateencodingstrategy

It seems ReferenceDate is 00:00:00 UTC on 1 January 2001.
https://developer.apple.com/documentation/foundation/nsdate/1409769-init

function dateToSwiftInterval(date: Date): number {
const referenceDate = Date.UTC(2001,0,1);
const timeSpanMs = (date - referenceDate);
return timeSpanMs / 1000;
}
const myDate = new Date(1598366769000);
console.log(dateToSwiftValue(myDate)); // 620102769

How to convert a date string with optional fractional seconds using Codable in Swift?

You can use two different date formatters (with and without fraction seconds) and create a custom DateDecodingStrategy. In case of failure when parsing the date returned by the API you can throw a DecodingError as suggested by @PauloMattos in comments:

iOS 9, macOS 10.9, tvOS 9, watchOS 2, Xcode 9 or later

The custom ISO8601 DateFormatter:

extension Formatter {
static let iso8601withFractionalSeconds: DateFormatter = {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
return formatter
}()
static let iso8601: DateFormatter = {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
return formatter
}()
}

The custom DateDecodingStrategy:

extension JSONDecoder.DateDecodingStrategy {
static let customISO8601 = custom {
let container = try $0.singleValueContainer()
let string = try container.decode(String.self)
if let date = Formatter.iso8601withFractionalSeconds.date(from: string) ?? Formatter.iso8601.date(from: string) {
return date
}
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid date: \(string)")
}
}

The custom DateEncodingStrategy:

extension JSONEncoder.DateEncodingStrategy {
static let customISO8601 = custom {
var container = $1.singleValueContainer()
try container.encode(Formatter.iso8601withFractionalSeconds.string(from: $0))
}
}

edit/update:

Xcode 10 • Swift 4.2 or later • iOS 11.2.1 or later

ISO8601DateFormatter now supports formatOptions .withFractionalSeconds:

extension Formatter {
static let iso8601withFractionalSeconds: ISO8601DateFormatter = {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
return formatter
}()
static let iso8601: ISO8601DateFormatter = {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime]
return formatter
}()
}

The customs DateDecodingStrategy and DateEncodingStrategy would be the same as shown above.



// Playground testing
struct ISODates: Codable {
let dateWith9FS: Date
let dateWith3FS: Date
let dateWith2FS: Date
let dateWithoutFS: Date
}


let isoDatesJSON = """
{
"dateWith9FS": "2017-06-19T18:43:19.532123456Z",
"dateWith3FS": "2017-06-19T18:43:19.532Z",
"dateWith2FS": "2017-06-19T18:43:19.53Z",
"dateWithoutFS": "2017-06-19T18:43:19Z",
}
"""


let isoDatesData = Data(isoDatesJSON.utf8)

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .customISO8601

do {
let isoDates = try decoder.decode(ISODates.self, from: isoDatesData)
print(Formatter.iso8601withFractionalSeconds.string(from: isoDates.dateWith9FS)) // 2017-06-19T18:43:19.532Z
print(Formatter.iso8601withFractionalSeconds.string(from: isoDates.dateWith3FS)) // 2017-06-19T18:43:19.532Z
print(Formatter.iso8601withFractionalSeconds.string(from: isoDates.dateWith2FS)) // 2017-06-19T18:43:19.530Z
print(Formatter.iso8601withFractionalSeconds.string(from: isoDates.dateWithoutFS)) // 2017-06-19T18:43:19.000Z
} catch {
print(error)
}

Date Format in Swift

You have to declare 2 different NSDateFormatters, the first to convert the string to a NSDate and the second to print the date in your format.

Try this code:

let dateFormatterGet = NSDateFormatter()
dateFormatterGet.dateFormat = "yyyy-MM-dd HH:mm:ss"

let dateFormatterPrint = NSDateFormatter()
dateFormatterPrint.dateFormat = "MMM dd,yyyy"

let date: NSDate? = dateFormatterGet.dateFromString("2016-02-29 12:24:26")
print(dateFormatterPrint.stringFromDate(date!))

Swift 3 and higher:

From Swift 3 NSDate class has been changed to Date and NSDateFormatter to DateFormatter.

let dateFormatterGet = DateFormatter()
dateFormatterGet.dateFormat = "yyyy-MM-dd HH:mm:ss"

let dateFormatterPrint = DateFormatter()
dateFormatterPrint.dateFormat = "MMM dd,yyyy"

if let date = dateFormatterGet.date(from: "2016-02-29 12:24:26") {
print(dateFormatterPrint.string(from: date))
} else {
print("There was an error decoding the string")
}

How to change a string I get from API

The func gets a date from the string, which you then can format to your wishes.

    var body: some View {
let dateString = "2006-03-24"
let date = dateFromString(dateString)

Text("\(date, style: .date)")
}

func dateFromString(_ string: String) -> Date {
let dateFormatter = DateFormatter()
dateFormatter.locale = .current
dateFormatter.dateFormat = "yyyy-MM-dd"
return dateFormatter.date(from: string) ?? Date()
}

convert ISO8601 String to reformatted date string(Swift)

ISO8601 has several different options, including a timezone. It appears that by default the ISO8601DateFormatter expects a timezone indicator in the string. You can disable this behaviour by using custom options like so:

let pulledDate = "2017-06-16T13:38:34.601767"
var dateFormatter = ISO8601DateFormatter()
dateFormatter.formatOptions = [.withYear, .withMonth, .withDay, .withTime, .withDashSeparatorInDate, .withColonSeparatorInTime]
let date = dateFormatter.date(from: pulledDate)

If you want to know what are the default options, just run this code in a playground:

let dateFormatter = ISO8601DateFormatter()
let options = dateFormatter.formatOptions
options.contains(.withYear)
options.contains(.withMonth)
options.contains(.withWeekOfYear)
options.contains(.withDay)
options.contains(.withTime)
options.contains(.withTimeZone)
options.contains(.withSpaceBetweenDateAndTime)
options.contains(.withDashSeparatorInDate)
options.contains(.withColonSeparatorInTime)
options.contains(.withColonSeparatorInTimeZone)
options.contains(.withFullDate)
options.contains(.withFullTime)
options.contains(.withInternetDateTime)

Of course, if your string doesn't contain a timezone, the date formatter will still interpret it in a timezone using its timeZone property, which – according to the documentation – defaults to GMT.

Remember to change it before using the formatter if you want to interpret your date in a different timezone:

dateFormatter.timeZone = TimeZone(identifier: "Europe/Paris")

Vapor pass date parameter

I realized that the Date object is returned in the following format when queried:

2021-12-31T14:29:00Z

So this is what I tried to pass and that worked! No need for any custom decoding.



Related Topics



Leave a reply



Submit