I am trying to get timeZones for locations on a global map that a user taps on. I have only the coordinates of those points. I am able to create a CLLocation and reverseGeocodeLocation to get the placemark and timeZone, however this only works when the user is connected to the internet.
Since timeZone boundaries are fixed, was wondering if there is anyway to get the timezone for a location when offline using the Swift libraries? I am not looking for third party libraries to achieve this.
The code for online query is:
let clLocation = CLLocation(latitude: location.latitude, longitude: location.longitude)
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(clLocation) { placemarks, error in
if let error = error {
print(error.localizedDescription)
} else {
if let placemarks = placemarks {
if let timeZone = placemarks.first!.timeZone {
self.timeZone = timeZone
}
}
}
}
Basically there are two options:
The easy way is to get the time zone from the location. The timestamp
property of CLLocation
displays the current time zone identifier when called with description(with:
. A regex can extract the time zone string
import CoreLocation
let currentLocation = CLLocation(latitude: 47.36667, longitude: 8.55) // Location of Zurich
let date = currentLocation.timestamp.description(with: Locale(identifier: "en_US_POSIX"))
let timeRange = date.firstRange(of: /(A|P)M\s/)
let timeZone = date[timeRange!.upperBound...]
print(String(timeZone)) // Central European Summer Time
Otherwise you need a lookup table like this one, a list of time zones and their location in a text format (here CSV)
Create a helper struct
struct TimeZoneData {
let name: String
let location: CLLocation
}
map the CSV list to instances of the struct and calculate the nearest distance from the current location. timeZoneCSV
represents the CSV text
import CoreLocation
let currentLocation = CLLocation(latitude: 47.36667, longitude: 8.55) // Location of Zurich
let nearestTimeZone = timeZoneCSV
.components(separatedBy: .newlines)
.map { line in
let fields = line.components(separatedBy: ";")
return TimeZoneData(name: fields[0], location: CLLocation(latitude: Double(fields[1])!, longitude: Double(fields[2])!))
}
.sorted(by: {$0.location.distance(from: currentLocation) < $1.location.distance(from: currentLocation)}).first!
print(nearestTimeZone) // "Europe/Zurich
Note that the first option displays the long version of the time zone identifier and the second option displays the name of the time zone.
Edit:
There is a library APTimeZones written in Objective-C. The code is straightforward and can be used with a bridging header or can be even easily converted to Swift.
Nevertheless all the mentioned ways to get the time zone are not reliable. For a more precise result you have to use shapes rather than coordinates, provided in TimeZone-Boundary-Builder