Skip to content

Commit 7f3e2a3

Browse files
committed
Pull optimization into shift_zone_for_iso_days_utc
1 parent d214b5b commit 7f3e2a3

2 files changed

Lines changed: 48 additions & 76 deletions

File tree

lib/elixir/lib/calendar/datetime.ex

Lines changed: 28 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,9 @@ defmodule DateTime do
720720
Other time zone databases can be passed as argument or set globally.
721721
See the "Time zone database" section in the module docs.
722722
723+
Shifting to the `"Etc/UTC"` time zone always succeeds without
724+
consulting the `time_zone_database`.
725+
723726
## Examples
724727
725728
iex> {:ok, pacific_datetime} = DateTime.shift_zone(~U[2018-07-16 10:00:00Z], "America/Los_Angeles", FakeTimeZoneDatabase)
@@ -753,6 +756,28 @@ defmodule DateTime do
753756
|> shift_zone_for_iso_days_utc(calendar, precision, time_zone, time_zone_database)
754757
end
755758

759+
defp shift_zone_for_iso_days_utc(iso_days_utc, calendar, precision, "Etc/UTC", _time_zone_db) do
760+
{year, month, day, hour, minute, second, {microsecond, _}} =
761+
calendar.naive_datetime_from_iso_days(iso_days_utc)
762+
763+
datetime = %DateTime{
764+
calendar: calendar,
765+
year: year,
766+
month: month,
767+
day: day,
768+
hour: hour,
769+
minute: minute,
770+
second: second,
771+
microsecond: {microsecond, precision},
772+
std_offset: 0,
773+
utc_offset: 0,
774+
zone_abbr: "UTC",
775+
time_zone: "Etc/UTC"
776+
}
777+
778+
{:ok, datetime}
779+
end
780+
756781
defp shift_zone_for_iso_days_utc(iso_days_utc, calendar, precision, time_zone, time_zone_db) do
757782
case time_zone_db.time_zone_period_from_utc_iso_days(iso_days_utc, time_zone) do
758783
{:ok, %{std_offset: std_offset, utc_offset: utc_offset, zone_abbr: zone_abbr}} ->
@@ -1708,44 +1733,6 @@ defmodule DateTime do
17081733
add(datetime, amount_to_add * 60, :second, time_zone_database)
17091734
end
17101735

