Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift ISO 8601 date formatting

I'm making an api call to randomuser.me and getting back a json file containing (amongst other data):

"dob": { "date": "1993-07-20T09:44:18.674Z", "age": 26 }

I'd like to display the date string in a text label as "dd-MMM-yyyy" but can't figure out how to format the string to achieve this.

I'v tried converting it into a date using ISO8601DateFormatter and then back into a string but have had no luck so far.

Can anyone help?

func getUserData() {

    let config = URLSessionConfiguration.default
    config.urlCache = URLCache.shared
    let session = URLSession(configuration: config)

    let url = URL(string: "https://randomuser.me/api/?page=\(page)&results=20&seed=abc")!
    let urlRequest = URLRequest(url: url, cachePolicy: .returnCacheDataElseLoad, timeoutInterval: 15.0)
    let task = session.dataTask(with: urlRequest) { data, response, error in

        // Check for errors
        guard error == nil else {
            print ("error: \(error!)")
            return
        }
        // Check that data has been returned
        guard let content = data else {
            print("No data")
            return
        }

        do {
            let decoder = JSONDecoder()
            decoder.keyDecodingStrategy = .convertFromSnakeCase
            let fetchedData = try decoder.decode(User.self, from: content)

            for entry in fetchedData.results {
                self.usersData.append(entry)
            }

            DispatchQueue.main.async {
                self.tableView.reloadData()
            }

        } catch let err {
            print("Err", err)
        }
    }
    // Execute the HTTP request
    task.resume()
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "userInfoCell") as! UserInfoCell

    let user: Result

    if isFiltering {
        user = filteredData[indexPath.row]
    } else {
        user = usersData[indexPath.row]
    }

    cell.nameLabel.text = "\(user.name.first) \(user.name.last)"
    cell.dateOfBirthLabel.text = user.dob.date
    cell.genderLabel.text = user.gender.rawValue
    cell.thumbnailImage.loadImageFromURL(user.picture.thumbnail)

    return cell
}
like image 354
Booysenberry Avatar asked Oct 19 '25 17:10

Booysenberry


2 Answers

import Foundation
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"
let theDate = dateFormatter.date(from: "1993-07-20T09:44:18.674Z")!
let newDateFormater = DateFormatter()
newDateFormater.dateFormat = "dd-MMM-yyyy"
print(newDateFormater.string(from: theDate))

First convert the string to date using proper date format. Then convert it back to string using the format you want.

like image 200
Mubashir Ali Avatar answered Oct 21 '25 08:10

Mubashir Ali


Generally, if manually converting 1993-07-20T09:44:18.674Z to a Date, we’d use ISO8601DateFormatter:

let formatter = ISO8601DateFormatter()
formatter.formatOptions.insert(.withFractionalSeconds)

In this approach, it takes care of time zones and locales for us.


That having been said, if you’re using JSONDecoder (and its dateDecodingStrategy outlined below), then we should define our model objects to use Date types, not String for all the dates. Then we tell the JSONDecoder to decode our Date types for us, using a particular dateDecodingStrategy.

But that can’t use the ISO8601DateFormatter. We have to use a DateFormatter with a dateFormat of "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ" and a locale of Locale(identifier: "en_US_POSIX"):

let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)  // this line only needed if you ever use the same formatter to convert `Date` objects back to strings, e.g. in `dateEncodingStrategy` of `JSONEncoder`

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
decoder.dateDecodingStrategy = .formatted(formatter)

See the “Working With Fixed Format Date Representations” sections of the DateFormatter documentation.


Then, for your formatter for your UI, if you absolutely want dd-MMM-yyyy format, you would have a separate formatter for that, e.g.:

let formatter = DateFormatter()
formatter.dateFormat = "dd-MMM-yyyy"

Note, for this UI date formatter, we don’t set a locale or a timeZone, but rather let it use the device’s current defaults.

That having been said, we generally like to avoid using dateFormat for date strings presented in the UI. We generally prefer dateStyle, which shows the date in a format preferred by the user:

let formatter = DateFormatter()
formatter.dateStyle = .medium

This way, the US user will see “Nov 22, 2019”, the UK user will see “22 Nov 2019”, and the French user will see “22 nov. 2019”. Users will see dates in the formats with which they are most accustomed.

See “Working With User-Visible Representations of Dates and Times” in the aforementioned DateFormatter documentation.

like image 22
Rob Avatar answered Oct 21 '25 09:10

Rob