From f235ad08019f725070583609bfd45f53f3aa322e Mon Sep 17 00:00:00 2001 From: patrick-zippenfenig Date: Sun, 7 Aug 2022 12:42:11 +0200 Subject: [PATCH] automatic resolution switching --- .../SwiftTimeZoneLookup.swift | 48 ++++++++++++++----- .../SwiftTimeZoneLookupTests.swift | 6 ++- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/Sources/SwiftTimeZoneLookup/SwiftTimeZoneLookup.swift b/Sources/SwiftTimeZoneLookup/SwiftTimeZoneLookup.swift index 6ef1e05..2183ed2 100644 --- a/Sources/SwiftTimeZoneLookup/SwiftTimeZoneLookup.swift +++ b/Sources/SwiftTimeZoneLookup/SwiftTimeZoneLookup.swift @@ -8,34 +8,60 @@ public enum SwiftTimeZoneLookupError: Error { public struct SwiftTimeZoneLookupResult { /// Timezone identifier like `Europe/Berlin` - let timezone: String + public let timezone: String /// Country name like `Germany` - let countryName: String? + public let countryName: String? /// 2 character country code like `DE` for Germany - let countryAlpha2: String? + public let countryAlpha2: String? } public final class SwiftTimeZoneLookup { - private let database: OpaquePointer + /// 0.00017 degrees (~20m) resolution + private let database21: OpaquePointer + + /// 0.0055 degrees (~0.5km) resolution + private let database16: OpaquePointer /// Throws if the timezone database could not be opened public init() throws { guard let timezone21 = Bundle.module.url(forResource: "timezone21", withExtension: "bin") else { throw SwiftTimeZoneLookupError.couldNotFindTimezone21bin } - guard let database = timezone21.withUnsafeFileSystemRepresentation({ timezone21 in - ZDOpenDatabase(timezone21) - }) else { + guard let timezone16 = Bundle.module.url(forResource: "timezone16", withExtension: "bin") else { + throw SwiftTimeZoneLookupError.couldNotFindTimezone21bin + } + + guard let database21 = timezone21.withUnsafeFileSystemRepresentation(ZDOpenDatabase) else { throw SwiftTimeZoneLookupError.couldNotOpenDatabase } - self.database = database + guard let database16 = timezone16.withUnsafeFileSystemRepresentation(ZDOpenDatabase) else { + throw SwiftTimeZoneLookupError.couldNotOpenDatabase + } + + self.database21 = database21 + self.database16 = database16 + } + + /// Try with lower resolution first and use high resolution database if too close to the border + private func highResLookup(latitude: Float, longitude: Float) -> UnsafeMutablePointer? { + var safezone: Float = .nan + guard let result = ZDLookup(database16, latitude, longitude, &safezone) else { + return nil + } + if safezone >= 0.0055*2 { + return result + } + guard let result21 = ZDLookup(database21, latitude, longitude, &safezone) else { + return nil + } + return result21 } /// Resolve timezone by coordinate and return timezone, country name and alpha2 public func lookup(latitude: Float, longitude: Float) -> SwiftTimeZoneLookupResult? { - guard let result = ZDLookup(database, latitude, longitude, nil) else { + guard let result = highResLookup(latitude: latitude, longitude: longitude) else { return nil } defer { ZDFreeResults(result) } @@ -72,7 +98,7 @@ public final class SwiftTimeZoneLookup { /// Resolve the timz public func simple(latitude: Float, longitude: Float) -> String? { - guard let result = ZDLookup(database, latitude, longitude, nil) else { + guard let result = highResLookup(latitude: latitude, longitude: longitude) else { return nil } defer { ZDFreeResults(result) } @@ -100,6 +126,6 @@ public final class SwiftTimeZoneLookup { } deinit { - ZDCloseDatabase(database) + ZDCloseDatabase(database21) } } diff --git a/Tests/SwiftTimeZoneLookupTests/SwiftTimeZoneLookupTests.swift b/Tests/SwiftTimeZoneLookupTests/SwiftTimeZoneLookupTests.swift index 01b8e6f..6e71fcf 100644 --- a/Tests/SwiftTimeZoneLookupTests/SwiftTimeZoneLookupTests.swift +++ b/Tests/SwiftTimeZoneLookupTests/SwiftTimeZoneLookupTests.swift @@ -1,5 +1,5 @@ import XCTest -@testable import SwiftTimeZoneLookup +import SwiftTimeZoneLookup final class SwiftTimeZoneLookupTests: XCTestCase { func testLookup() throws { @@ -13,5 +13,9 @@ final class SwiftTimeZoneLookupTests: XCTestCase { XCTAssertEqual(database.lookup(latitude: 42.5, longitude: -8.6)?.countryName, "Spain") XCTAssertEqual(database.lookup(latitude: 42.5, longitude: -8.6)?.countryAlpha2, "ES") + + // on the border to the netherlands. Requires high resolution lookup + XCTAssertEqual(database.simple(latitude: 53.242293, longitude: 7.209253), "Europe/Berlin") + XCTAssertEqual(database.simple(latitude: 53.239692, longitude: 7.207879), "Europe/Amsterdam") } }