Home > Net >  DateFormatter showing incorrect time
DateFormatter showing incorrect time

Time:06-29

My requirement is to convert date string (in PHT) received from my API to another date string format.

  • API Date String format: "MM/dd/yyyy HH:mma" eg. "06/28/2022 06:55PM"
  • UI Date String Format: "dd MMM yyyy, hh:mm a", eg. "28 Jun 2022, 06:55 PM"

I wrote following code:

extension Date {
    func toString(format: String) -> String {
        let formatter = DateFormatter()
        formatter.dateFormat = format
        return formatter.string(from: self)
    }
    
    init?(from: String) {
        let formatter = DateFormatter()
        formatter.dateFormat = "MM/dd/yyyy HH:mma"
        guard let date = formatter.date(from: from) else { return nil }
        self = date
    }
}

let dateFormat = "dd MMM yyyy, hh:mm a"
var dateStr = "06/28/2022 06:55PM"
let date = Date(from: dateStr)
date?.toString(format: dateFormat)

However, the time is incorrect. I get "28 Jun 2022, 12:55 PM" as the output. I tried adding locale and timeZone as well. Nothing seems to help. Any help would be much appreciated.

EDIT: I apologise for compilation issues. I edited few variable names at the end to make it more readable, erroneously deleted few method calls. I have fixed and updated the code to run when directly pasted in Playground file.

@Larme : Thank you for your answer. It worked! Could you please post it as a separate answer so that I can accept it.

CodePudding user response:

As @Larne's call to change from "MM/dd/yyyy HH:mma" to "MM/dd/yyyy hh:mma" and improvised of mistakes code like below

extension Date {
    func toString(format: String) -> String {
        let formatter = DateFormatter()
        formatter.dateFormat = format
        return formatter.string(from: self)
    }
    
    init?(from: String) {
        let formatter = DateFormatter()
        formatter.dateFormat = "MM/dd/yyyy hh:mma"
        guard let date = formatter.date(from: from) else { return nil }
        self = date
    }
}

let dateFormat = "dd MMM yyyy, hh:mm a"
let dateStr = "06/28/2022 06:55PM"
let date = Date(from: dateStr)
let finalDate = date?.toString(format: dateFormat)
print("FinalDate",finalDate)

CodePudding user response:

Personally, I handle everything related to dates in Swift with this library SwiftDate. It's just great. I leave you an example of how simple this kind of thing is.

import SwiftDate
func workingWithDate() {
    let inputFormat = "MM/dd/yyyy hh:mma"
    let outputFormat = "dd MMM yyyy, hh:mma"
    let dateStr = "06/28/2022 06:55PM"
    guard let dateFromString = dateStr.toDate(inputFormat, region: Region.ISO) else { return }
        
    print(dateFromString)
    print("Output date: \(dateFromString.toFormat(outputFormat, locale: nil))")
}

The output is:

{abs_date='2022-06-28T18:55:00Z', rep_date='2022-06-28T18:55:00Z',
region={calendar='gregorian', timezone='GMT', locale='en_US_POSIX'}
Output date: 28 Jun 2022, 06:55PM

The main thing to understand is that the string you are trying to parse is in ISO format since it doesn't contain any zone information and therefore if you tried to parse it with UTC/GMT etc, I don't think you would get the correct value. For example if you parse using UTC without including AM/PM it works, but if you include 'a' in the input format and AM or PM in the date string it won't be able to parse it.

CodePudding user response:

First: Your time conversion issue was caused by using HH instead of hh in your input format.

Then I would advice to never instantiate a DateFormatter within a function call as you did in your extension, as this is a relatively expensive operation.

Create the time formatters once and reuse them:

import Foundation

let inputDateFormatter = DateFormatter()
inputDateFormatter.dateFormat = "MM/dd/yyyy hh:mma"  // Note LOWERCASE hh

// uncomment the following line if you'd like to display the time in the user's time zone
// inputDateFormatter.timeZone = TimeZone(abbreviation: "PHT")

let outputDateFormatter = DateFormatter()
outputDateFormatter.dateFormat = "dd MMM yyyy, hh:mm a"

let dateStr = "06/28/2022 06:55PM"

if let date = inputDateFormatter.date(from: dateStr) {
    let output = outputDateFormatter.string(from: date)
}

Extending Date with specific functionality under such a generic name can also quickly lead to funny behavior elsewhere in your app if not properly scoped.

Also keep in mind that if you don't set the timezone on the input, then you are assuming that the string is displayed in the same time zone as the API service. So if your user is not in PHT, the time display will be off (it will display PHT time, though the user is somewhere else).

Hope it helps!

  • Related