wojtekmach

wojtekmach

Hex Core Team

CalendarInterval - Functions for working with calendar intervals

Hi everyone,

I created a library to work with calendar intervals:

Key ideas:

  • Time is enumerable: “2018” is a collection of “2018-01/2018-12” months, “2018-01-01/2018-12-31” days etc
  • Everything is an interval: “2018” is an interval of 1 year, or 12 months, or 365 days etc.
    A timestamp with microsecond precision is an interval 1 microsecond long
  • Allen’s Interval Algebra: formalism for relations between time intervals

Examples

use CalendarInterval

iex> ~I"2018-06".precision
:month

iex> CalendarInterval.next(~I"2018-12-31")
~I"2019-01-01"

iex> CalendarInterval.nest(~I"2018-06-15", :minute)
~I"2018-06-15 00:00/23:59"

iex> CalendarInterval.relation(~I"2018-01", ~I"2018-02/12")
:meets

iex> Enum.count(~I"2016-01-01/12-31")
366

References

This library is heavily inspired by “Exploring Time” talk by Eric Evans [1] where he mentioned the concept of “Countable Time” and introduced me to “Allen’s Interval Algebra” [2].

I’ve also given a talk about some of these ideas at Empex NYC 2018: video, slides.

Feedback is very appreciated here or in the issue tracker.

Happy hacking!

Most Liked

wojtekmach

wojtekmach

Hex Core Team

My takeaway from Eric Evans’ work is a time instant and a time interval are one and the same so in that vein the library does work as a potential (definitely incomplete :)) replacement for a general Date/NaiveDateTime manipulation library (there’s no support for Time and DateTime yet), we have things that are not directly related to ranges like:

iex> CalendarInterval.next(~I"2020-07-12")
~I"2020-07-13"

iex> CalendarInterval.prev(~I"2020-07", 2)
~I"2020-05"

Second question, how do I get for example a Date.Range from an interval such as ~I"2018-01" ? Or a Date from the first element of the interval ~I"2018-01" ?

iex> x = ~I"2020"; Date.range(NaiveDateTime.to_date(x.first), NaiveDateTime.to_date(x.last))
#DateRange<~D[2020-01-01], ~D[2020-12-31]>

iex> Date.to_iso8601(~I"2020-12".first)
"2020-12-01"

More generally, if I work with such intervals, at some point you have to work with Ecto or other components of the application, and you want Elixir types

Agreed.

OvermindDL1

OvermindDL1

Well this looks quite cool! Does it have an associated Ecto type for PostgreSQL datetime/date/time ranges? I’m using a lot of fragments for that currently and this whole style would fit really well. :slight_smile:

wojtekmach

wojtekmach

Hex Core Team

Well this looks quite cool!

Thanks!

Btw, I probably should have emphasised this more, to get background for all this, folks should really watch https://www.youtube.com/watch?v=Zm95cYAtAa8. I think Eric described very well the problems he was trying to solve with this approach; I cannot recommend this enough.

Does it have an associated Ecto type for PostgreSQL datetime/date/time ranges?

No and no concrete plans for this at the moment, but yes I do eventually want to support it.

There are at least two things that would need to happen first to support that:

  1. In Postgres, ranges may have inclusive (e.g. [1, 10]) or exclusive (e.g.: [1, 10)) bounds. This intervals implementation follows Elixir ranges and is always inclusive, but I can see how exclusive ranges can be useful too. I believe ISO 8601 intervals are always inclusive but I’d have to double-check the spec.

    Worth mentioning that both Postgrex’s and Timex’s intervals support inclusive and exclusive bounds.

  2. While date and datetime intervals are supported by design, time (without date) intervals are not. It is a bit tricky, because currently the underlying interval structs holds: first :: NaiveDateTime.t, last :: NaiveDateTime.t, precision: :year | :month | ..., so it can’t represent time. (I really don’t want to shoehorn it in into the same struct.)

    I’ve been actually thinking a lot about this recently, and I think I could define a protocol and have implementations for CalendarInterval.NaiveDateTimeInterval and CalendarInterval.TimeInterval structs. That would be one way to support DateTime intervals, which I also want to eventually have, too.

    I really like the ~I sigil and on one hand I’d like to have just that, but on the other hand I’m not yet sure how I feel about ~I"2019" representing one struct, but, say, ~I"T09:00/17:00" a completely different struct.

    Last but not least, as far as I could tell, ISO 8601 does not specify time (without date) intervals but that’s not necessarily a reason not to have them, I think they’d be quite useful.

Where Next?

Popular in Announcing Top

Crowdhailer
The latest release of Ace (0.10.0) includes serving content over HTTP/2. I have started writing a webserver to teach my self more about...
New
dominicletz
Hi, I thought I had posted my library before but seems I hadn’t. The project is still in early stages but it’s growing and so I think it...
New
tfwright
After working on it for a couple of months and using it in production for most of that time, today I’ve released LiveAdmin, a LiveView ba...
New
mikehostetler
I’m excited to announce Jido, a framework providing foundational primitives for building autonomous agent systems in Elixir. While develo...
New
aesmail
Hello guys, I have finally made it. I created an admin interface for a framework. It’s been on my todo list for years and with the curre...
New
alisinabh
Hey everyone i’ve developed a library for Jalaali calendar for elixir which supports converting Gregorian dates to Jalaali and vice vers...
New
riverrun
I’ve just released version 3 of Comeonin, a password hashing library. The following small changes have been made: changes to the NIF c...
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 52341 488
New
Crowdhailer
Experimenting with this code. OK.try do user &lt;- fetch_user(1) cart &lt;- fetch_cart(1) order = checkout(cart, user) save_orde...
New
Crowdhailer
Raxx is an alternative to Plug and is inspired by projects such as Rack(Ruby) and Ring(Clojure). 1.0-rc.1 is now available. To use it re...
New

Other popular topics Top

Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
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 30877 112
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
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
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
dogweather
I wrote this comment on r/haskell, and it’s not popular there. :wink: But I think I’m on to something… Haskell reminds me of Java, and e...
New

We're in Beta

About us Mission Statement