Skip to content

Commit

Permalink
Fix: preserve datetime precision after Timex.shift/2
Browse files Browse the repository at this point in the history
Resolves #731

Unfortunately this broke with elixir-lang/elixir@5a583c7
which was release with Elixir 1.14.3

Elixir 1.13.4:

```
iex(3)> DateTime.utc_now() |> IO.inspect() |> DateTime.truncate(:second) |> IO.inspect() |> Timex.shift(minutes: 1) |> IO.inspect()
~U[2023-04-13 09:56:10.136274Z]
~U[2023-04-13 09:56:10Z]
~U[2023-04-13 09:57:10Z]
```

Elixir 1.14.4:

```
iex(1)> DateTime.utc_now() |> IO.inspect() |> DateTime.truncate(:second) |> IO.inspect() |> Timex.shift(minutes: 1) |> IO.inspect()
~U[2023-04-13 09:55:16.405357Z]
~U[2023-04-13 09:55:16Z]
~U[2023-04-13 09:56:16.000000Z]
```
  • Loading branch information
tfiedlerdejanze committed Apr 13, 2023
1 parent 7f584ce commit 98394f7
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

---

Expand Down
17 changes: 14 additions & 3 deletions lib/datetime/datetime.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -480,6 +484,13 @@ defimpl Timex.Protocol, for: DateTime do
err
end

defp retain_precision(
%DateTime{microsecond: {ms, _prevision}} = 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
Expand Down
40 changes: 40 additions & 0 deletions test/shift_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 98394f7

Please sign in to comment.