Skip to content

Commit

Permalink
Merge pull request #29 from tpgillam/tg/region_overlap
Browse files Browse the repository at this point in the history
Handle region overlap with new API
  • Loading branch information
tpgillam authored Jun 6, 2023
2 parents ca8a872 + 3315042 commit 0d9b13d
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 15 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
matrix:
version:
- '1.6'
- '1.8'
- '1'
- 'nightly'
os:
- ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "TimeZoneFinder"
uuid = "3ccf6684-3f25-4581-8c58-114637dcab4a"
authors = ["Tom Gillam <[email protected]>"]
version = "0.3.5"
version = "0.4.0"

[deps]
JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
Expand Down
1 change: 1 addition & 0 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Documentation for [TimeZoneFinder](https://github.com/tpgillam/TimeZoneFinder.jl
## API

```@docs
timezones_at
timezone_at
```

Expand Down
60 changes: 47 additions & 13 deletions src/TimeZoneFinder.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module TimeZoneFinder

export timezone_at
export timezone_at, timezones_at

using JSON3
using LazyArtifacts
Expand Down Expand Up @@ -147,7 +147,7 @@ Get the version of timezone-boundary-builder data that we should use.
If no arguments are provided, the `tzdata_version` is determined by that currently in use by
the `TimeZones` package. The map from tzdata version -> boundary version is memoized.
This is determined by the rules in the "note" in the docstring for `timezone_at`.
This is determined by the rules in the "note" in the docstring for [`timezone_at`](@ref).
"""
@memoize function _timezone_boundary_builder_version(tzdata_version::AbstractString)
boundary_builder_versions = _get_boundary_builder_versions()
Expand All @@ -163,13 +163,22 @@ function _timezone_boundary_builder_version()
end

"""
timezone_at(latitude, longitude)
timezones_at(latitude, longitude)
Get the timezones used at the given `latitude` and `longitude`.
Get the timezone at the given `latitude` and `longitude`.
If no timezones are known, then an empty list will be returned. Conversely, if the
co-ordinates land in a disputed region, all applicable timezones will be returned.
```jldoctest
julia> timezone_at(52.5061, 13.358)
Europe/Berlin (UTC+1/UTC+2)
julia> timezones_at(52.5061, 13.358)
1-element Vector{Dates.TimeZone}:
Europe/Berlin (UTC+1/UTC+2)
julia> timezones_at(69.8, -141)
2-element Vector{Dates.TimeZone}:
America/Anchorage (UTC-9/UTC-8)
America/Dawson (UTC-7)
```
!!! note
Expand All @@ -184,19 +193,44 @@ Europe/Berlin (UTC+1/UTC+2)
1. There were no boundary changes in a tzdata release, which means that there will
never be a boundary release for this particular version.
2. The boundary dataset is not yet available for a new tzdata release.
Returns a `TimeZone` instance if `latitude` and `longitude` correspond to a known timezone,
otherwise `nothing` is returned.
"""
function timezone_at(latitude::Real, longitude::Real)
function timezones_at(latitude, longitude)
version = _timezone_boundary_builder_version()
data = load_data(version)
p = Point{2,Float64}(longitude, latitude)
# This is an unintelligent linear search through all polygons. There is much room for
# improvement by building a spatial index.
i = findfirst(shape -> in(p, shape), data.shapes)
isnothing(i) && return nothing
return data.tzs[i]
is = findall(shape -> in(p, shape), data.shapes)
return data.tzs[is]
end

"""
timezone_at(latitude, longitude)
Get any uniquely applicable timezone at the given `latitude` and `longitude`.
```jldoctest
julia> timezone_at(52.5061, 13.358)
Europe/Berlin (UTC+1/UTC+2)
```
See additional note on docstring for [`timezone_at`](@ref) regarding the version of tzdata
that will be used.
# Returns
- a `TimeZone` instance if `latitude` and `longitude` correspond to a unqiue timezone.
- `nothing` if no timezone is found.
An exception is raised if multiple timezones correspond to this location - use
[`timezones_at`](@ref) to obtain all the matches.
"""
function timezone_at(latitude::Real, longitude::Real)
tzs = timezones_at(latitude, longitude)
isempty(tzs) && return nothing
if length(tzs) > 1
throw(ArgumentError("Found multiple timezones: $tzs at ($latitude, $longitude)"))
end
return only(tzs)
end

# Precompile the primary API.
Expand Down
13 changes: 13 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ const TEST_LOCATIONS =
@test isempty(memoize_cache(TimeZoneFinder.load_data))

@test timezone_at(52.5061, 13.358) == TimeZone("Europe/Berlin")
@test timezones_at(52.5061, 13.358) == [TimeZone("Europe/Berlin")]

# Memoize cache should now be populated
@test !isempty(memoize_cache(TimeZoneFinder.load_data))
Expand All @@ -171,6 +172,9 @@ const TEST_LOCATIONS =
@test isnothing(timezone_at(91, 0))
@test isnothing(timezone_at(0, 181))
@test isnothing(timezone_at(0, -181))
@test isempty(timezones_at(91, 0))
@test isempty(timezones_at(0, 181))
@test isempty(timezones_at(0, -181))
end

@testset "known locations (read_from_cache=$read_from_cache)" begin
Expand All @@ -179,6 +183,15 @@ const TEST_LOCATIONS =
location.timezone
end
end

@testset "multiple timezones (read_from_cache=$read_from_cache)" begin
# This is the disputed Beaufort sea region.
# Taken from timezone-boundary-builder. Full list of expected overlaps are here:
# https://github.com/evansiroky/timezone-boundary-builder/blob/master/expectedZoneOverlaps.json
@test timezones_at(69.8, -141) ==
[TimeZone("America/Anchorage"), TimeZone("America/Dawson")]
@test_throws ArgumentError timezone_at(69.8, -141)
end
end

@testset "old tzdata versions" begin
Expand Down

2 comments on commit 0d9b13d

@tpgillam
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/84970

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.4.0 -m "<description of version>" 0d9b13d170fc78af1d8db1c836f2dc8d2f54b4d6
git push origin v0.4.0

Please sign in to comment.