I am storing user birth dates on my backend via storing a date component dictionary. It looks something like this:
{
"day": 1,
"month": 1,
"year": 1970,
"timeZone": "GMT"
}
To store this object, it grabs the user's birth day, month, and year from user input. The user time zone, however, is gathered via TimeZone.current.abbreviation()
.
Now, some of my user birthdate objects on my backend have their "timeZone"
formatted as "CST"
, "BST"
, or "PDT"
. "timeZone"
s that are formatted this way successfully initialize a TimeZone
on the front end via let timeZone = TimeZone(abbreviation: "CST")!
, let timeZone = TimeZone(abbreviation: "BST")!
, or let timeZone = TimeZone(abbreviation: "PDT")!
, respectively.
The problem is, other user birthdate objects on my backend have their "timeZone"
formatted as "GMT+8"
. When trying to initialize "timeZone"
s formatted like this via let timeZone = TimeZone(abbreviation: "GMT+8")!
, the initialization returns nil
. I also tried let timeZone = TimeZone(identifier: "GMT+8")!
, but this returns nil
as well.
Is there a way to initialize a TimeZone
when it is formatted with respect to its offset to GMT as opposed to its unique abbreviation? I've seen a TimeZone
initializer that is TimeZone(secondsFromGMT: Int)
. Could I simply take the 8
from "GMT+8"
and multiply it by 3600
(the number of seconds in an hour) and pass this result to TimeZone(secondsFromGMT: Int)
?
I ended up writing code that adapts my application to account for these unexpected fringe cases where a TimeZone's abbreviation is formatted like "GMT+8"
rather than "SGT"
. I created an extension to TimeZone
:
extension TimeZone {
static func timeZone(from string: String) -> TimeZone {
//The string format passed into this function should always be similar to "GMT+8" or "GMT-3:30"
if string.contains("±") {
//This case should always be "GMT±00:00", or simply GMT
return TimeZone(secondsFromGMT: 0)!
} else {
//If the string doesn't contain "±", then there should be some offset. We will split the string into timeZone components. "GMT+8" would split into ["GMT", "8"]. "GMT-3:30" would split int ["GMT","3","30"]
let timeZoneComponents = string.components(separatedBy: CharacterSet(charactersIn: "+-:"))
var isAheadOfGMT: Bool!
//Check if the string contains "+". This will dictate if we add or subtract seconds from GMT
if string.contains("+") {
isAheadOfGMT = true
} else {
isAheadOfGMT = false
}
//Grab the second element in timeZoneElements. This represents the offset in hours
let offsetInHours = Int(timeZoneComponents[1])!
//Convert these hours into seconds
var offsetInSeconds: Int!
if isAheadOfGMT {
offsetInSeconds = offsetInHours * 3600
} else {
offsetInSeconds = offsetInHours * -3600
}
//Check if there is a colon in the passed string. If it does, then there are additional minutes we need to account for
if string.contains(":") {
let additionalMinutes = Int(timeZoneComponents[2])!
let additionalSeconds = additionalMinutes * 60
offsetInSeconds += additionalSeconds
}
//Create a TimeZone from this calculated offset in seconds
let timeZoneFromOffset = TimeZone(secondsFromGMT: offsetInSeconds)!
//Return this value
return timeZoneFromOffset
}
}
}
It is used like so:
let json: [String:String] = ["timeZone":"GMT+8"]
let timeZone = json["timeZone"]
let birthDate: BirthDate!
if let timeZoneFromAbbrev = TimeZone(abbreviation: timeZone) {
birthDate = BirthDate(day: birthDay, month: birthMonth, year: birthYear, timeZone: timeZoneFromAbbrev)
} else {
let timeZoneFromOffset = TimeZone.timeZone(from: timeZone)
print(timeZoneFromOffset.abbreviation())
//Prints "GMT+8"
birthDate = BirthDate(day: birthDay, month: birthMonth, year: birthYear, timeZone: timeZoneFromOffset)
}
My BirthDate
class for context:
class BirthDate {
var day: Int
var month: Int
var year: Int
var timeZone: TimeZone
init(day: Int, month: Int, year: Int, timeZone: TimeZone) {
self.day = day
self.month = month
self.year = year
self.timeZone = timeZone
}
}
Time zones are funny things to work with. If anybody sees issue with the TimeZone
extension above, please let me know. I think I've accounted for all scenarios, but could be mistaken.