1711-
def add(
1712-
%{calendar: calendar, time_zone: "Etc/UTC"} = datetime,
1713-
amount_to_add,
1714-
unit,
1715-
_time_zone_database
1716-
)
1717-
when is_integer(amount_to_add) do
1718-
%{microsecond: {_, precision}} = datetime
1719-
1720-
if not is_integer(unit) and unit not in ~w(second millisecond microsecond nanosecond)a do
1721-
raise ArgumentError,
1722-
"unsupported time unit. Expected :day, :hour, :minute, :second, :millisecond, :microsecond, :nanosecond, or a positive integer, got #{inspect(unit)}"
1723-
end
1724-
1725-
precision = max(Calendar.ISO.time_unit_to_precision(unit), precision)
1726-
1727-
{year, month, day, hour, minute, second, {microsecond, _}} =
1728-
datetime
1729-
|> to_iso_days()
1730-
|> Calendar.ISO.shift_time_unit(amount_to_add, unit)
1731-
|> calendar.naive_datetime_from_iso_days()
1732-
1733-
%DateTime{
1734-
calendar: calendar,
1735-
year: year,
1736-
month: month,
1737-
day: day,
1738-
hour: hour,
1739-
minute: minute,
1740-
second: second,
1741-
microsecond: {microsecond, precision},
1742-
time_zone: "Etc/UTC",
1743-
zone_abbr: "UTC",
1744-
utc_offset: 0,
1745-
std_offset: 0
1746-
}
1747-
end
1748-
17491736
def add(%{calendar: calendar} = datetime, amount_to_add, unit, time_zone_database)
17501737
when is_integer(amount_to_add) do
17511738
%{
@@ -1785,6 +1772,9 @@ defmodule DateTime do
17851772
17861773
Allowed units are: `:year`, `:month`, `:week`, `:day`, `:hour`, `:minute`, `:second`, `:microsecond`.
17871774
1775+
If the datetime is in the `"Etc/UTC"` time zone, this function
1776+
always succeeds without consulting the `time_zone_database`.
1777+
17881778
This operation is equivalent to shifting the datetime wall clock
17891779
(in other words, the value as someone in that timezone would see
17901780
on their watch), then applying the time zone offset to convert it
@@ -1853,44 +1843,6 @@ defmodule DateTime do
18531843
@spec shift(Calendar.datetime(), Duration.duration(), Calendar.time_zone_database()) :: t
18541844
def shift(datetime, duration, time_zone_database \\ Calendar.get_time_zone_database())
18551845

1856-
def shift(%{calendar: calendar, time_zone: "Etc/UTC"} = datetime, duration, _time_zone_database) do
1857-
%{
1858-
year: year,
1859-
month: month,
1860-
day: day,
1861-
hour: hour,
1862-
minute: minute,
1863-
second: second,
1864-
microsecond: microsecond
1865-
} = datetime
1866-
1867-
{year, month, day, hour, minute, second, microsecond} =
1868-
calendar.shift_naive_datetime(
1869-
year,
1870-
month,
1871-
day,
1872-
hour,
1873-
minute,
1874-
second,
1875-
microsecond,
1876-
__duration__!(duration)
1877-
)
1878-
1879-
%DateTime{
1880-
year: year,
1881-
month: month,
1882-
day: day,
1883-
hour: hour,
1884-
minute: minute,
1885-
second: second,
1886-
microsecond: microsecond,
1887-
time_zone: "Etc/UTC",
1888-
zone_abbr: "UTC",
1889-
std_offset: 0,
1890-
utc_offset: 0
1891-
}
1892-
end
1893-
18941846
def shift(%{calendar: calendar} = datetime, duration, time_zone_database) do
18951847
%{
18961848
year: year,

lib/elixir/test/elixir/calendar/datetime_test.exs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -961,6 +961,18 @@ defmodule DateTimeTest do
961961
end
962962

963963
describe "shift_zone" do
964+
test "to Etc/UTC does not consult the time zone database" do
965+
dt =
966+
DateTime.from_naive!(
967+
~N[2018-07-16 12:00:00.123],
968+
"Europe/Copenhagen",
969+
FakeTimeZoneDatabase
970+
)
971+
972+
assert DateTime.shift_zone(dt, "Etc/UTC", EmptyTimeZoneDatabase) ==
973+
{:ok, ~U[2018-07-16 10:00:00.123Z]}
974+
end
975+
964976
test "with compatible calendar" do
965977
holocene_ndt = %NaiveDateTime{
966978
calendar: Calendar.Holocene,
@@ -1195,4 +1207,12 @@ defmodule DateTimeTest do
11951207
"unknown unit :months. Expected :year, :month, :week, :day, :hour, :minute, :second, :microsecond",
11961208
fn -> DateTime.shift(~U[2012-01-01 00:00:00Z], months: 12) end
11971209
end
1210+
1211+
test "shift/3 with Etc/UTC datetime does not consult the time zone database" do
1212+
assert DateTime.shift(~U[2000-01-01 00:00:00Z], [month: 1], EmptyTimeZoneDatabase) ==
1213+
~U[2000-02-01 00:00:00Z]
1214+
1215+
assert DateTime.shift(~U[2000-01-01 00:00:00.123Z], [hour: -1], EmptyTimeZoneDatabase) ==
1216+
~U[1999-12-31 23:00:00.123Z]
1217+
end
11981218
end

0 commit comments

Comments
 (0)