Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift - Retrieve timezone from ISO8601 date string

Tags:

swift

I have dates saved in a database in this format - "yyyy-MM-dd'T'HH:mm:ssZ". For example, "2018-05-17T11:15:00+0330". The time zone varies, it is in whatever user's local time zone is.

I want to retrieve and display the date like "May 17, 2018 11.15AM", keeping the date/time in the same time zone as it was saved. How can I do that?

I tried this.

let dateString = "2018-05-17T11:15:00+0330"
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
//formatter.timeZone = ???
print(formatter.date(from: dateString))

But I get the Date object in local timezone and converting that to date string displays the date in my local timezone as well.

Is there anything like this, https://stackoverflow.com/a/46216251/1373592, in Swift?

Thanks.

like image 343
SKT Avatar asked May 17 '18 06:05

SKT


2 Answers

What you are really trying to do, is to ignore the time zone.

You can just strip off the last 5 characters from the date string, parse it in one format, and format it in another format:

let dateString = "2018-05-17T11:15:00+0330"
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
let date = formatter.date(from: String(dateString.dropLast(5)))! // note the stripping off here
formatter.dateFormat = "MMMM d, yyyy hh.mma"
print(formatter.string(from: date))

Note that you can't compare this date with other dates because you've stripped its time zone off.

Also, SwiftDate is a similar library to JodaTime.

like image 129
Sweeper Avatar answered Dec 30 '22 15:12

Sweeper


Here's a little extension to TimeZone I just whipped up. You can use it to create a TimeZone from an ISO8601 date string. It will handle timezones in the string in the following formats:

  • Z (for UTC time)
  • +/-HH
  • +/-HHmm
  • +/-HH:mm

Here is the extension:

extension TimeZone {
    init?(iso8601: String) {
        let tz = iso8601.dropFirst(19) // remove yyyy-MM-ddTHH:mm:ss part
        if tz == "Z" {
            self.init(secondsFromGMT: 0)
        } else if tz.count == 3 { // assume +/-HH
            if let hour = Int(tz) {
                self.init(secondsFromGMT: hour * 3600)
                return
            }
        } else if tz.count == 5 { // assume +/-HHMM
            if let hour = Int(tz.dropLast(2)), let min = Int(tz.dropFirst(3)) {
                self.init(secondsFromGMT: (hour * 60 + min) * 60)
                return
            }
        } else if tz.count == 6 { // assime +/-HH:MM
            let parts = tz.components(separatedBy: ":")
            if parts.count == 2 {
                if let hour = Int(parts[0]), let min = Int(parts[1]) {
                    self.init(secondsFromGMT: (hour * 60 + min) * 60)
                    return
                }
            }
        }

        return nil
    }
}

And a test:

print(TimeZone(iso8601: "2018-05-17T11:15:00+0330"))

You can combine this parsing and formatting so the final result is in the original timezone.

let dateString = "2018-05-17T11:15:00+0330"
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
formatter.locale = Locale(identifier: "en_US_POSIX")
if let date = formatter.date(from: dateString), let tz = TimeZone(iso8601: dateString) {
    let newFmt = DateFormatter()
    newFmt.dateStyle = .medium
    newFmt.timeStyle = .short
    newFmt.timeZone = tz
    let newString = newFmt.string(from: date)
    print(newString)
}
like image 45
rmaddy Avatar answered Dec 30 '22 15:12

rmaddy