The time zones of times and of dates and times, and what offset they have (D-54).
A zone is :floating (none), {:offset, seconds} (a fixed offset from UTC), or {:iana, id}
(the id of a zone of the IANA time zone database, as "Europe/Paris"); see
Feel.Value.Time.zone/0. A fixed offset is its own offset. The offset of a zone id depends on
the date, since a zone changes between standard and daylight saving time, and has changed
over the years, so it is looked up in the database, which is the Elixir package tz: Elixir has
none of its own, and tz compiles one into the code, so nothing is read or fetched when a
value is used.
The functions here take a date and a time of day as a number of days (as
Date.to_gregorian_days/1 counts them) and a number of seconds in the day, so that any year of
FEEL, from -999,999,999 to 999,999,999, can be asked about. Before the database has the zone's
first rule it gives the zone's local mean time, and after its last it applies the rules of the
zone as they stand.
A local time that is not one time
The wall clock of a zone skips an hour when it goes to daylight saving time (the gap) and
repeats one when it goes back (the overlap). The specification does not say which instant a
local date and time in either is (notes 02, A-21). offset_at_wall/3 reads them as java.time does:
a time in the gap is shifted forward by the length of the gap, which is to use the offset from
before it, and a time in the overlap is the earlier of the two instants, which is the offset
from before the clocks went back.
A zone without a date
The zone id of a time of day (10:30:00@Europe/Paris) says nothing of the date, so it has no
offset of its own (notes 02, A-16). standard_offset/1 gives it the offset the zone has in
standard time, daylight saving time left out, on 2000-01-01.
iex> Feel.Value.Zone.offset_at_wall("Europe/Paris", Date.to_gregorian_days(~D[2021-01-02]), 36_000)
{:ok, 3600}
iex> Feel.Value.Zone.standard_offset("Australia/Melbourne")
{:ok, 36_000}
iex> Feel.Value.Zone.exists?("Foo/Bar")
false
Summary
Functions
Returns true when the id is a zone of the database.
The offset, in seconds, that a zone has at an instant, given as the UTC date and time of day:
{:ok, seconds}, or :error when the zone is not in the database.
The offset, in seconds, that a zone has at a local date and time: {:ok, seconds}, or :error
when the zone is not in the database.
The offset of a zone in standard time, on 2000-01-01, without the daylight saving time that the zone may have then. It is what a time of day with a zone id is read to have (see above).
Types
Functions
Returns true when the id is a zone of the database.
iex> Feel.Value.Zone.exists?("Europe/Paris")
true
iex> Feel.Value.Zone.exists?("Europe/Atlantis")
false
@spec offset_at_utc(String.t() | {:offset, integer()}, integer(), non_neg_integer()) :: {:ok, integer()} | :error
The offset, in seconds, that a zone has at an instant, given as the UTC date and time of day:
{:ok, seconds}, or :error when the zone is not in the database.
iex> Feel.Value.Zone.offset_at_utc("Europe/Paris", Date.to_gregorian_days(~D[2021-07-01]), 0)
{:ok, 7200}
iex> Feel.Value.Zone.offset_at_utc("Foo/Bar", 0, 0)
:error
@spec offset_at_wall(String.t() | {:offset, integer()}, integer(), non_neg_integer()) :: {:ok, integer()} | :error
The offset, in seconds, that a zone has at a local date and time: {:ok, seconds}, or :error
when the zone is not in the database.
A fixed offset is its own. A time in a gap has the offset from before it, and a time that occurs twice has the earlier offset (see above).
iex> Feel.Value.Zone.offset_at_wall("Europe/Paris", Date.to_gregorian_days(~D[2021-07-01]), 0)
{:ok, 7200}
iex> Feel.Value.Zone.offset_at_wall({:offset, -18_000}, 0, 0)
{:ok, -18_000}
iex> Feel.Value.Zone.offset_at_wall("Foo/Bar", 0, 0)
:error
The offset of a zone in standard time, on 2000-01-01, without the daylight saving time that the zone may have then. It is what a time of day with a zone id is read to have (see above).
iex> Feel.Value.Zone.standard_offset("Europe/Paris")
{:ok, 3600}
iex> Feel.Value.Zone.standard_offset("Etc/UTC")
{:ok, 0}
iex> Feel.Value.Zone.standard_offset("Foo/Bar")
:error