Skip to content

Commit 9125776

Browse files
committed
Introduce a Shortstring based Name type
1 parent 906cea0 commit 9125776

File tree

12 files changed

+134
-66
lines changed

12 files changed

+134
-66
lines changed

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
1111
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
1212
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
1313
Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
14+
ShortStrings = "63221d1c-8677-4ff0-9126-0ff0817b4975"
1415
Unicode = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
1516

1617
[compat]
1718
EzXML = "0.9.1, 1"
1819
Mocking = "0.7"
1920
RecipesBase = "0.7, 0.8, 1"
21+
ShortStrings = 0.3.5
2022
julia = "1"
2123

2224
[extras]

src/TimeZones.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ module TimeZones
22

33
using Dates
44
using Printf
5+
using ShortStrings
56
using Serialization
67
using RecipesBase: RecipesBase, @recipe
78
using Unicode
@@ -55,6 +56,7 @@ include("indexable_generator.jl")
5556

5657
include("class.jl")
5758
include("utcoffset.jl")
59+
include(joinpath("types", "name.jl"))
5860
include(joinpath("types", "timezone.jl"))
5961
include(joinpath("types", "fixedtimezone.jl"))
6062
include(joinpath("types", "variabletimezone.jl"))

src/types/fixedtimezone.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const FIXED_TIME_ZONE_REGEX = r"""
3030
A `TimeZone` with a constant offset for all of time.
3131
"""
3232
struct FixedTimeZone <: TimeZone
33-
name::String
33+
name::Name
3434
offset::UTCOffset
3535
end
3636

src/types/name.jl

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
struct SName
2+
region::ShortString15
3+
locality1::ShortString15
4+
locality2::ShortString15
5+
end
6+
7+
function Base.print(io::IO, name::SName)
8+
print(io, name.region)
9+
if !isempty(name.locality1)
10+
print(io,"/", name.locality1)
11+
if !isempty(name.locality2)
12+
print(io,"/", name.locality2)
13+
end
14+
end
15+
end
16+
17+
Base.convert(::Type{String}, name::SName) = string(name)
18+
function Base.convert(::Type{SName}, str::AbstractString)
19+
parts = split(str, "/"; limit=3)
20+
return if length(parts) == 3
21+
SName(parts[1], parts[2], parts[3])
22+
elseif length(parts) == 2
23+
SName(parts[1], parts[2], ss15"")
24+
else
25+
SName(parts[1], ss15"", ss15"")
26+
end
27+
end
28+
29+
Base.isempty(name::SName) = isempty(name.region) # region being empty implies all empty
30+
31+
name_parts(str::AbstractString) = split(str, "/")
32+
function name_parts(name::SName)
33+
# TODO this could be faster by returning an iterator but not really performance critial
34+
parts = [name.region]
35+
if !isempty(name.locality1)
36+
push!(parts, name.locality1)
37+
if !isempty(name.locality2)
38+
push!(parts, name.locality2)
39+
end
40+
end
41+
return parts
42+
end
43+
44+
# Short strings are broken on 32bit:
45+
# TODO: https://github.com/JuliaString/MurmurHash3.jl/issues/12
46+
const Name = Int === Int32 ? String : SName

src/types/timezone.jl

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const TIME_ZONE_CACHE = Dict{String,Tuple{TimeZone,Class}}()
1+
const TIME_ZONE_CACHE = Dict{Name,Tuple{TimeZone,Class}}()
22

33
"""
44
TimeZone(str::AbstractString) -> TimeZone
@@ -41,11 +41,15 @@ US/Pacific (UTC-8/UTC-7)
4141
TimeZone(::AbstractString, ::Class)
4242

4343
function TimeZone(str::AbstractString, mask::Class=Class(:DEFAULT))
44+
return TimeZone(convert(Name, str), mask)
45+
end
46+
47+
function TimeZone(name::Name, mask::Class=Class(:DEFAULT))
48+
str = string(name)
4449
# Note: If the class `mask` does not match the time zone we'll still load the
4550
# information into the cache to ensure the result is consistent.
46-
tz, class = get!(TIME_ZONE_CACHE, str) do
47-
tz_path = joinpath(TZData.COMPILED_DIR, split(str, "/")...)
48-
51+
tz, class = get!(TIME_ZONE_CACHE, name) do
52+
tz_path = joinpath(TZData.COMPILED_DIR, name_parts(name)...)
4953
if isfile(tz_path)
5054
open(deserialize, tz_path, "r")
5155
elseif occursin(FIXED_TIME_ZONE_REGEX, str)
@@ -91,19 +95,20 @@ end
9195
9296
Check whether a string is a valid for constructing a `TimeZone` with the provided `mask`.
9397
"""
94-
function istimezone(str::AbstractString, mask::Class=Class(:DEFAULT))
98+
function istimezone(str::Union{AbstractString, Name}, mask::Class=Class(:DEFAULT))
9599
# Start by performing quick FIXED class test
96-
if mask & Class(:FIXED) != Class(:NONE) && occursin(FIXED_TIME_ZONE_REGEX, str)
100+
if mask & Class(:FIXED) != Class(:NONE) && occursin(FIXED_TIME_ZONE_REGEX, string(str))
97101
return true
98102
end
103+
name = convert(Name, str)
99104

