Marcus

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

josevalim

Creator of Elixir

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: https://github.com/elixir-lang/elixir/pull/10199

12
Post #2
josevalim

josevalim

Creator of Elixir

To add to that, most of Timex functionality has been included into Elixir itself at this point. The pending parts are:

  1. multiple timezone support - built into Elixir but requires tzdata
  2. datetime formatting - it will be in the next Elixir release (v1.11)
  3. datetime parsing - requires timex
  4. 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. :slight_smile:

LostKobrakai

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.

Marcus

Marcus

@dimitarvp the example above show just Tox.DateTime, but Tox contains also Tox.NaiveDateTime, Tox.Date and Tox.Time all with here own add/2 function. The idea was not a Tox API but Tox as a namespace for some helpful modules and structs. It is the very first version an I am open to suggestions.

baldwindavid

baldwindavid

@Marcus This looks great. Date/time handling is one of the concepts in Elixir that I’m never quite sure whether to use a library or whether it is available in core and I’m just not understanding it. It does seem like some of these things are evolving to get pulled into core though. I’ve previously been using the Timex library to fill in the gaps of necessary functionality. Would this be a direct alternative to that or is this different in its goals? It’s fine either way as it is nice to have multiple options.

Where Next?

Popular in Libraries Top

marcuslankenau
I feel kind of stuck with the absence of a proper xml library for Elixir. Currently I use SweetXML which was ok for me more or less to pa...
New
zoltanszogyenyi
Hey everyone :wave: Excited to join this forum - I am one of the founders and current project maintainers of a popular and open-source U...
New
deadtrickster
I’ve just released stable versions of my Prometheus Elixir libs: Elixir client [docs]; Ecto collector [docs]; Plugs instrumenter/Export...
New
kevinlang
Hey all, We have made an Ecto3 Adapter for SQLite3, ecto_sqlite3! We have successfully on-boarded the full suite of integration tests (...
New
praveenperera
FastRSS Parse RSS feeds very quickly: This is rust NIF built using rustler Uses the RSS rust crate to do the actual RSS parsing Speed...
New
achempion
Hi, I would like to tell about my initiative to further maintain and develop Waffle project which is the fork of Arc library. The progre...
New
woylie
Flop is an Elixir library that applies filtering, ordering and pagination parameters to your Ecto queries. offset-based pagination with...
New
ahamez
Hi everyone, I’ve been working on this protobuf library for 3 years. We use it in the company I work for, EasyMile, to communicate with ...
New
gabrielpoca
Hello everyone! I want to share with you something that I’m really proud of: https://stillstatic.io/ Still is a static site builder for...
New
mtrudel
Bandit is an HTTP server for Plug and WebSock apps. Bandit is written entirely in Elixir and is built atop Thousand Island. It can serve...
New

Other popular topics Top

JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 30048 115
New
shahryarjb
Hello, I get Persian date from my client and convert it to normal calendar like this: def jalali_string_to_miladi_english_number(persi...
New
sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New

Sub Categories:

We're in Beta

About us Mission Statement