Current section
Files
Jump to
Current section
Files
README.md
Calendar=======### (formerly known as Kalends)[](https://travis-ci.org/lau/calendar)[](http://hexdocs.pm/calendar/)[](https://hex.pm/packages/calendar)Calendar is a date and time library for Elixir.The Olson/Eggert "Time Zone Database" is used. Years 1 through 9999are supported.## Getting startedAdd Calendar as a dependency to an Elixir project by adding it to your mix.exs file:```elixirdefp deps do [ {:calendar, "~> 0.10.0"}, ]end```Also add `tzdata` to the list of applications in the mix.exs file:```elixir def application do [applications: [:logger, :tzdata]] end```Then run `mix deps.get` which will fetch Calendar via the hex package manager.## Upgrading from versions earlier than 0.10.0Calendar 0.10.0 supports Tzdata ~> 0.5.1 as well as ~> 0.1.7With Tzdata 0.5.1 it is now necessary to have tzdata in the application listas described above in the "Getting started" secion.## TypesCalendar has 4 basic types of structs:* `Date` - a simple date without time e.g. `2015-12-24`* `Time` - a simple time without a date e.g. `14:30:00` or `15:21:12.532985`* `NaiveDateTime` - datetimes without timezone information e.g. `2015-12-24 14:30:00`* `DateTime` - datetimes where the proper timezone name is known e.g. `2015-12-24 14:30:00` in `America/New_York` or `2015-12-24 17:30:00` in `Etc/UTC`## Polymorphism and protocolsThe functions of each module are appropriate for that type. For instance the `Date` module has a function `next_day!` that returns a `Date` struct for the next day of a provided date. Any Calendar type that contains a date can be used as an argument. So in addition to `Date`, a `DateTime` or `NaiveDateTime` can be used. Also erlang-style tuples with a date or date-time can be used. Example:```elixir{2015, 12, 24} |> Calendar.Date.next_day!%Calendar.Date{day: 25, month: 12, year: 2015}```And using a NaiveDateTime containing the date 2015-12-24 would also return a Date struct for 2015-12-25:```elixirCalendar.NaiveDateTime.from_erl!({{2015, 12, 24}, {13, 45, 55}}) |> Calendar.Date.next_day!%Calendar.Date{day: 25, month: 12, year: 2015}```In the same fashion other tuples with at least the same amount of information can be used with other modules. E.g.` NaiveDateTime`, `DateTime`, `Time` structs can be used in the `Time` module because they all contain an hour, minute and second. `DateTime` structs and erlang style datetime tuples can be used in the `NaiveDateTime` module because they contain a date and a time.## Date examplesThe Date module is used for handling dates.```elixir# You can create a new date with the from_erl! function:> jan_first = {2015, 1, 1} |> Calendar.Date.from_erl!%Calendar.Date{day: 1, month: 1, year: 2015}# Get a date that is 10000 days ahead of that one> ten_k_days_later = jan_first |> Calendar.Date.advance!(10000)%Calendar.Date{day: 19, month: 5, year: 2042}# Is it friday?> jan_first |> Calendar.Date.friday?false# What day of the week is it?> jan_first |> Calendar.Date.day_of_week_name"Thursday"# In Spanish by passing :es as language codejan_first |> Calendar.Date.day_of_week_name :es"jueves"# Use the DateTime module to get the time right now and# pipe it to the Date module to get the week number> Calendar.DateTime.now_utc |> Calendar.Date.week_number{2015, 28}# Pipe the week number tuple into another function to get a list# of the dates for that week> Calendar.DateTime.now_utc |> Calendar.Date.week_number |> Calendar.Date.dates_for_week_number[%Calendar.Date{day: 6, month: 7, year: 2015}, %Calendar.Date{day: 7, month: 7, year: 2015}, %Calendar.Date{day: 8, month: 7, year: 2015}, %Calendar.Date{day: 9, month: 7, year: 2015}, %Calendar.Date{day: 10, month: 7, year: 2015}, %Calendar.Date{day: 11, month: 7, year: 2015}, %Calendar.Date{day: 12, month: 7, year: 2015}]```## NaiveDateTimeUse NaiveDateTime modules when you have a date-time, but do not know thetimezone.```elixir# An erlang style datetime tuple advanced 10 seconds{{1999, 12, 31}, {23, 59, 59}} |> Calendar.NaiveDateTime.advance!(10)%Calendar.NaiveDateTime{day: 1, hour: 0, min: 0, month: 1, sec: 9, usec: nil, year: 2000}# Parse a "C Time" string.> {:ok, ndt} = "Wed Apr 9 07:53:03 2003" |> Calendar.NaiveDateTime.Parse.asctime{:ok, %Calendar.NaiveDateTime{day: 9, hour: 7, min: 53, month: 4, sec: 3, usec: nil, year: 2003}}# NaiveDateTime.Format.asctime can take a naive datetime and format it# as a as a C time string. We format the NaiveDateTime struct we just got from# parsing and get the same result as the original input:> ndt |> Calendar.NaiveDateTime.Format.asctime"Wed Apr 9 07:53:03 2003"```## DateTime usage examplesFor these example first either alias DateTime with this command: `alias Calendar.DateTime` or for use within a module add `use Calendar` to the module.The time right now for a specified time zone:```elixircph = DateTime.now! "Europe/Copenhagen"%Calendar.DateTime{abbr: "CEST", day: 5, hour: 21, min: 59, month: 10, sec: 24, std_off: 3600, timezone: "Europe/Copenhagen", usec: 678805, utc_off: 3600, year: 2014}```Get a DateTime struct for the 4th of October 2014 at 23:44:32 in the city ofMontevideo:```elixir{:ok, mvd} = DateTime.from_erl {{2014,10,4},{23,44,32}}, "America/Montevideo"{:ok, %Calendar.DateTime{abbr: "UYT", day: 4, hour: 23, min: 44, month: 10, sec: 32, std_off: 0, timezone: "America/Montevideo", usec: nil, utc_off: -10800, year: 2014}}```A DateTime struct is now assigned to the variable `mvd`. Let's get a DateTimestruct for the same time in the London time zone:```elixirlondon = mvd |> DateTime.shift_zone! "Europe/London"%Calendar.DateTime{abbr: "BST", day: 5, hour: 3, min: 44, month: 10, sec: 32, std_off: 3600, timezone: "Europe/London", usec: nil, utc_off: 0, year: 2014}```...and then in UTC:```elixirlondon |> DateTime.shift_zone! "Etc/UTC"%Calendar.DateTime{abbr: "UTC", day: 5, hour: 2, min: 44, month: 10, sec: 32, std_off: 0, timezone: "Etc/UTC", usec: nil, utc_off: 0, year: 2014}```Transforming a DateTime to a string in ISO 8601 / RFC 3339 format:```elixir> mvd |> DateTime.Format.rfc3339"2014-10-04T23:44:32-03:00"# or ISO 8601 basic> mvd |> DateTime.Format.iso_8601_basic"20141004T234432-0300"```Format as a unix timestamp:```elixirmvd |> DateTime.Format.unix1412477072```Parsing an RFC 3339 timestamp as UTC:```elixir{:ok, parsed} = DateTime.Parse.rfc3339_utc "2014-10-04T23:44:32.4999Z"{:ok, %Calendar.DateTime{abbr: "UTC", day: 4, usec: 499900, hour: 23, min: 44, month: 10, sec: 32, std_off: 0, timezone: "Etc/UTC", utc_off: 0, year: 2014}}# Format the parsed DateTime as ISO 8601 Basicparsed |> DateTime.Format.iso_8601_basic"20141004T234432Z"```Transform a DateTime struct to an Erlang style tuple:```elixircph |> DateTime.to_erl{{2014, 10, 5}, {21, 59, 24}}```Make a new DateTime from a tuple and advance it 1800 seconds.```elixirDateTime.from_erl!({{2014,10,4},{23,44,32}}, "Europe/Oslo") |> DateTime.advance(1800){:ok, %Calendar.DateTime{abbr: "CEST", day: 5, hour: 0, min: 14, month: 10, sec: 32, std_off: 3600, timezone: "Europe/Oslo", usec: nil, utc_off: 3600, year: 2014}}```## String formattingCalendar has polymorphic string formatting that does not get you intotrouble by silently using fake data.If you need a well known format, such as RFC 3339 the `DateTime.Format` and`NaiveDateTime.Format` modules have functions for a lot of those. In case youwant to do something custom or want to format simple `Date`s or `Time`s, youcan use the `Strftime` module. It uses formatting strings already known fromthe strftime "standard".The strftime function takes all the struct types: Date, Time, DateTime,NaiveDateTime and datetime tuples. You just have to make sure that theconversion specs (the codes with the %-signs) are appropriate for whatever isinput.```elixir# a Date struct works fine with these conversion specs (%a, %d, %m, %y)# because they just require a dateCalendar.Date.from_erl!({2014,9,6}) |> Calendar.Strftime.strftime "%a %d.%m.%y"{:ok, "Sat 06.09.14"}# A tuple like this is treated as a NaiveDateTime and also works because# it contains a date.{{2014,9,6}, {12, 13, 34}} |> Calendar.Strftime.strftime "%a %d.%m.%y"{:ok, "Sat 06.09.14"}# Trying to use date conversion specs and passing a Time struct results in an# error because a Time struct does not have the year or any other of the# data necessary for the string "%a %d.%m.%y"Calendar.Time.from_erl!({12, 30, 59}) |> Calendar.Strftime.strftime "%a %d.%m.%y"{:error, :missing_data_for_conversion_spec}```## DocumentationDocumentation can be found at http://hexdocs.pm/calendar/## EctoIf you want to use Calendar with Ecto, there is a library for that:Calecto https://github.com/lau/calectoThis makes it easy to save the different types of time and daterepresentations to a database. And later work with them in an easy andsafe manner.## PhoenixIf you want to use Calendar with Phoenix, there is a library thattakes care of that: https://github.com/lau/phoenix_calendar## Raison d'êtreThe purpose of Calendar is to have an easy to use library for handlingdates, time and datetimes that gives correct results.Instead of treating everything as a datetime, the differenttypes (Date, Time, NaiveDateTime, DateTime) provide clarity and safetyfrom certain bugs.Before Calendar, there was no Elixir library withcorrect time zone support. The timezone information was laterextracted from Calendar into the Tzdata library.## "use" macroYou can then call Calendar functions like this: `Calendar.DateTime.now_utc`. But in order to avoid typing Calendar all the time you can add `use Calendar` to your modules. This aliases Calendar modules such as `DateTime`, `Time`, `Date` and `NaiveDateTime`. Which means that you can call for instance `DateTime.now_utc` without writing `Calendar.` Example:```elixirdefmodule NewYearsHttpLib do use Calendar def httpdate_new_years(year) do {:ok, dt} = DateTime.from_erl({{year,1,1},{0,0,0}}, "Etc/UTC") DateTime.Format.httpdate(dt) end # Calling httpdate_new_years(2015) will return # "Thu, 01 Jan 2015 00:00:00 GMT"end```## Name change from Kalends, upgrade instructions.For existing users of Kalends: Kalends has changed its name to Calendar. To upgrade:- In your code replace all instances of `Kalends` with `Calendar`- In your code replace all instances of `:kalends` with `:calendar`- In case you are also using Kalecto, it has changed its name to [Calecto](https://github.com/lau/calecto). In a similair fashion replace `Kalecto` with `Calecto` and `:kalecto` with `:calecto`- In your `mix.exs` file make sure you are specifying a valid version of :calendar## Known bugsThere are no confirmed bugs as this is written. But if you do find a problem,please create an issue on the GitHub page: https://github.com/lau/calendar## LicenseCalendar is released under the MIT license. See the LICENSE file.