A few weeks ago my friend and colleague Martin Svalin and I paired on an experimental Elixir application, implementing the forward portion of the store and forward pattern. In doing so, we wrote three very small date and time related functions that we extracted to a library of its own. Mostly as an afterthought, GoodTimes was born.

Over the next few days we quickly fleshed it out with proper tests and expanded on it to handle a multitude of different time units. As I was going to bed, just hours after releasing version 0.5.0, a mail dropped into my inbox, informing me that the Elixir sips screencast had released an episode (number 145) on our little hobby project. As the upload of that particular episode to the media server had failed, I had to wait until the next day to watch it. The episode writeup was very kind and encouraging though. Thrilled and excited, it was hard falling asleep that night being giddy like a child the night before Christmas.

The actual episode made me ecstatic. Omitted from the writeup was comments like "unbelievably nice" and "I'm proud that we have this". Needless to say, Martin and I renewed our efforts with vigour, hacking on most of our spare time on GoodTimes. The week after saw the release of 0.6.0, but then we started to prune, organise, and edit our up until this point organically grown collection of functions. We debated questions on scope, structure, documentation, testing, and more. As we did so, we dove deeper and deeper into Elixir. I think it's safe to say that the last few weeks have accelerated my learning and increased my understanding of Elixir tenfold. Martin even made his first commit to Elixir by fixing a doctest bug!

So, it's with great pleasure and without further ado we present GoodTimes 1.0.0!

With a few exceptions, the entire library operates on and returns Erlang datetimes; a tuple of two three-element tuples representing a date and a time respectively. In fact, apart from a few conversion functions GoodTimes completely relies on Erlang's Calendar module and it's implementation of the Gregorian calendar; though this entails that dates before year 0 are not supported. We only deal with UTC (Coordinated Universal Time) and ISO weeks - hence weeks start with Mondays.

At it's core, the main GoodTimes module adds a number of expressive functions to shift a datetime by a given number of time units. For example, a_week_ago will give you exactly what it claims; the date and time a week ago. And 2 |> hours_from_now or 10 |> days_after {{2016, 2, 27}, {10, 30, 0}} hold no secrets.

Building upon these core functions are the generators of GoodTimes.Generate, allowing expressions such as all_hours_from_now |> Enum.take 5; in this case returning the next five datetimes stepping forward an hour at a time.

The boundary finding functions of GoodTimes.Boundary enables you to find the first second of any given time unit, e.g. now |> beginning_of_year will return {{2015, 1, 1}, {0, 0, 0}}.

The GoodTimes.Convert module deals with converting between dates and times while GoodTimes.Date adds a few expressive functions such as today and yesterday, returning dates rather than datetimes.

This whirlwind tour of the library only scratches the surface and I hope you'll find the library itself as useful as we enjoyed building it. As always, ideas on how to improve and expand GoodTimes are most welcome.