Skip to content

Commit 51b4645

Browse files
LostKobrakaiBenjamin Milde
andauthored
Update :utc_datetime handling (#49)
* Store utc_datetime as offsetless iso8601 and ignore offset when fetching from the db * Update tests to not use sigil_U Co-authored-by: Benjamin Milde <[email protected]>
1 parent 8ff7c8d commit 51b4645

File tree

3 files changed

+49
-18
lines changed

3 files changed

+49
-18
lines changed

lib/ecto/adapters/sqlite3.ex

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -241,24 +241,19 @@ defmodule Ecto.Adapters.SQLite3 do
241241

242242
@impl Ecto.Adapter
243243
def loaders(:utc_datetime_usec, type) do
244-
[&Codec.datetime_decode/1, type]
244+
[&Codec.utc_datetime_decode/1, type]
245245
end
246246

247247
@impl Ecto.Adapter
248248
def loaders(:utc_datetime, type) do
249-
[&Codec.datetime_decode/1, type]
249+
[&Codec.utc_datetime_decode/1, type]
250250
end
251251

252252
@impl Ecto.Adapter
253253
def loaders(:naive_datetime, type) do
254254
[&Codec.naive_datetime_decode/1, type]
255255
end
256256

257-
@impl Ecto.Adapter
258-
def loaders(:datetime, type) do
259-
[&Codec.datetime_decode/1, type]
260-
end
261-
262257
@impl Ecto.Adapter
263258
def loaders(:date, type) do
264259
[&Codec.date_decode/1, type]
@@ -326,11 +321,26 @@ defmodule Ecto.Adapters.SQLite3 do
326321
[type, &Codec.time_encode/1]
327322
end
328323

324+
@impl Ecto.Adapter
325+
def dumpers(:utc_datetime, type) do
326+
[type, &Codec.utc_datetime_encode/1]
327+
end
328+
329+
@impl Ecto.Adapter
330+
def dumpers(:utc_datetime_usec, type) do
331+
[type, &Codec.utc_datetime_encode/1]
332+
end
333+
329334
@impl Ecto.Adapter
330335
def dumpers(:naive_datetime, type) do
331336
[type, &Codec.naive_datetime_encode/1]
332337
end
333338

339+
@impl Ecto.Adapter
340+
def dumpers(:naive_datetime_usec, type) do
341+
[type, &Codec.naive_datetime_encode/1]
342+
end
343+
334344
@impl Ecto.Adapter
335345
def dumpers({:array, _}, type) do
336346
[type, &Codec.json_encode/1]

lib/ecto/adapters/sqlite3/codec.ex

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,13 @@ defmodule Ecto.Adapters.SQLite3.Codec do
4343

4444
def decimal_decode(_), do: :error
4545

46-
def datetime_decode(nil), do: {:ok, nil}
47-
48-
def datetime_decode(val) do
49-
# TODO: Should we be preserving the timezone? SQLite3 stores everything
50-
# shifted to UTC. sqlite_ecto2 used a custom field type "TEXT_DATETIME"
51-
# to preserve the original string inserted. But I don't know if that
52-
# is desirable or not.
53-
#
54-
# @warmwaffles 2021-02-28
55-
case DateTime.from_iso8601(val) do
56-
{:ok, dt, _offset} -> {:ok, dt}
46+
def utc_datetime_decode(nil), do: {:ok, nil}
47+
48+
def utc_datetime_decode(val) do
49+
with {:ok, naive} <- NaiveDateTime.from_iso8601(val),
50+
{:ok, dt} <- DateTime.from_naive(naive, "Etc/UTC") do
51+
{:ok, dt}
52+
else
5753
_ -> :error
5854
end
5955
end
@@ -95,6 +91,11 @@ defmodule Ecto.Adapters.SQLite3.Codec do
9591
{:ok, value}
9692
end
9793

94+
# Ecto does check this already, so there should be no need to handle errors
95+
def utc_datetime_encode(%{time_zone: "Etc/UTC"} = value) do
96+
{:ok, NaiveDateTime.to_iso8601(value)}
97+
end
98+
9899
def naive_datetime_encode(value) do
99100
{:ok, NaiveDateTime.to_iso8601(value)}
100101
end

test/ecto/adapters/sqlite3/codec_test.exs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,24 @@ defmodule Ecto.Adapters.SQLite3.CodecTest do
8080
{:ok, ^decimal} = Codec.decimal_decode(1.2)
8181
end
8282
end
83+
84+
describe ".utc_datetime_decode/1" do
85+
test "nil" do
86+
assert {:ok, nil} = Codec.utc_datetime_decode(nil)
87+
end
88+
89+
test "string" do
90+
{:ok, dt} = DateTime.from_naive(~N[2021-08-25 10:58:59Z], "Etc/UTC")
91+
assert {:ok, ^dt} = Codec.utc_datetime_decode("2021-08-25 10:58:59")
92+
93+
{:ok, dt} = DateTime.from_naive(~N[2021-08-25 10:58:59.111111], "Etc/UTC")
94+
assert {:ok, ^dt} = Codec.utc_datetime_decode("2021-08-25 10:58:59.111111")
95+
end
96+
97+
test "ignores timezone offset if present" do
98+
{:ok, dt} = DateTime.from_naive(~N[2021-08-25 10:58:59.111111], "Etc/UTC")
99+
assert {:ok, ^dt} = Codec.utc_datetime_decode("2021-08-25 10:58:59.111111Z")
100+
assert {:ok, ^dt} = Codec.utc_datetime_decode("2021-08-25 10:58:59.111111+02:30")
101+
end
102+
end
83103
end

0 commit comments

Comments
 (0)