{"id":13507729,"url":"https://github.com/lau/calendar","last_synced_at":"2026-02-22T10:35:41.664Z","repository":{"id":21509443,"uuid":"24828488","full_name":"lau/calendar","owner":"lau","description":"date-time and time zone handling in Elixir","archived":false,"fork":false,"pushed_at":"2024-01-05T11:56:33.000Z","size":836,"stargazers_count":461,"open_issues_count":4,"forks_count":35,"subscribers_count":9,"default_branch":"master","last_synced_at":"2024-04-01T10:22:02.996Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"jervisfm/ExampleCode","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lau.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2014-10-05T23:09:49.000Z","updated_at":"2024-03-16T05:08:08.000Z","dependencies_parsed_at":"2024-01-31T07:02:00.282Z","dependency_job_id":"db7406cb-aaec-4e68-b4b0-d11e1ab14a10","html_url":"https://github.com/lau/calendar","commit_stats":{"total_commits":412,"total_committers":22,"mean_commits":"18.727272727272727","dds":0.1189320388349514,"last_synced_commit":"c910efc8ca5088dc401e367c1fac81748d13f671"},"previous_names":["lau/kalends"],"tags_count":46,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lau%2Fcalendar","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lau%2Fcalendar/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lau%2Fcalendar/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lau%2Fcalendar/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lau","download_url":"https://codeload.github.com/lau/calendar/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246301963,"owners_count":20755512,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T02:00:38.295Z","updated_at":"2025-10-21T19:02:25.240Z","avatar_url":"https://github.com/lau.png","language":"Elixir","funding_links":[],"categories":["Date and Time"],"sub_categories":[],"readme":"Calendar\n=======\n\nCalendar is a datetime library for Elixir.\n\nProviding explicit types for datetimes, dates and times.\nFull timezone support via its sister package [tzdata](https://github.com/lau/tzdata).\n\nSafe parsing and formatting of standard formats (ISO, RFC, Unix, JS etc.)\nplus strftime formatting. Easy and safe interoperability with erlang style\ndate, time, datetime tuples. Extendable through protocols.\n\nRelated packages are available for [i18n](https://github.com/padde/calendar_translations) interoperability.\n\n[Documentation is available on hexdocs.](http://hexdocs.pm/calendar/)\n\n## Elixir standard library\n\nSince the Calendar library was started, a lot of functionality previously only available in the Calendar library is now also available in the Elixir standard library. With Elixir 1.8 and later you can do time zone conversion in the [DateTime module of the Elixir standard library](https://hexdocs.pm/elixir/DateTime.html) provided you are using the [tzdata library](https://github.com/lau/tzdata#getting-started).\n\nCertain features like more advanced formatting are not yet available in the Elixir standard library. In these case this library is useful.\n\n## Getting started\n\nAdd Calendar as a dependency to an Elixir project by adding it to your mix.exs file:\n\n```elixir\ndefp deps do\n  [  {:calendar, \"~\u003e 1.0.0\"},  ]\nend\n```\n\nThen run `mix deps.get` which will fetch Calendar via the hex package manager.\n\n## Types\n\nSince Elixir 1.3 the types used are now built into the Elixir standard library.\n\n* `Date` - a simple date without time e.g. `2015-12-24`\n* `Time` - a simple time without a date e.g. `14:30:00` or `15:21:12.532985`\n* `NaiveDateTime` - datetimes without timezone information e.g. `2015-12-24 14:30:00`\n* `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`\n\n## String formatting\n\nCalendar has polymorphic string formatting that does not get you into\ntrouble by silently using fake data.\n\nIf you need a well known format, such as RFC 3339 the `DateTime.Format` and\n`NaiveDateTime.Format` modules have functions for a lot of those. In case you\nwant to do something custom or want to format simple `Date`s or `Time`s, you\ncan use the `Strftime` module. It uses formatting strings already known from\nthe strftime \"standard\".\n\nThe strftime function takes all the struct types: Date, Time, DateTime,\nNaiveDateTime and datetime tuples. You just have to make sure that the\nconversion specs (the codes with the %-signs) are appropriate for whatever is\ninput.\n\n```elixir\n# a Date struct works fine with these conversion specs (%a, %d, %m, %y)\n# because they just require a date\nCalendar.Date.from_erl!({2014,9,6}) |\u003e Calendar.Strftime.strftime \"%a %d.%m.%y\"\n{:ok, \"Sat 06.09.14\"}\n# A tuple like this is treated as a NaiveDateTime and also works because\n# it contains a date.\n{{2014,9,6}, {12, 13, 34}} |\u003e Calendar.Strftime.strftime \"%a %d.%m.%y\"\n{:ok, \"Sat 06.09.14\"}\n# Trying to use date conversion specs and passing a Time struct results in an\n# error because a Time struct does not have the year or any other of the\n# data necessary for the string \"%a %d.%m.%y\"\nCalendar.Time.from_erl!({12, 30, 59}) |\u003e Calendar.Strftime.strftime \"%a %d.%m.%y\"\n{:error, :missing_data_for_conversion_spec}\n```\n\n## Polymorphism and protocols\n\nThe 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:\n\n```elixir\n{2015, 12, 24} |\u003e Calendar.Date.next_day!\n~D[2015-12-25]\n```\n\nAnd using a NaiveDateTime containing the date 2015-12-24 would also return a Date struct for 2015-12-25:\n\n```elixir\nCalendar.NaiveDateTime.from_erl!({{2015, 12, 24}, {13, 45, 55}}) |\u003e Calendar.Date.next_day!\n~D[2015-12-25]\n```\n\nIn 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.\n\n`File.lstat!/2` is an example of a function that returns datetime tuples.\nA datetime tuple can be used in place of a NaiveDateTime, Date or Time.\n```elixir\n# Returns the mtime of the file mix.exs\n\u003e File.lstat!(\"mix.exs\").mtime\n{{2015, 12, 31}, {14, 30, 26}}\n# Format this datetime using one of the NaiveDateTime fun\nFile.lstat!(\"mix.exs\").mtime |\u003e Calendar.NaiveDateTime.Format.asctime\n\"Thu Dec 31 14:30:26 2015\"\n# Using the tuple with the Date class, the date information is used\n\u003e File.lstat!(\"mix.exs\").mtime |\u003e Calendar.Date.day_of_week_name\n\"Thursday\"\n# We know from the erlang documentation that lstat! by default returns UTC.\n# But the tuple does not contain this information.\n# So we can explicitly cast the tuple to be a DateTime in UTC.\n# And then pipe that to the DateTime.Format.unix function in order to get a UNIX timestamp\n\u003e File.lstat!(\"mix.exs\").mtime |\u003e Calendar.NaiveDateTime.to_date_time_utc |\u003e Calendar.DateTime.Format.unix\n1451572226\n# String formatting\n\u003e File.lstat!(\"mix.exs\").mtime |\u003e Calendar.Strftime.strftime!(\"%H:%M:%S\")\n\"14:30:26\"\n```\n\n## Date examples\n\nThe Date module is used for handling dates.\n\n```elixir\n# You can create a new date with the from_erl! function:\n\u003e jan_first = {2015, 1, 1} |\u003e Calendar.Date.from_erl!\n~D[2015-01-01]\n# Get a date that is 10000 days ahead of that one\n\u003e ten_k_days_later = jan_first |\u003e Calendar.Date.add!(10000)\n~D[2042-05-19]\n# Is it friday?\n\u003e jan_first |\u003e Calendar.Date.friday?\nfalse\n# What day of the week is it?\n\u003e jan_first |\u003e Calendar.Date.day_of_week_name\n\"Thursday\"\n\n# Compare dates\n\u003e jan_first |\u003e Calendar.Date.before?({2015, 12, 24})\ntrue\n# Get the difference in days between two dates. Using the Elixir 1.3 Date sigil.\n\u003e jan_first |\u003e Calendar.Date.diff(~D[2015-12-24])\n-357\n# Because of protocols, datetimes can also be provided as arguments,\n# but only the date will be used\n\u003e jan_first |\u003e Calendar.Date.diff({{2015, 12, 24}, {9, 10, 10}})\n-357\n\n# Use the DateTime module to get the time right now and\n# pipe it to the Date module to get the week number\n\u003e Calendar.DateTime.now_utc |\u003e Calendar.Date.week_number\n{2015, 28}\n# Pipe the week number tuple into another function to get a list\n# of the dates for that week\n\u003e Calendar.DateTime.now_utc |\u003e Calendar.Date.week_number |\u003e Calendar.Date.dates_for_week_number\n[~D[2016-06-13], ~D[2016-06-14], ~D[2016-06-15], ~D[2016-06-16],\n ~D[2016-06-17], ~D[2016-06-18], ~D[2016-06-19]]\n```\n\n## NaiveDateTime\n\nUse NaiveDateTime modules when you have a date-time, but do not know the\ntimezone.\n\n```elixir\n# An erlang style datetime tuple advanced 10 seconds\n{{1999, 12, 31}, {23, 59, 59}} |\u003e Calendar.NaiveDateTime.add!(10)\n~N[2000-01-01 00:00:09]\n# Parse a \"C Time\" string.\n\u003e {:ok, ndt} = \"Wed Apr  9 07:53:03 2003\" |\u003e Calendar.NaiveDateTime.Parse.asctime\n{:ok, ~N[2003-04-09 07:53:03]}\n# Calendar.NaiveDateTime.Format.asctime can take a naive datetime and format it\n# as a as a C time string. We format the NaiveDateTime struct we just got from\n# parsing and get the same result as the original input:\n\u003e ndt |\u003e Calendar.NaiveDateTime.Format.asctime\n\"Wed Apr  9 07:53:03 2003\"\n# Compare with another naive datetime in the form of an erlang style datetime tuple\n# Returns the difference in seconds, microseconds and if it is before after or at the\n# same time\n\u003e ndt |\u003e Calendar.NaiveDateTime.diff({{2003, 4, 8}, {10, 0, 0}})\n{:ok, 78783, 0, :after}\n# There are also boolean functions to just find out if a naive datetime is before or\n# after another one\n\u003e ndt |\u003e Calendar.NaiveDateTime.after?(~N[2003-04-08 10:00:00])\ntrue\n```\n\n## DateTime usage examples\n\nThe time right now for a specified time zone:\n\n```elixir\ncph = Calendar.DateTime.now! \"Europe/Copenhagen\"\n%DateTime{calendar: Calendar.ISO, day: 14, hour: 5, microsecond: {496149, 6}, minute: 27, month: 6, second: 14, std_offset: 3600,\n time_zone: \"Europe/Copenhagen\", utc_offset: 3600, year: 2016, zone_abbr: \"CEST\"}\n```\n\nGet a DateTime struct for the 4th of October 2014 at 23:44:32 in the city of\nMontevideo:\n\n```elixir\n{:ok, mvd} = Calendar.DateTime.from_erl {{2014,10,4},{23,44,32}}, \"America/Montevideo\"\n{:ok,\n %DateTime{calendar: Calendar.ISO, day: 4, hour: 23, microsecond: {0, 0}, minute: 44, month: 10, second: 32, std_offset: 0,\n  time_zone: \"America/Montevideo\", utc_offset: -10800, year: 2014, zone_abbr: \"-03\"}}\n```\n\nA DateTime struct is now assigned to the variable `mvd`. Let's get a DateTime\nstruct for the same time in the London time zone:\n\n```elixir\nlondon = mvd |\u003e Calendar.DateTime.shift_zone!(\"Europe/London\")\n%DateTime{calendar: Calendar.ISO, day: 5, hour: 3, microsecond: {0, 0}, minute: 44, month: 10, second: 32, std_offset: 3600,\n time_zone: \"Europe/London\", utc_offset: 0, year: 2014, zone_abbr: \"BST\"}\n```\n\n...and then in UTC:\n\n```elixir\nlondon |\u003e Calendar.DateTime.shift_zone!(\"Etc/UTC\")\n%DateTime{calendar: Calendar.ISO, day: 5, hour: 2, microsecond: {0, 0}, minute: 44, month: 10, second: 32, std_offset: 0, time_zone: \"Etc/UTC\",\n utc_offset: 0, year: 2014, zone_abbr: \"UTC\"}\n```\n\nTransforming a DateTime to a string in ISO 8601 / RFC 3339 format:\n\n```elixir\n\u003e mvd |\u003e Calendar.DateTime.Format.rfc3339\n\"2014-10-04T23:44:32-03:00\"\n# or ISO 8601 basic\n\u003e mvd |\u003e Calendar.DateTime.Format.iso8601_basic\n\"20141004T234432-0300\"\n```\n\nFormat as a unix timestamp:\n\n```elixir\nmvd |\u003e Calendar.DateTime.Format.unix\n1412477072\n```\n\nFormat as milliseconds that can be used by JavaScript:\n\n```elixir\nmvd |\u003e Calendar.DateTime.Format.js_ms\n1412477072000\n# Can be used like this in Javascript: new Date(1412477072000)\n```\n\nParsing an RFC 3339 timestamp as UTC:\n\n```elixir\n{:ok, parsed} = Calendar.DateTime.Parse.rfc3339_utc \"2014-10-04T23:44:32.4999Z\"\n{:ok,\n %DateTime{calendar: Calendar.ISO, day: 4, hour: 23, microsecond: {499900, 4}, minute: 44, month: 10, second: 32, std_offset: 0, time_zone: \"Etc/UTC\",\n  utc_offset: 0, year: 2014, zone_abbr: \"UTC\"}}\n# Format the parsed DateTime as ISO 8601 Basic\nparsed |\u003e Calendar.DateTime.Format.iso8601_basic\n\"20141004T234432Z\"\n```\n\nTransform a DateTime struct to an Erlang style tuple:\n\n```elixir\ncph |\u003e Calendar.DateTime.to_erl\n{{2016, 6, 14}, {5, 27, 14}}\n```\n\nMake a new `%DateTime{}` struct in the future from a tuple by adding 1800 seconds.\n\n```elixir\nCalendar.DateTime.from_erl!({{2014,10,4},{23,44,32}}, \"Europe/Oslo\") |\u003e Calendar.DateTime.add(1800)\n{:ok,\n %DateTime{calendar: Calendar.ISO, day: 5, hour: 0, microsecond: {0, 0}, minute: 14, month: 10, second: 32, std_offset: 3600,\n  time_zone: \"Europe/Oslo\", utc_offset: 3600, year: 2014, zone_abbr: \"CEST\"}}\n```\n\nCreate DateTime struct from :os.timestamp / erlang \"now\" format:\n\n```\n\u003e {1457, 641101, 48030} |\u003e Calendar.DateTime.from_erlang_timestamp\n%DateTime{calendar: Calendar.ISO, day: 10, hour: 20, microsecond: {48030, 6}, minute: 18, month: 3, second: 21, std_offset: 0, time_zone: \"Etc/UTC\",\n utc_offset: 0, year: 2016, zone_abbr: \"UTC\"}\n```\n\n## Documentation\n\nDocumentation can be found at http://hexdocs.pm/calendar/\n\n\n## Elixir versions earlier than 1.3\n\nIf you are using an Elixir version earlier than 1.3, use Calendar version `~\u003e 0.14.2`\nThis version accepts the Calendar structs from earlier versions (e.g. `%Calendar.DateTime`)\nAnd additionally when running on Elixir 1.3 or higher, it accepts the built in calendar\ntypes present in Elixir 1.3 (e.g. `%DateTime`). But Calendar structs are always returned.\n\n## Raison d'être\n\nThe purpose of Calendar is to have an easy to use library for handling\ndates, time and datetimes that gives correct results.\n\nInstead of treating everything as a datetime, the different\ntypes (Date, Time, NaiveDateTime, DateTime) provide clarity and safety\nfrom certain bugs.\n\nBefore Calendar, there was no Elixir library with\ncorrect time zone support. The timezone information was later\nextracted from Calendar into the Tzdata library.\n\n## Video presentation with some Calendar examples\n\nIn [a talk from ElixirConf 2015](http://img.youtube.com/vi/keUbVvMJeKY/0.jpg) Calendar is featured. Specifically from around 27:07 into the video there are some\nCalendar examples.\n\n[![Talk from ElixirConf 2015](http://img.youtube.com/vi/keUbVvMJeKY/0.jpg)](http://www.youtube.com/watch?v=keUbVvMJeKY)\n\n## Trouble shooting\n\nProblem: an error like this occours:\n\n```\n** (exit) an exception was raised:\n    ** (ArgumentError) argument error\n        (stdlib) :ets.lookup(:tzdata_current_release, :release_version)\n        lib/tzdata/release_reader.ex:41: Tzdata.ReleaseReader.current_release_from_table/0\n        lib/tzdata/release_reader.ex:13: Tzdata.ReleaseReader.simple_lookup/1\n```\n\nSolution: add :calendar to the application list in the mix.exs file of your\nproject.\n\n## License\n\nCalendar is released under the MIT license. See the LICENSE file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flau%2Fcalendar","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flau%2Fcalendar","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flau%2Fcalendar/lists"}