Skip to content

Timezone.convert/2 gives :error on DST gap instead of AmbiguousDateTime #789

Description

@tisdall

Looking at the source code, it looks like Timezone.convert/2 is supposed to return AmbiguousDateTime on :ambiguous AND :gap. However, in some cases it instead returns a not very helpful :error tuple.

Example with gap datetime:

iex(1)> use Timex
Timex.Timezone
iex(2)> naive_datetime_on_gap = ~N[2025-03-09 02:30:00]
~N[2025-03-09 02:30:00]
iex(3)> Timex.to_datetime(naive_datetime_on_gap, "America/Toronto")
{:error, {:could_not_resolve_timezone, "America/Toronto", 63908706600, :wall}}
iex(4)> Timezone.convert(naive_datetime_on_gap, "America/Toronto")
{:error, {:could_not_resolve_timezone, "America/Toronto", 63908706600, :wall}}

The error seems to come from calling Timezone.get("America/Toronto", ~N[2025-03-09 02:30:00]) and ultimately Tzdata.periods_for_time("America/Toronto", 63908706600, :wall) which comes back with []. From the period_for_time docs, this seems to be the expected return for "non-existing wall time for the zone".

I seem to be able to get around this issue if I skip Timezone.get:

iex(6)> a = Timezone.convert(naive_datetime_on_gap, %TimezoneInfo{full_name: "America/Toronto"})
#<Gap(#DateTime<2025-03-09 01:59:59.999999-05:00 EST America/Toronto> ~ #DateTime<2025-03-09 03:00:00-04:00 EDT America/Toronto>)>
iex(7)> a.__struct__
Timex.AmbiguousDateTime
iex(8)> a.before
#DateTime<2025-03-09 01:59:59.999999-05:00 EST America/Toronto>
iex(9)> a.after
#DateTime<2025-03-09 03:00:00-04:00 EDT America/Toronto>

I'm wondering if the following should be changed from

def convert(%NaiveDateTime{} = date, tz) do
with %TimezoneInfo{} = tzinfo <- get(tz, date) do
convert(date, tzinfo)
end
end

to something like:

  def convert(%NaiveDateTime{} = date, tz) do
    convert(date, %TimezoneInfo{full_name: tz})
  end

That seems to give the expected results:

iex(22)> Timezone.convert(~N[2025-03-09 02:30:00], %TimezoneInfo{full_name: "America/Toronto"})
#<Gap(#DateTime<2025-03-09 01:59:59.999999-05:00 EST America/Toronto> ~ #DateTime<2025-03-09 03:00:00-04:00 EDT America/Toronto>)>
iex(23)> Timezone.convert(~N[2025-11-02 01:30:00], %TimezoneInfo{full_name: "America/Toronto"})
#<Ambiguous(#DateTime<2025-11-02 01:30:00-04:00 EDT America/Toronto> ~ #DateTime<2025-11-02 01:30:00-05:00 EST America/Toronto>)>
iex(24)> Timezone.convert(~N[2025-11-12 01:30:00], %TimezoneInfo{full_name: "America/Toronto"})
#DateTime<2025-11-12 01:30:00-05:00 EST America/Toronto>
iex(25)> Timezone.convert(~N[2025-11-12 01:30:00], %TimezoneInfo{full_name: "NOT FOUND"})
{:error, :time_zone_not_found}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions