Skip to content

Update :utc_datetime handling #49

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions lib/ecto/adapters/sqlite3.ex
Original file line number Diff line number Diff line change
Expand Up @@ -241,24 +241,19 @@ defmodule Ecto.Adapters.SQLite3 do

@impl Ecto.Adapter
def loaders(:utc_datetime_usec, type) do
[&Codec.datetime_decode/1, type]
[&Codec.utc_datetime_decode/1, type]
end

@impl Ecto.Adapter
def loaders(:utc_datetime, type) do
[&Codec.datetime_decode/1, type]
[&Codec.utc_datetime_decode/1, type]
end

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

@impl Ecto.Adapter
def loaders(:datetime, type) do
[&Codec.datetime_decode/1, type]
end

@impl Ecto.Adapter
def loaders(:date, type) do
[&Codec.date_decode/1, type]
Expand Down Expand Up @@ -326,11 +321,26 @@ defmodule Ecto.Adapters.SQLite3 do
[type, &Codec.time_encode/1]
end

@impl Ecto.Adapter
def dumpers(:utc_datetime, type) do
[type, &Codec.utc_datetime_encode/1]
end

@impl Ecto.Adapter
def dumpers(:utc_datetime_usec, type) do
[type, &Codec.utc_datetime_encode/1]
end

@impl Ecto.Adapter
def dumpers(:naive_datetime, type) do
[type, &Codec.naive_datetime_encode/1]
end

@impl Ecto.Adapter
def dumpers(:naive_datetime_usec, type) do
[type, &Codec.naive_datetime_encode/1]
end

@impl Ecto.Adapter
def dumpers({:array, _}, type) do
[type, &Codec.json_encode/1]
Expand Down
23 changes: 12 additions & 11 deletions lib/ecto/adapters/sqlite3/codec.ex
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,13 @@ defmodule Ecto.Adapters.SQLite3.Codec do

def decimal_decode(_), do: :error

def datetime_decode(nil), do: {:ok, nil}

def datetime_decode(val) do
# TODO: Should we be preserving the timezone? SQLite3 stores everything
# shifted to UTC. sqlite_ecto2 used a custom field type "TEXT_DATETIME"
# to preserve the original string inserted. But I don't know if that
# is desirable or not.
#
# @warmwaffles 2021-02-28
case DateTime.from_iso8601(val) do
{:ok, dt, _offset} -> {:ok, dt}
def utc_datetime_decode(nil), do: {:ok, nil}

def utc_datetime_decode(val) do
with {:ok, naive} <- NaiveDateTime.from_iso8601(val),
{:ok, dt} <- DateTime.from_naive(naive, "Etc/UTC") do
{:ok, dt}
else
_ -> :error
end
end
Expand Down Expand Up @@ -95,6 +91,11 @@ defmodule Ecto.Adapters.SQLite3.Codec do
{:ok, value}
end

# Ecto does check this already, so there should be no need to handle errors
def utc_datetime_encode(%{time_zone: "Etc/UTC"} = value) do
{:ok, NaiveDateTime.to_iso8601(value)}
end

def naive_datetime_encode(value) do
{:ok, NaiveDateTime.to_iso8601(value)}
end
Expand Down
20 changes: 20 additions & 0 deletions test/ecto/adapters/sqlite3/codec_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,24 @@ defmodule Ecto.Adapters.SQLite3.CodecTest do
{:ok, ^decimal} = Codec.decimal_decode(1.2)
end
end

describe ".utc_datetime_decode/1" do
test "nil" do
assert {:ok, nil} = Codec.utc_datetime_decode(nil)
end

test "string" do
{:ok, dt} = DateTime.from_naive(~N[2021-08-25 10:58:59Z], "Etc/UTC")
assert {:ok, ^dt} = Codec.utc_datetime_decode("2021-08-25 10:58:59")

{:ok, dt} = DateTime.from_naive(~N[2021-08-25 10:58:59.111111], "Etc/UTC")
assert {:ok, ^dt} = Codec.utc_datetime_decode("2021-08-25 10:58:59.111111")
end

test "ignores timezone offset if present" do
{:ok, dt} = DateTime.from_naive(~N[2021-08-25 10:58:59.111111], "Etc/UTC")
assert {:ok, ^dt} = Codec.utc_datetime_decode("2021-08-25 10:58:59.111111Z")
assert {:ok, ^dt} = Codec.utc_datetime_decode("2021-08-25 10:58:59.111111+02:30")
end
end
end