简体   繁体   中英

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
}

pass your date into this

    let dateFormatterPrint = DateFormatter()
    dateFormatterPrint.dateFormat = "dd-MM-yyyy"

    let val = dateFormatterPrint.string(from: "pass your date")
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.

You can use extensions for you to be able to convert the date to your desired format.

var todayDate = "1993-07-20T09:44:18.674Z"

extension String {

    func convertDate(currentFormat: String, toFormat : String) ->  String {
        let dateFormator = DateFormatter()
        dateFormator.dateFormat = currentFormat
        let resultDate = dateFormator.date(from: self)
        dateFormator.dateFormat = toFormat
        return dateFormator.string(from: resultDate!)
    }
}

Then you can implement like this:

cell.dateOfBirthLabel.text = self.todayDate.convertDate(currentFormat: "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", toFormat: "dd-MMM-yyyy")

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, eg:

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM