Changeset truncates Time, DateTime and NaiveDateTime to whole seconds
Ash 3.5.8 is removing microseconds from Time, DateTime and NaiveDateTime, which I think is a bug. I've noticed this in a create changeset. For example ~U[2025-05-09 08:02:21.294214Z] is truncated to ~U[2025-05-09 08:02:21Z].
14 Replies
do you have an example of code that does this?
I realise there is such a thing as :utc_datetime_usec which has microsec, but I'm trying to build a datalayer which reads existing UTC dateTime and Time to correct elixir types
attribute :datetime, :datetime, public?: true
attribute :time, :time, public?: true
Yes I've got a failed test on ash_neo4j on the 9-property-types branch https://github.com/diffo-dev/ash_neo4j/tree/9-property-types
GitHub
GitHub - diffo-dev/ash_neo4j at 9-property-types
Ash Neo4j datalayer. Contribute to diffo-dev/ash_neo4j development by creating an account on GitHub.
can you narrow it down a little?
There is a single test case that is failing, it is showing that ash create (where I inject a time property defined as above) is truncating the time / date_time and naive_datetime values in the Test.Type resource to whole seconds, actually in the changeset rather than the create.
The failing test is in ash_neo4j_test.exs and is “type node can be created using ash with properties”
It is comparing the injected and returned property values
Microseconds are important …

It is actually the Ecto.Type.cast which is truncating the microseconds, for example iex(13)> time = Time.utc_now()
~T[09:53:47.984771]
iex(14)> Ecto.Type.cast(:time, time)
{:ok, ~T[09:53:47]}
iex(15)> {:ok, ecto_time} = Ecto.Type.cast(:time, time)
{:ok, ~T[09:53:47]}
iex(16)> ecto_time
~T[09:53:47]
iex(17)> time == ecto_time
false
iex(18)> time |> Map.from_struct()
%{
microsecond: {984771, 6},
second: 47,
calendar: Calendar.ISO,
minute: 53,
hour: 9
}
iex(19)> ecto_time |> Map.from_struct()
%{microsecond: {0, 0}, second: 47, calendar: Calendar.ISO, minute: 53, hour: 9}
could you link me to the specific test?
🤔 actually I'm not sure I understand
If you're using just
:datetime
, it will truncate the usec
You shouldn't first Ecto.Type.cast
the value
you should use Ash.Type.cast_stored(type, initialized_constraints)
Yes I named it above. The issue is with microsecond. In Ecto.Type.Time microsecond is an integer, in Elixir’s Time it is a Calender.microsecond struct, so ecto doesn’t cast it.
Ash time.ex can fix by converting time struct to map or tuple ahead of cast, decapsulating microsecond.
I can PR ash if you like?
I’m not casting using Ecto, ash is in time.ex
I was just showing the issue with the cast
One easy way to test it in ash is by modifying time_test.exs to inject/expect times with and without microseconds.
I’m building a datalayer to support Time, DateTime, etc so I need a solution for time with usec. I’m aware of utc_datetime_usec in Ash. I’m not sure why Ash.Type.Time, Ash.Type.DateTime and Ash.Type.NaiveDateTime are lossy when they are used to store corresponding Elixir types but I think this is wrong.
So, the
:time
type explicitly does not support usecs
we just need to add :time_usec
that does
Just like :utc_datetime
does not support usec either
and you're meant to use :utc_datetime_usec
if you want usec precisionOK, we should have naive_datetime_usec too.
If the intent of existing types is to never have microseconds we should nerf the Calendar.microsecond struct in ash before the ecto cast or one day if someone fixes ecto to accept the elixir types completely rather than accidentally then ash users of these native types will still have zeroed microseconds in the database.
Do you want me to raise a PR along these lines?
I’m not sure it is explicit, looks accidental. It isn’t in the ash docs… I understand fixing it might be disruptive to those with ash in prod.
It may not be documented, but it is an intentional split, modelled after ecto, and I'm confident they wouldn't consider it a bug either (hence why they have
:time
and :time_usec
)
We only really did it because they did, though FWIW, and at the time we were making these initial types we were trying to stay close to Ecto's patterns
we have pretty explicit code for this, so I'm not really sure what you mean 🤔
But more concretely, we should add a :precision
option for the :time
type, and default it to :second
in the same way
and then we can define:
and add a short code for it.
PR/issues welcome for that 🙂Thanks Zach, will do
Have created https://github.com/ash-project/ash/issues/2022 and PR https://github.com/ash-project/ash/pull/2023
GitHub
Support time with microsecond precision, :time_usec · Issue #2022 ...
Ash supports types with short name :datetime and :utc_datetime_usec, but lacks support for time_usec. I'd prefer date, dateTime and time to behave like their Elixir counterparts rather than Ect...
GitHub
feat - support :time_usec by matt-beanland · Pull Request #2023 ·...
Contributor checklist
[ n/a] Bug fixes include regression tests
[✅] Chores
[✅] Documentation changes
[✅] Features include unit/acceptance tests
[n/a] Refactoring
[n/a ] Update dependencies