Convert HH:mm string to GMT time string HH:mm

119 views Asked by At

I'm using this code to convert HH:mm string to GMT time string HH:mm:

func convertToGMT(timeString: String) -> String {
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "HH:mm"
    
    // Assuming the input time is in the user's local time zone
    dateFormatter.timeZone = TimeZone.current
    
    // Parse the input time string into a Date object
    guard let date = dateFormatter.date(from: timeString) else {
        return "" // Return nil if the input format is invalid
    }
    
    // Convert the date to GMT (UTC) time zone
    dateFormatter.timeZone = TimeZone(abbreviation: "GMT")
    let gmtTimeString = dateFormatter.string(from: date)
    
    return gmtTimeString
}

The problem is that in Jerusalem GMT -3 I get for 12:00 -> 10:00 instead of 09:00.

What is the problem?

3

There are 3 answers

4
Claudio On BEST ANSWER

The problem is how your converting the date, when you're doing the conversion to date using the date formatter

    // Parse the input time string into a Date object
    guard let date = dateFormatter.date(from: timeString) else {
        return "" // Return nil if the input format is invalid
    }
    

It is producing the following date 2000-01-01 10:00:00 +0000.

Upon looking on a time zone converter website the result produced below shows that the conversion being done on your current code is correct. enter image description here

The conversion shown below using current date indicates a difference of 3 hours, which is also the difference you expected your code to produce. This difference is explained by JeremyP in the comments:

In July, Israel is in daylight savings time. In January it is not. In January of any year including 2000, Israel is two hours ahead of GMT. In July it is three hours ahead.

enter image description here

A possible solution is to create the date using DateComponents:

func convertToGMT(timeString: String) -> String {
    var dateComponents = Calendar.current.dateComponents(
        in: TimeZone.current,
        from: Date()
    )
    
    let dateParts = timeString.split(separator: ":")
    dateComponents.hour = Int(dateParts[0]) // TODO: validate existance on array
    dateComponents.minute = Int(dateParts[1]) // TODO: validate existance on array
    
    // Parse the input time string into a Date object
    guard let date = Calendar.current.date(from: dateComponents) else {
        return "" // Return nil if the input format is invalid
    }
    
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "HH:mm"
    // Convert the date to GMT (UTC) time zone
    dateFormatter.timeZone = TimeZone(abbreviation: "GMT")
    let gmtTimeString = dateFormatter.string(from: date)
    
    return gmtTimeString
}
0
vadian On

As mentioned in Claudio's answer the actual time difference depends on the date portion, and the confusion occurs because DateFormatter sets the date to Jan 1, 2000 for a pure time string.

You have to calculate the time difference from the time in today to consider daylight saving time.

This is an approach which extracts hour and minute with Regular Expression and sets the hour and minute of the current date with date(bySettingHour:minute:second:of: of Calendar.

The code works in any locale and throws errors for a format mismatch and if the digits are not in 0..<24 (hour) and 0..<60 (minute).

enum TimeConversionError: Error {
    case formatMismatch, outOfRange
}

func convertToGMT(timeString: String) throws -> String {
    guard let hhmm = timeString.firstMatch(of: /(\d{1,2}):(\d{2})/) else { throw TimeConversionError.formatMismatch }
    guard let currentDate = Calendar.current.date(bySettingHour: Int(hhmm.1)!, minute: Int(hhmm.2)!, second: 0, of: .now) else { throw TimeConversionError.outOfRange }
    let dateFormatter = DateFormatter()
    dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
    dateFormatter.dateFormat = "HH:mm"
    return dateFormatter.string(from: currentDate)
}

let string = "12:00"
do {
    let gmtDateString = try convertToGMT(timeString: string)
} catch { print(error) }
3
Leo Dabus On

The problem is that you need to set the dateFormater’s defaultDate to startOfDay for .now. Make sure to set the dateFormatter calendar property as well.

func convertToUTC(timeString: String) -> String? {
    let calendar = Calendar(identifier: .iso8601)
    let locale = Locale(identifier: "en_US_POSIX")
    let defaultDate = calendar.startOfDay(for: .now)
    let dateFormatter = DateFormatter()
    dateFormatter.calendar = calendar
    dateFormatter.locale = locale
    // Assuming the input time is in the user's local time zone which is the default. This line can be omitted
    dateFormatter.timeZone = .current
    dateFormatter.defaultDate = defaultDate
    dateFormatter.dateFormat = "HH:mm"
    // Parse the input time string into a Date object
    guard let date = dateFormatter.date(from: timeString) else {
        return nil // Return nil if the input format is invalid
    }
    // Convert the date to UTC time zone
    dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
    return dateFormatter.string(from: date)
}
convertToUTC(timeString: "09:00")  // "12:00" my timezone is -3hs