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

mhanberg
I just released the first version of Temple: an HTML DSL for Elixir and Phoenix! You can read this blog post or the docs for more info...
New
wmnnd
Hi there, for my project DBLSQD, I needed a file storage solution that is a bit more flexible than Arc. Because I thought others might f...
New
vic
Expat is a tiny experiment I did for extracting patterns and being able to reuse them (compose and share patterns between elixir librarie...
New
markmark206
simple_feature_flags is a tiny package that lets you turn features on or off based on which environment (e.g. localhost, staging, product...
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
josevalim
EDIT: since Ecto 3.0 final version is out, this post was amended to use the final versions in the instructions below. Hi everyone, We a...
New
tmbb
I’ve published the first version of my Makeup library. It’s a syntax highlighter for Elixir in the spirit of Pygments, Currently it highl...
New
mbuhot
EctoJob A transactional job queue built with Ecto, PostgreSQL and GenStage Available on Hex.pm: ecto_job | Hex Docs: API Reference — ec...
New
bryanjos
Hi, I wanted share a small library we at Revelry Labs made for rendering react components from the server side. There are instructions fo...
New
bluzky
You may know https://ui.shadcn.com/, a UI component library for React. I really love it’s design style and components. I’ve built some co...
381 12391 119
New

Other popular topics Top

TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
273 38985 115
New
peerreynders
Manning 2016 Halloween weekend sale via Deal of the Day Friday, October 28 - Half off all MEAPs - code WM102816LT Saturday, October 29 ...
326 29600 154
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
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
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 49522 488
New
beno
I will often find my self writing things similar to: case some_value do nil -&gt; something() "" -&gt; something() _ -&gt; someth...
New
Nvim
Elixir appears to be a superior language to Python. I don’t see any advantage of Python over Elixir. Are there any?
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New

Sub Categories:

We're in Beta

About us Mission Statement