wojtekmach
CalendarInterval - Functions for working with calendar intervals
Hi everyone,
I created a library to work with calendar intervals:
- GitHub - wojtekmach/calendar_interval: Functions for working with calendar intervals · GitHub
- calendar_interval | Hex
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].
- [1] https://www.youtube.com/watch?v=Zm95cYAtAa8
- [2] https://www.ics.uci.edu/~alspaugh/cls/shr/allen.html
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
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.Rangefrom an interval such as~I"2018-01"? Or aDatefrom 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
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. ![]()
wojtekmach
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:
-
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.
-
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.NaiveDateTimeIntervalandCalendarInterval.TimeIntervalstructs. That would be one way to support DateTime intervals, which I also want to eventually have, too.I really like the
~Isigil 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.
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
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








