Normalize DateTime casts to UTC for :time and :time_usec - #4792
Merged
Merged
Conversation
elixir-ecto#4775 moved :date, :naive_datetime and :naive_datetime_usec onto the UTC instant when given a non-UTC %DateTime{}, but cast_time/1 has no %DateTime{} clause, so it falls through to the map clauses and rebuilds a Time from the wall-clock fields. That leaves a schema casting one %DateTime{} into a :date field and a :time field with 2020-05-31 and 00:30:07, which is neither the UTC instant nor the submitted local time. Route %DateTime{} through cast_utc_datetime/1 first, the same way cast_date/1 and cast_naive_datetime/1 do.
Member
|
💚 💙 💜 💛 ❤️ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#4775 normalized non-UTC
%DateTime{}casts to the UTC instant for:date,:naive_datetimeand:naive_datetime_usec.cast_time/1has no%DateTime{}clause, so a DateTime falls through to the map clauses atlib/ecto/type.ex:1132/:1140and aTimeis rebuilt from the wall-clock fields.On
2020-06-01 00:30:07.008+02:00 CEST:So a schema casting that one value into a
:datefield and a:timefield stores2020-05-31with00:30:07- neither the UTC instant nor the submitted local time. Before #4775 the pair was at least consistently local.The new clause mirrors
cast_date/1(:1072) andcast_naive_datetime/1(:1202) exactly. A%DateTime{}already inEtc/UTCshort-circuits incast_utc_datetime/1, so the existingtime_usec cast from DateTimetest is unaffected.Same backward-incompatibility as #4775, and the same workaround: call
DateTime.to_time/1first if you want the wall time. Issue #4774 did not mention:time, which is presumably why it was missed.Verification
Elixir 1.17.3 / OTP 27, matching the CI matrix row.
mix deps.unlock --check-unused,mix compile --warnings-as-errors,mix test- the three commandsci.ymlruns - all exit 0: 97 doctests, 1492 tests, 0 failures, identical to a pristine checkout.Both new assertions go into tests that already exist. Reverting the clause fails both of them; keeping the clause but also truncating to the second fails only the
time_usecrow, since:timetruncates microseconds anyway.Disclosure: prepared with AI assistance (Claude). I reviewed it, reproduced the split date/time pair against
Ecto.Typedirectly, and verified the red-green tests myself.