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!

First 6 of 6 Posts Switch mode

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 OP

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.

OvermindDL1

OvermindDL1

Is there a text version? I rarely am in a place where I can watch videos. ^.^;

Wooo.

Hmm, just having inclusive without exclusive is a bit painful at times, causes more checks have to be performed on occasion…

Honestly I’d think there should be separate structs for each datetime, date, and time. They cover different ranges and concepts.

Could always force a trailing modifier to distinguish, that way you know at the call site what it is (and things like include/exclude first/last bounds)? Or put it in the string somewhere.

+1

wojtekmach

wojtekmach OP

Hex Core Team

Sorry, no idea.

Could always force a trailing modifier to distinguish, that way you know at the call site what it is (and things like include/exclude first/last bounds)? Or put it in the string somewhere.

Yeah, notice I added a leading “T” in ~I"T09:00", maybe it’s a bit hard to see :slight_smile: But yeah, one sigil for different structs is probably the way to go, thanks for validating this.

Speaking of one sigil, there’s a sister library for this: GitHub - wojtekmach/calendar_recurrence: Recurrence is an Elixir library for working with recurring dates · GitHub. It’s even less stable so I’m not yet announcing it. Since RRULE-style recurring intervals are part of new ISO 8601 draft, we could use the sigil for yet another struct (that implements the protocol!): ~I"R3/2019-01-01/FREQ=DA;INTR=2" (every other day since 2019-01-01, total 3 dates). Maybe it’s complicating things too much though so that’s why I started with separate libraries, but yeah, that’s another thing I’m thinking about.

thojanssens1

thojanssens1

Hi. Thank you for this library!

I have watched the video, and it seems that Eric talks about intervals (among other concepts) as a possible general way to work with time. I.e. as an alternative to NaiveDateTime, DateTime, and so on.

But this library’s purpose is only to work with intervals, and includes some ideas shown by Eric for working with them, right?

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"?
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; or am I wrong?

wojtekmach

wojtekmach OP

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.

— All posts loaded —

Where Next?

Trending in Announcing Top

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...
387 14960 120
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
shahryarjb
The Chelekom project is a library of Phoenix and LiveView components generated via Mix tasks to fit developer needs seamlessly. One of i...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New
wintermeyer
There are three potential reasons for members of this forum to have a look at https://vutuv.de You are tired or annoyed of LinkedIn. Yo...
New

Other Trending Topics Top

type1fool
I just stumbled on a newly redesigned elixir-lang.org. :tada: It looks like @Software_Mansion did the work, and I think it is generally a...
New
akoutmos
@hugobarauna and I (Alex Koutmos) have been hard at work on writing a book on Nerves that takes you from simply blinking LEDs to building...
New
juhalehtonen
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New
bjorng
We want to introduce a new native datatype to Erlang: native records. Although replacing all tuple records with native records is not our...
New
spammy
I’m looking to build a personal workflow to quickly deploy web applications written in elixir/phoenix, for local consumption (ie not on t...
New
alexslade
Fly’s CEO posted this recently - Turn And Face The Strange · The Fly Blog It says that Fly is going all-in on sprites, which is a worry ...
New

We're in Beta

About us Mission Statement