diff --git a/CHANGELOG.md b/CHANGELOG.md index 22d7c3fa..24c13ea0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Updated `Timex.now/1` typespec to remove the `AmbiguousDateTime` - Corrected pluralization rules for bg/cs/he/id/ro/ru +- Fixed `Timex.shift/2` to preserve the precision of the provided datetime --- diff --git a/lib/datetime/datetime.ex b/lib/datetime/datetime.ex index 25cb8e9e..8bd79ff5 100644 --- a/lib/datetime/datetime.ex +++ b/lib/datetime/datetime.ex @@ -447,13 +447,17 @@ defimpl Timex.Protocol, for: DateTime do err %DateTime{} = datetime when shift != 0 -> - DateTime.add(datetime, shift, :microsecond, Timex.Timezone.Database) + datetime + |> DateTime.add(shift, :microsecond, Timex.Timezone.Database) + |> retain_precision(datetime) %DateTime{} = datetime -> datetime - {{ty, _, _}, %DateTime{} = orig} when ty in [:gap, :ambiguous] and shift != 0 -> - DateTime.add(orig, shift, :microsecond, Timex.Timezone.Database) + {{ty, _, _}, %DateTime{} = original} when ty in [:gap, :ambiguous] and shift != 0 -> + original + |> DateTime.add(shift, :microsecond, Timex.Timezone.Database) + |> retain_precision(datetime) {{ty, _a, _b} = amb, _} when ty in [:gap, :ambiguous] -> amb @@ -480,6 +484,13 @@ defimpl Timex.Protocol, for: DateTime do err end + defp retain_precision( + %DateTime{microsecond: {ms, _precision}} = new_datetime, + %DateTime{microsecond: {_ms, precision}} = _original_datetime + ) do + %{new_datetime | microsecond: {ms, precision}} + end + defp logical_shift(datetime, []), do: datetime defp logical_shift(datetime, shifts) do diff --git a/test/shift_test.exs b/test/shift_test.exs index f82b0148..f3f36438 100644 --- a/test/shift_test.exs +++ b/test/shift_test.exs @@ -229,4 +229,44 @@ defmodule ShiftTests do expected = ~D[2017-12-31] |> Timex.to_datetime() assert expected === date end + + describe "DateTime does not change precision" do + test "seconds" do + datetime = Timex.shift(~U[2023-04-13 08:00:00Z], minutes: 1) + expected = ~U[2023-04-13 08:01:00Z] + assert expected === datetime + end + + test "milliseconds" do + datetime = Timex.shift(~U[2023-04-13 08:00:00.000Z], minutes: 1) + expected = ~U[2023-04-13 08:01:00.000Z] + assert expected === datetime + end + + test "microseconds" do + datetime = Timex.shift(~U[2023-04-13 08:00:00.000000Z], minutes: 1) + expected = ~U[2023-04-13 08:01:00.000000Z] + assert expected === datetime + end + end + + describe "NaiveDateTime does not change precision" do + test "seconds" do + datetime = Timex.shift(~N[2023-04-13 08:00:00Z], minutes: 1) + expected = ~N[2023-04-13 08:01:00Z] + assert expected === datetime + end + + test "milliseconds" do + datetime = Timex.shift(~N[2023-04-13 08:00:00.000Z], minutes: 1) + expected = ~N[2023-04-13 08:01:00.000Z] + assert expected === datetime + end + + test "microseconds" do + datetime = Timex.shift(~N[2023-04-13 08:00:00.000000Z], minutes: 1) + expected = ~N[2023-04-13 08:01:00.000000Z] + assert expected === datetime + end + end end