Skip to content

Commit c557310

Browse files
Consolidate shift_time_unit/3 for add/2 and shift/2 (#13472)
1 parent cf3a500 commit c557310

File tree

5 files changed

+39
-58
lines changed

5 files changed

+39
-58
lines changed

lib/elixir/lib/calendar/date.ex

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -708,12 +708,7 @@ defmodule Date do
708708
@spec add(Calendar.date(), integer()) :: t
709709
def add(%{calendar: Calendar.ISO} = date, days) do
710710
%{year: year, month: month, day: day} = date
711-
712-
{year, month, day} =
713-
Calendar.ISO.date_to_iso_days(year, month, day)
714-
|> Kernel.+(days)
715-
|> Calendar.ISO.date_from_iso_days()
716-
711+
{year, month, day} = Calendar.ISO.shift_days({year, month, day}, days)
717712
%Date{calendar: Calendar.ISO, year: year, month: month, day: day}
718713
end
719714

lib/elixir/lib/calendar/datetime.ex

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1640,30 +1640,28 @@ defmodule DateTime do
16401640
add(datetime, amount_to_add * 60, :second, time_zone_database)
16411641
end
16421642

1643-
def add(datetime, amount_to_add, unit, time_zone_database) when is_integer(amount_to_add) do
1643+
def add(%{calendar: calendar} = datetime, amount_to_add, unit, time_zone_database)
1644+
when is_integer(amount_to_add) do
16441645
%{
1646+
microsecond: {_, precision},
1647+
time_zone: time_zone,
16451648
utc_offset: utc_offset,
1646-
std_offset: std_offset,
1647-
calendar: calendar,
1648-
microsecond: {_, precision}
1649+
std_offset: std_offset
16491650
} = datetime
16501651

1651-
if not is_integer(unit) and
1652-
unit not in ~w(second millisecond microsecond nanosecond)a do
1652+
if not is_integer(unit) and unit not in ~w(second millisecond microsecond nanosecond)a do
16531653
raise ArgumentError,
16541654
"unsupported time unit. Expected :day, :hour, :minute, :second, :millisecond, :microsecond, :nanosecond, or a positive integer, got #{inspect(unit)}"
16551655
end
16561656

1657-
ppd = System.convert_time_unit(86400, :second, unit)
1658-
total_offset = System.convert_time_unit(utc_offset + std_offset, :second, unit)
16591657
precision = max(Calendar.ISO.time_unit_to_precision(unit), precision)
16601658

16611659
result =
16621660
datetime
16631661
|> to_iso_days()
1664-
# Subtract total offset in order to get UTC and add the integer for the addition
1665-
|> Calendar.ISO.add_day_fraction_to_iso_days(amount_to_add - total_offset, ppd)
1666-
|> shift_zone_for_iso_days_utc(calendar, precision, datetime.time_zone, time_zone_database)
1662+
|> Calendar.ISO.shift_time_unit(amount_to_add, unit)
1663+
|> apply_tz_offset(utc_offset + std_offset)
1664+
|> shift_zone_for_iso_days_utc(calendar, precision, time_zone, time_zone_database)
16671665

16681666
case result do
16691667
{:ok, result_datetime} ->

lib/elixir/lib/calendar/iso.ex

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1550,7 +1550,8 @@ defmodule Calendar.ISO do
15501550
end)
15511551
end
15521552

1553-
defp shift_days({year, month, day}, days) do
1553+
@doc false
1554+
def shift_days({year, month, day}, days) do
15541555
{year, month, day} =
15551556
date_to_iso_days(year, month, day)
15561557
|> Kernel.+(days)
@@ -1576,34 +1577,37 @@ defmodule Calendar.ISO do
15761577
{new_year, new_month, new_day}
15771578
end
15781579

1579-
defp shift_time_unit({year, month, day, hour, minute, second, microsecond}, value, unit)
1580-
when unit in [:second, :microsecond] do
1580+
@doc false
1581+
def shift_time_unit({year, month, day, hour, minute, second, microsecond}, value, unit)
1582+
when unit in [:second, :millisecond, :microsecond, :nanosecond] or is_integer(unit) do
15811583
{value, precision} = shift_time_unit_values(value, microsecond)
15821584

1583-
ppd = System.convert_time_unit(86400, :second, unit)
1584-
15851585
{year, month, day, hour, minute, second, {ms_value, _}} =
15861586
naive_datetime_to_iso_days(year, month, day, hour, minute, second, microsecond)
1587-
|> add_day_fraction_to_iso_days(value, ppd)
1587+
|> shift_time_unit(value, unit)
15881588
|> naive_datetime_from_iso_days()
15891589

15901590
{year, month, day, hour, minute, second, {ms_value, precision}}
15911591
end
15921592

1593-
defp shift_time_unit({hour, minute, second, microsecond}, value, unit)
1594-
when unit in [:second, :microsecond] do
1593+
def shift_time_unit({hour, minute, second, microsecond}, value, unit)
1594+
when unit in [:second, :millisecond, :microsecond, :nanosecond] or is_integer(unit) do
15951595
{value, precision} = shift_time_unit_values(value, microsecond)
15961596

