Skip to content

Commit

Permalink
Store external storage index in time zones
Browse files Browse the repository at this point in the history
  • Loading branch information
omus committed Sep 29, 2020
1 parent 2e0c296 commit 74c0be3
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 27 deletions.
1 change: 1 addition & 0 deletions src/TimeZones.jl
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ include("indexable_generator.jl")

include("class.jl")
include("utcoffset.jl")
include("external.jl")
include(joinpath("types", "timezone.jl"))
include(joinpath("types", "fixedtimezone.jl"))
include(joinpath("types", "variabletimezone.jl"))
Expand Down
15 changes: 15 additions & 0 deletions src/external.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
struct ExternalField{T}
table::Dict{T,Int}
data::Vector{T}
end

ExternalField{T}() where T = ExternalField{T}(Dict{T,Int}(), Vector{T}())

function add!(x::ExternalField{T}, val::T) where T
get!(x.table, val) do
push!(x.data, val)
lastindex(x.data)
end
end

Base.getindex(x::ExternalField, i::Int) = x.data[i]
42 changes: 41 additions & 1 deletion src/types/fixedtimezone.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,20 @@ const FIXED_TIME_ZONE_REGEX = r"""
A `TimeZone` with a constant offset for all of time.
"""
struct FixedTimeZone <: TimeZone
mutable struct FixedTimeZone <: TimeZone
name::String
offset::UTCOffset
index::Int

function FixedTimeZone(name::String, utc_offset::UTCOffset)
tz = new(name, utc_offset)
tz.index = add!(_TIME_ZONES, tz)
return tz
end
end

function FixedTimeZone(name::AbstractString, utc_offset::UTCOffset)
FixedTimeZone(convert(String, name), utc_offset)
end

"""
Expand All @@ -48,6 +59,21 @@ function FixedTimeZone(name::AbstractString, utc_offset::Second, dst_offset::Sec
FixedTimeZone(name, UTCOffset(utc_offset, dst_offset))
end

# Overload serialization to ensure that `FixedTimeZone` serialization doesn't transfer
# state information which is specific to the current Julia process.
function Serialization.serialize(s::AbstractSerializer, tz::FixedTimeZone)
Serialization.serialize_type(s, typeof(tz))
serialize(s, tz.name)
serialize(s, tz.offset)
end

function Serialization.deserialize(s::AbstractSerializer, ::Type{FixedTimeZone})
name = deserialize(s)
offset = deserialize(s)

return FixedTimeZone(name, offset)
end

# https://en.wikipedia.org/wiki/ISO_8601#Coordinated_Universal_Time_(UTC)
const UTC_ZERO = FixedTimeZone("Z", 0)

Expand Down Expand Up @@ -95,3 +121,17 @@ end

name(tz::FixedTimeZone) = tz.name
rename(tz::FixedTimeZone, name::AbstractString) = FixedTimeZone(name, tz.offset)

function Base.:(==)(a::FixedTimeZone, b::FixedTimeZone)
return a.name == b.name && a.offset == b.offset
end

function Base.hash(tz::FixedTimeZone, h::UInt)
h = hash(:timezone, h)
h = hash(tz.name, h)
return h
end

function Base.isequal(a::FixedTimeZone, b::FixedTimeZone)
return isequal(a.name, b.name) && isequal(a.offset, b.offset)
end
1 change: 1 addition & 0 deletions src/types/timezone.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const TIME_ZONE_CACHE = Dict{String,Tuple{TimeZone,Class}}()
const _TIME_ZONES = ExternalField{TimeZone}()

"""
TimeZone(str::AbstractString) -> TimeZone
Expand Down
32 changes: 30 additions & 2 deletions src/types/variabletimezone.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,49 @@ end

Base.isless(a::Transition, b::Transition) = isless(a.utc_datetime, b.utc_datetime)

function Base.:(==)(a::Transition, b::Transition)
return a.utc_datetime == b.utc_datetime && a.zone == b.zone
end

function Base.isequal(a::Transition, b::Transition)
return isequal(a.utc_datetime, b.utc_datetime) && isequal(a.zone, b.zone)
end

"""
VariableTimeZone
A `TimeZone` with an offset that changes over time.
"""
struct VariableTimeZone <: TimeZone
mutable struct VariableTimeZone <: TimeZone
name::String
transitions::Vector{Transition}
cutoff::Union{DateTime,Nothing}
index::Int

function VariableTimeZone(name::AbstractString, transitions::Vector{Transition}, cutoff::Union{DateTime,Nothing}=nothing)
new(name, transitions, cutoff)
tz = new(name, transitions, cutoff)
tz.index = add!(_TIME_ZONES, tz)
return tz
end
end

# Overload serialization to ensure that `VariableTimeZone` serialization doesn't transfer
# state information which is specific to the current Julia process.
function Serialization.serialize(s::AbstractSerializer, tz::VariableTimeZone)
Serialization.serialize_type(s, typeof(tz))
serialize(s, tz.name)
serialize(s, tz.transitions)
serialize(s, tz.cutoff)
end

function Serialization.deserialize(s::AbstractSerializer, ::Type{VariableTimeZone})
name = deserialize(s)
transitions = deserialize(s)
cutoff = deserialize(s)

return VariableTimeZone(name, transitions, cutoff)
end

name(tz::VariableTimeZone) = tz.name

function rename(tz::VariableTimeZone, name::AbstractString)
Expand Down
29 changes: 5 additions & 24 deletions src/types/zoneddatetime.jl
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
using Dates: AbstractDateTime, argerror, validargs

# Stores non-bits data outside of the `ZonedDateTime` structure
const _ZDT_FIELDS = Vector{Tuple{TimeZone,FixedTimeZone}}()

# Stored indexes into `_ZDT_FIELDS`
const _TZ_INDEX = Dict{Tuple{TimeZone,FixedTimeZone},Int}()


# """
# ZonedDateTime

Expand All @@ -16,38 +9,26 @@ const _TZ_INDEX = Dict{Tuple{TimeZone,FixedTimeZone},Int}()
struct ZonedDateTime <: AbstractDateTime
utc_datetime::DateTime
_tz_index::Int
_zone_index::Int

function ZonedDateTime(utc_datetime::DateTime, timezone::TimeZone, zone::FixedTimeZone)
return new(utc_datetime, _tz_index(timezone, zone))
return new(utc_datetime, timezone.index, zone.index)
end

function ZonedDateTime(utc_datetime::DateTime, timezone::VariableTimeZone, zone::FixedTimeZone)
if timezone.cutoff !== nothing && utc_datetime >= timezone.cutoff
throw(UnhandledTimeError(timezone))
end

return new(utc_datetime, _tz_index(timezone, zone))
end
end

function _tz_index(tz::TimeZone, zone::FixedTimeZone)
t = (tz, zone)

i = get!(_TZ_INDEX, t) do
push!(_ZDT_FIELDS, t)
lastindex(_ZDT_FIELDS)
return new(utc_datetime, timezone.index, zone.index)
end

return i
end

function Base.getproperty(zdt::ZonedDateTime, field::Symbol)
if field === :zone
tz, zone = _ZDT_FIELDS[getfield(zdt, :_tz_index)]
return zone
return _TIME_ZONES[getfield(zdt, :_zone_index)]::FixedTimeZone
elseif field === :timezone
tz, zone = _ZDT_FIELDS[getfield(zdt, :_tz_index)]
return tz
return _TIME_ZONES[getfield(zdt, :_tz_index)]::TimeZone
else
return getfield(zdt, field)
end
Expand Down

0 comments on commit 74c0be3

Please sign in to comment.