Search code examples
swiftnsdateformatterdateformatter

How to format date string if the given date format changes


I'm getting a response from api that is date string, it's format changes sometimes so I need to format it dynamically,

Here's my code

import Foundation

func format(from: String?, fromFormat: String, to: String) -> String {
    if from == nil { return "" }
    let inputDateFormatter = DateFormatter()
    inputDateFormatter.dateFormat = fromFormat
    let date = inputDateFormatter.date(from: from ?? "")
    
    let outputDateFormatter = DateFormatter()
    outputDateFormatter.dateFormat = to
    if let date = date {
        return outputDateFormatter.string(from: date)
    }
    return "not formatted"
}

let strFromApi = "2020-12-22"

print(format(from: strFromApi, fromFormat: "yyyy-MM-dd", to: "d MMM yyyy"))

As you can see, I have a code there that can format successfully, the problem here is that strFromApi variable was came from api, and is changing between 2020-12-22 and 2020-12-22 00:00:00, when it changes to 2020-12-22 00:00:00, my current code can't format it anymore.

Question: How can I format it even the given format from the server has time?


Solution

  • You can create two date formatters, one with time and another without it and use nil coalescing operator to provide a fallback in case the first one fails. Regarding the date format when returning your final string you should respect the user's device locale and settings. Just use dateStyle and timeStyle when displaying a date to the user:

    extension Formatter {
        static let date: DateFormatter = {
            let formatter = DateFormatter()
            formatter.calendar = Calendar(identifier: .iso8601)
            formatter.locale = Locale(identifier: "en_US_POSIX")
            formatter.dateFormat = "yyyy-MM-dd"
            return formatter
        }()
        static let dateAndTime: DateFormatter = {
            let formatter = DateFormatter()
            formatter.calendar = Calendar(identifier: .iso8601)
            formatter.locale = Locale(identifier: "en_US_POSIX")
            formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
            return formatter
        }()
        static let localizedDate: DateFormatter = {
            let formatter = DateFormatter()
            formatter.dateStyle = .medium
            formatter.timeStyle = .none
            return formatter
        }()
    }
    

    extension Date {
        var localizedDate: String { Formatter.localizedDate.string(from: self) }
    }
    

    func formatted(from string: String?) -> String {
        guard 
            let string = string,
            let date = Formatter.dateAndTime.date(from: string) ??
                       Formatter.date.date(from: string)
        else { return "" }
        
        return date.localizedDate
    }
    
    let strFromApi = "2020-12-22 00:00:00" // "2020-12-22" //
    formatted(from: strFromApi)  // "Dec 22, 2020"