Skip to content

Stop creating at a vector in interpret #289

@oxinabox

Description

@oxinabox

I have been poking around at at https://github.com/JuliaTime/TimeZones.jl/tree/cv/zdt-isbits-poc
The only reason creating a ZonedDateTime allocates is because of the lines in Interpret where a vector of interpetations is created and pushed to.

function interpret(local_dt::DateTime, tz::VariableTimeZone, ::Type{Local})
interpretations = ZonedDateTime[]
t = tz.transitions
n = length(t)
for i in transition_range(local_dt, tz, Local)
# Convert the local DateTime into UTC
utc_dt = local_dt - t[i].zone.offset
if utc_dt >= t[i].utc_datetime && (i == n || utc_dt < t[i + 1].utc_datetime)
push!(interpretations, ZonedDateTime(utc_dt, tz, t[i].zone))
end
end
return interpretations
end
function interpret(utc_dt::DateTime, tz::VariableTimeZone, ::Type{UTC})
range = transition_range(utc_dt, tz, UTC)
length(range) == 1 || error("Internal TimeZones error: A UTC DateTime should only have a single interpretation")
i = first(range)
return [ZonedDateTime(utc_dt, tz, tz.transitions[i].zone)]
end

I did a benchmark of replacing just the UTC case with a tuple.
This case is easy, we know there is only going to be one result.
Allocations all gone, time for creation goes from:

julia> @btime ZonedDateTime(Date(2011, 6, 1), tz"America/Winnipeg", from_utc=true)
  113.625 ns (1 allocation: 112 bytes)
2011-05-31T19:00:00-05:00

down to

julia> @btime ZonedDateTime(Date(2011, 6, 1), tz"America/Winnipeg", from_utc=true)
  85.499 ns (0 allocations: 0 bytes)
2011-05-31T19:00:00-05:00

This case is particularly important is it shows up when you do things like adding 1 hour to a zoned date time.
So just changing that the a Tuple might be nice.

The other case has 2 allocations.

julia> @btime ZonedDateTime(Date(2011, 6, 1), tz"America/Winnipeg")
  144.806 ns (2 allocations: 192 bytes)
2011-06-01T00:00:00-05:00

Because we alocate a vector and push to it.
So we can give a good error message if there is 0 or 2 results.
Probably the easiest way to remove this is to change the logic for the constructor to not use interpret at all,
or to make a interpret1_or_error function, ether way to avoid a vector and just give one result or throw the error then and there

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions