Marcus
Tox - Some structs and functions to work with dates, times, periods, and intervals
Hello,
I have released Tox 0.1.0, a little library for working with DateTime, NaiveDateTime, Date, and Time. It also adds Tox-Period and Tox-Interval. My motivation was to have some date-time calculation functions that work with any time zone database and different calendars. So you can use Tox with TimeZoneInfo, Tz, and Tzdata.
For the tests and examples, I use the Calendar.ISO and the calendars Cldr.Calendar.Coptic, Cldr.Calendar.Ethiopic, and Cldr.Calendar.Persian implemented by @kip.
Some examples:
add:
iex> datetime = DateTime.from_naive!(~N[2020-07-19 12:00:00], "Europe/Berlin")
#DateTime<2020-07-19 12:00:00+02:00 CEST Europe/Berlin>
iex> Tox.DateTime.add(datetime, year: 1, month: 1, day: 1, hour: 1, minute: 1)
#DateTime<2021-08-20 13:01:00+02:00 CEST Europe/Berlin>
iex> Tox.DateTime.add(datetime, year: -1, month: 1, day: 1, hour: 1, minute: 1)
#DateTime<2019-08-20 13:01:00+02:00 CEST Europe/Berlin>
iex> Tox.DateTime.add(datetime, week: 1)
#DateTime<2020-07-26 12:00:00+02:00 CEST Europe/Berlin>
iex> datetime = DateTime.from_naive!(~N[2020-07-19 12:00:00], "Europe/Berlin")
...> |> DateTime.convert!(Cldr.Calendar.Coptic)
#DateTime<1736-11-12 12:00:00+02:00 CEST Europe/Berlin Cldr.Calendar.Coptic>
iex> Tox.DateTime.add(datetime, month: 1)
#DateTime<1736-12-12 12:00:00+02:00 CEST Europe/Berlin Cldr.Calendar.Coptic>
iex> Tox.DateTime.add(datetime, month: 2)
#DateTime<1736-13-05 12:00:00+02:00 CEST Europe/Berlin Cldr.Calendar.Coptic>
iex> DateTime.from_naive(~N[2020-03-29 02:30:00], "Europe/Berlin")
{:gap, #DateTime<2020-03-29 01:59:59.999999+01:00 CET Europe/Berlin>,
#DateTime<2020-03-29 03:00:00+02:00 CEST Europe/Berlin>}
iex> datetime = DateTime.from_naive!(~N[2020-03-29 01:30:00], "Europe/Berlin")
#DateTime<2020-03-29 01:30:00+01:00 CET Europe/Berlin>
iex> Tox.DateTime.add(datetime, hour: 1)
#DateTime<2020-03-29 03:30:00+02:00 CEST Europe/Berlin>
beginning_of_, end_of_:
iex> ~N[2020-07-12 11:12:13]
...> |> DateTime.from_naive!("America/Winnipeg")
...> |> Tox.DateTime.beginning_of_year()
#DateTime<2020-01-01 00:00:00-06:00 CST America/Winnipeg>
iex> ~N[2020-07-12 11:12:13]
...> |> DateTime.from_naive!("Europe/Berlin")
...> |> Tox.DateTime.end_of_year()
#DateTime<2020-12-31 23:59:59.999999+01:00 CET Europe/Berlin>
# In 1994 the year ended one day earlier in the time zone Pacific/Kirimati.
iex> ~N[1994-07-12 11:12:13]
...> |> DateTime.from_naive!("Pacific/Kiritimati")
...> |> Tox.DateTime.end_of_year()
#DateTime<1994-12-30 23:59:59.999999-10:00 -10 Pacific/Kiritimati>
iex> datetime = DateTime.from_naive!(~N[2020-07-15 12:00:00], "Europe/Berlin")
#DateTime<2020-07-15 12:00:00+02:00 CEST Europe/Berlin>
iex> Tox.DateTime.end_of_week(datetime)
#DateTime<2020-07-19 23:59:59.999999+02:00 CEST Europe/Berlin>
iex> Tox.DateTime.end_of_day(datetime)
#DateTime<2020-07-15 23:59:59.999999+02:00 CEST Europe/Berlin>
iex> Tox.DateTime.beginning_of_week(datetime)
#DateTime<2020-07-13 00:00:00+02:00 CEST Europe/Berlin>
iex> Tox.DateTime.beginning_of_day(datetime)
#DateTime<2020-07-15 00:00:00+02:00 CEST Europe/Berlin>
iex> datetime = DateTime.from_naive!(~N[2020-07-19 12:00:00], "Europe/Berlin")
#DateTime<2020-07-19 12:00:00+02:00 CEST Europe/Berlin>
iex> datetime = DateTime.convert!(datetime, Cldr.Calendar.Coptic)
#DateTime<1736-11-12 12:00:00+02:00 CEST Europe/Berlin Cldr.Calendar.Coptic>
iex> Tox.DateTime.end_of_year(datetime)
#DateTime<1736-13-05 23:59:59.999999+02:00 CEST Europe/Berlin Cldr.Calendar.Coptic>
Tox.Period, Tox.Interval:
iex> now = DateTime.from_naive!(~N[2020-07-12 21:33:43], "America/New_York")
iex> period = Tox.Period.new!(month: 1)
#Tox.Period<P1M>
iex> interval = Tox.Interval.new!(Tox.DateTime.beginning_of_month(now), period)
#Tox.Interval<[2020-07-01T00:00:00-04:00/P1M[>
iex> Tox.Interval.contains?(interval, now)
true
iex> Tox.Interval.since_start(interval, now, :millisecond)
{:ok, 1028023000}
iex> Tox.Interval.until_ending(interval, now, :millisecond)
{:ok, 1650377000}
iex> Tox.Interval.until_ending(interval, Tox.DateTime.add(now, month: 1), :millisecond)
:error
Most Liked
josevalim
This looks great @Marcus!
We were recenetly having discussions about adding similar functionality directly to Elixir, so if you are interested in joining the discussion, we would love your $.02 on it: Support :week or :day units in Date.add/3 by sorentwo · Pull Request #10199 · elixir-lang/elixir · GitHub
josevalim
To add to that, most of Timex functionality has been included into Elixir itself at this point. The pending parts are:
- multiple timezone support - built into Elixir but requires tzdata
- datetime formatting - it will be in the next Elixir release (v1.11)
- datetime parsing - requires timex
- datetime shifting - requires timex or tox
So I like Tox exactly because it solves one problem well. Now we only need someone to cover or extract parsing from timex and we will have all functionality either in Elixir or available in a meal-piece fashion. ![]()
LostKobrakai
This is mostly due to the fact that elixir doesn’t implement functionality, which doesn’t soundly cover the complexity involved, which takes effort and therefore time. E.g. what does month: 1 do when being added to 31. of January? Will it add 31 days, 30 days or result in 27. of February as the last day of the next month?
Given the constant movement I can however understand that it’s not easy to keep up to date at all times.
Last Post!
LostKobrakai
Thinking a bit more about it, that makes sense. For :gap it can just jump and ambiguity doesn’t even happen, because it’s not dealing with an naive datetime, which would apply twice, but with an fixed datetime + fixed duration to get to another one.
Popular in Announcing
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
- #code-sync
- #javascript
- #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
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #hex
- #security