100105
# Perform more expensive checks against pre-compiled time zones
101106
tz, class = get(TIME_ZONE_CACHE, str) do
102-
tz_path = joinpath(TZData.COMPILED_DIR, split(str, "/")...)
107+
tz_path = joinpath(TZData.COMPILED_DIR, name_parts(name)...)
103108

104109
if isfile(tz_path)
105110
# Cache the data since we're already performing the deserialization
106-
TIME_ZONE_CACHE[str] = open(deserialize, tz_path, "r")
111+
TIME_ZONE_CACHE[name] = open(deserialize, tz_path, "r")
107112
else
108113
nothing, Class(:NONE)
109114
end

src/types/variabletimezone.jl

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,12 @@ Base.isless(a::Transition, b::Transition) = isless(a.utc_datetime, b.utc_datetim
1111
A `TimeZone` with an offset that changes over time.
1212
"""
1313
struct VariableTimeZone <: TimeZone
14-
name::String
14+
name::Name
1515
transitions::Vector{Transition}
1616
cutoff::Union{DateTime,Nothing}
17-
18-
function VariableTimeZone(name::AbstractString, transitions::Vector{Transition}, cutoff::Union{DateTime,Nothing}=nothing)
19-
new(name, transitions, cutoff)
20-
end
17+
end
18+
function VariableTimeZone(name::AbstractString, transitions::Vector{Transition})
19+
VariableTimeZone(name, transitions, nothing)
2120
end
2221

2322
name(tz::VariableTimeZone) = tz.name

src/types/zoneddatetime.jl

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,20 @@ using Dates: AbstractDateTime, argerror, validargs
66
# A `DateTime` that includes `TimeZone` information.
77
# """
88

9-
struct ZonedDateTime <: AbstractDateTime
9+
struct ZonedDateTime{T<:TimeZone} <: AbstractDateTime
1010
utc_datetime::DateTime
11-
timezone::TimeZone
11+
timezone::T
1212
zone::FixedTimeZone # The current zone for the utc_datetime.
13+
end
1314

14-
function ZonedDateTime(utc_datetime::DateTime, timezone::TimeZone, zone::FixedTimeZone)
15-
return new(utc_datetime, timezone, zone)
15+
function ZonedDateTime(
16+
utc_datetime::DateTime, timezone::VariableTimeZone, zone::FixedTimeZone
17+
)
18+
if timezone.cutoff !== nothing && utc_datetime >= timezone.cutoff
19+
throw(UnhandledTimeError(timezone))
1620
end
1721

18-
function ZonedDateTime(utc_datetime::DateTime, timezone::VariableTimeZone, zone::FixedTimeZone)
19-
if timezone.cutoff !== nothing && utc_datetime >= timezone.cutoff
20-
throw(UnhandledTimeError(timezone))
21-
end
22-
23-
return new(utc_datetime, timezone, zone)
24-
end
22+
return ZonedDateTime{VariableTimeZone}(utc_datetime, timezone, zone)
2523
end
2624

2725
"""

src/tzdata/compile.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ using Dates: parse_components
44

55
using ...TimeZones: TIME_ZONE_CACHE
66
using ...TimeZones: TimeZones, TimeZone, FixedTimeZone, VariableTimeZone, Transition, Class
7-
using ...TimeZones: rename
7+
using ...TimeZones: name_parts, rename
88
using ..TZData: TimeOffset, ZERO, MIN_GMT_OFFSET, MAX_GMT_OFFSET, MIN_SAVE, MAX_SAVE,
99
ABS_DIFF_OFFSET
1010

@@ -697,7 +697,7 @@ function compile(tz_source::TZSource, dest_dir::AbstractString; kwargs...)
697697
empty!(TIME_ZONE_CACHE)
698698

699699
for (tz, class) in results
700-
parts = split(TimeZones.name(tz), '/')
700+
parts = name_parts(TimeZones.name(tz))
701701
tz_path = joinpath(dest_dir, parts...)
702702
tz_dir = dirname(tz_path)
703703

test/arithmetic.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ spring_zdt = ZonedDateTime(spring, warsaw)
7171
)
7272
@test results == expected
7373
@test length(results) == 2
74-
@test results isa StepRange{ZonedDateTime}
74+
@test results isa StepRange{<:ZonedDateTime}
7575
end
7676

7777
@testset "date-period" begin
@@ -89,7 +89,7 @@ spring_zdt = ZonedDateTime(spring, warsaw)
8989
)
9090
@test results == expected
9191
@test length(results) == 2
92-
@test results isa StepRange{ZonedDateTime}
92+
@test results isa StepRange{<:ZonedDateTime}
9393
end
9494

9595
@testset "ambiguous" begin

test/interpret.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ long = VariableTimeZone("Test/LongGap", [
125125
])
126126

127127
# A time zone with an unnecessary transition that typically is hidden to the user
128-
hidden = VariableTimeZone("Test/HiddenTransition", [
128+
hidden = VariableTimeZone("Test/Hidden", [
129129
Transition(DateTime(1800,1,1,0), zone["T+1"])
130130
Transition(DateTime(1900,1,1,0), zone["T+0"])
131131
Transition(DateTime(1935,4,1,2), zone["T+1"]) # The hidden transition

0 commit comments

Comments
 (0)