Search code examples
iosswiftxcodensdateformatter

How to do a proper Date formatter in Swift


I am not quiet sure how to make dateFormatter that is reusable, since I read that dateFormatter is really expensive to do multiple times and that I should reuse it if its possible. Currently my function looks like this:

    func formatDate(date: String) -> String {
    let dateFormatterGet = DateFormatter()

    //this is format I get from my response
    dateFormatterGet.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
    let dateFormatterSet = DateFormatter()

    //this is format I would like to show on my screen
    dateFormatterSet.dateFormat = "EEE, dd MMM YYYY, HH:mm a"
    let dateObj: Date? = dateFormatterGet.date(from: date)
    return dateFormatterSet.string(from: dateObj!)
}

I wouldn't be bothered if I would use it on one screen, but I am using on two, yet maybe its not to expensive either, but I want to know for future cases how to do it properly.

This is function from my other screen, only difference is dateFormatterSet, which I use for showing different format on my screen.

    func formatDate(date: String) -> String {
    let dateFormatterGet = DateFormatter()
    dateFormatterGet.dateFormat = "YYYY-MM-dd'T'HH:mm:ss.SSS'Z'"
    let dateFormatterSet = DateFormatter()
    dateFormatterSet.dateFormat = "EEE, dd MMM"
    guard let dateObj = dateFormatterGet.date(from: date) else { return "Start date 
    unknown"}
    return dateFormatterSet.string(from: dateObj)
}

Thanks in advance.


Solution

  • You don't need two date formatters to convert one date string to another.

    My suggestion is to create one persistent date formatter with a fixed locale

    let dateFormatter : DateFormatter = {
        let formatter = DateFormatter()
        formatter.locale = Locale(identifier: "en_US_POSIX")
        return formatter
    }()
    

    This could be also an extension

    extension DateFormatter {
        static let posixFormatter : DateFormatter = {
            let formatter = DateFormatter()
            formatter.locale = Locale(identifier: "en_US_POSIX")
            return formatter
        }()
    }
    

    And secondly a function with parameters for input format with a default value and output format

    func formatDate(dateString: String, inFormat: String = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", outFormat: String) -> String? {
    
        // let dateFormatter = DateFormatter.posixFormatter
       
        //this is format I get from my response
        dateFormatter.dateFormat = inFormat
        guard let date = dateFormatter.date(from: dateString) else { return nil }
    
        dateFormatter.dateFormat = outFormat
        return dateFormatter.string(from: date)
    }
    

    You have to check if a date could be created from the string so the return value is an optional.

    Side note:

    The Z in an ISO8601 string is the time zone specifier. It must not be wrapped in single quotes. And the year specifier is always lowercase yyyy.