mudasobwa
Adding days with a TZ is confusing
This question is more about semantics. Adding a :day to the DateTime instance changes the time value, which might be slightly confusing.
iex(1)> DateTime.new!(~D[2024-04-06], ~T[23:59:00], "Australia/Sydney")
...(1)> |> IO.inspect()
...(1)> |> DateTime.add(1, :day)
#DateTime<2024-04-06 23:59:00+11:00 AEDT Australia/Sydney>
#DateTime<2024-04-07 22:59:00+10:00 AEST Australia/Sydney>
Yes, adding the exact number of seconds (86_400) multiplied by the number of days results in this, but DateTime(dt, 5, :day) usually means the developer wants to get the very same hh:mm:ss time back.
Consider a ToDoApp (huh) example. If I am after producing a Stream of recurring events, I am surely to expect it won’t change the Time part alongside with a DST change.
I discovered this when people complained about Tempus.Slots.Stream.recurrent/3 is broken in an aforementioned way. I fixed it by reassuring Time part to stay intact in the produced stream elements, but it looks like a hack and I am positive, DateTime.add(…, …, :day) should not corrupt the Time anyway.
Thought?
Most Liked
LostKobrakai
This function relies on a contiguous representation of time, ignoring the wall time and timezone changes. For example, if you add one day when there are summer time/daylight saving time changes, it will also change the time forward or backward by one hour, so the elapsed time is precisely 24 hours.
This is called out in the docs as the intended behaviour.
If you want a continuous stream of datetimes at the same hour you can use a date and Date.add and turn those dates into datetimes with a time. Though you might run into invalid/duplicate datetimes this way if a date+time combination is in a DST change switch.
zachallaun
The docs for the new DateTime.shift/3 landing in 1.17 addresses this specifically and offers a suggestion using NaiveDateTime. It looks like this works:
iex(1)> Mix.install([:tz])
:ok
iex(2)> Calendar.put_time_zone_database(Tz.TimeZoneDatabase)
:ok
iex(3)> dt = DateTime.new!(~D[2024-04-06], ~T[23:59:00], "Australia/Sydney")
#DateTime<2024-04-06 23:59:00+11:00 AEDT Australia/Sydney>
iex(4)> dt |> DateTime.to_naive() |> NaiveDateTime.add(1, :day) |> DateTime.from_naive(dt.time_zone)
{:ok, #DateTime<2024-04-07 23:59:00+10:00 AEST Australia/Sydney>}
Edit to note that the above isn’t using anything in 1.17 and was copied from a 1.16 REPL.
LostKobrakai
DateTime.shift could make that decision, sure. But DateTime.add adds a certain (and fixed per amount/unit) amount of seconds to the supplied DateTime. That’s a different task. The issue is that it allows for :day with a constant of 86400 seconds when days are not always that long. That’s also the reason why :week, :month, :year weren’t added to DateTime.add, but handled into the new DateTime.shift.
Popular in Discussions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #javascript
- #code-sync
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