1597-
time = {0, time_to_day_fraction(hour, minute, second, microsecond)}
1598-
amount_to_add = System.convert_time_unit(value, unit, :microsecond)
1599-
total = iso_days_to_unit(time, :microsecond) + amount_to_add
1600-
parts = Integer.mod(total, @parts_per_day)
1597+
{_days, day_fraction} =
1598+
shift_time_unit({0, time_to_day_fraction(hour, minute, second, microsecond)}, value, unit)
16011599

1602-
{hour, minute, second, {microsecond, _}} = time_from_day_fraction({parts, @parts_per_day})
1600+
{hour, minute, second, {microsecond, _}} = time_from_day_fraction(day_fraction)
16031601

16041602
{hour, minute, second, {microsecond, precision}}
16051603
end
16061604

1605+
def shift_time_unit({_days, _day_fraction} = iso_days, value, unit)
1606+
when unit in [:second, :millisecond, :microsecond, :nanosecond] or is_integer(unit) do
1607+
ppd = System.convert_time_unit(86400, :second, unit)
1608+
add_day_fraction_to_iso_days(iso_days, value, ppd)
1609+
end
1610+
16071611
defp shift_time_unit_values({0, _}, {_, original_precision}) do
16081612
{0, original_precision}
16091613
end

lib/elixir/lib/calendar/naive_datetime.ex

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -468,23 +468,21 @@ defmodule NaiveDateTime do
468468
end
469469

470470
def add(
471-
%{microsecond: {_, precision}, calendar: calendar} = naive_datetime,
471+
%{calendar: calendar, microsecond: {_, precision}} = naive_datetime,
472472
amount_to_add,
473473
unit
474474
)
475475
when is_integer(amount_to_add) do
476-
if not is_integer(unit) and
477-
unit not in ~w(second millisecond microsecond nanosecond)a do
476+
if not is_integer(unit) and unit not in ~w(second millisecond microsecond nanosecond)a do
478477
raise ArgumentError,
479478
"unsupported time unit. Expected :day, :hour, :minute, :second, :millisecond, :microsecond, :nanosecond, or a positive integer, got #{inspect(unit)}"
480479
end
481480

482-
ppd = System.convert_time_unit(86400, :second, unit)
483481
precision = max(Calendar.ISO.time_unit_to_precision(unit), precision)
484482

485483
naive_datetime
486484
|> to_iso_days()
487-
|> Calendar.ISO.add_day_fraction_to_iso_days(amount_to_add, ppd)
485+
|> Calendar.ISO.shift_time_unit(amount_to_add, unit)
488486
|> from_iso_days(calendar, precision)
489487
end
490488

lib/elixir/lib/calendar/time.ex

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ defmodule Time do
5050
calendar: Calendar.calendar()
5151
}
5252

53-
@parts_per_day 86_400_000_000
5453
@seconds_per_day 24 * 60 * 60
5554

5655
@doc """
@@ -524,17 +523,19 @@ defmodule Time do
524523

525524
unless valid? do
526525
raise ArgumentError,
527-
"unsupported time unit. Expected :hour, :minute, :second, :millisecond, " <>
528-
":microsecond, :nanosecond, or a positive integer, got #{inspect(unit)}"
526+
"unsupported time unit. Expected :hour, :minute, :second, :millisecond, :microsecond, :nanosecond, or a positive integer, got #{inspect(unit)}"
529527
end
530528

531-
amount_to_add = System.convert_time_unit(amount_to_add, unit, :microsecond)
532-
total = time_to_microseconds(time) + amount_to_add
533-
parts = Integer.mod(total, @parts_per_day)
529+
%{hour: hour, minute: minute, second: second, microsecond: microsecond} = time
530+
534531
precision = max(Calendar.ISO.time_unit_to_precision(unit), precision)
535532

536-
{hour, minute, second, {microsecond, _}} =
537-
calendar.time_from_day_fraction({parts, @parts_per_day})
533+
{hour, minute, second, {microsecond, _precision}} =
534+
Calendar.ISO.shift_time_unit(
535+
{hour, minute, second, microsecond},
536+
amount_to_add,
537+
unit
538+
)
538539

539540
%Time{
540541
hour: hour,
@@ -545,21 +546,6 @@ defmodule Time do
545546
}
546547
end
547548

548-
defp time_to_microseconds(%{
549-
calendar: Calendar.ISO,
550-
hour: 0,
551-
minute: 0,
552-
second: 0,
553-
microsecond: {0, _}
554-
}) do
555-
0
556-
end
557-
558-
defp time_to_microseconds(time) do
559-
iso_days = {0, to_day_fraction(time)}
560-
Calendar.ISO.iso_days_to_unit(iso_days, :microsecond)
561-
end
562-
563549
@doc """
564550
Shifts given `time` by `duration` according to its calendar.
565551

0 commit comments

Comments
 (0)