#
View Source Calendar behaviour (Elixir v1.15.7)

This module defines the responsibilities for working with calendars, dates, times and datetimes in Elixir.

It defines types and the minimal implementation for a calendar behaviour in Elixir. The goal of the calendar features in Elixir is to provide a base for interoperability rather than a full-featured datetime API.

For the actual date, time and datetime structs, see `Date`, `Time`, `NaiveDateTime`, and `DateTime`.

Types for year, month, day, and more are overspecified. For example, the `month/0` type is specified as an integer instead of `1..12`. This is because different calendars may have a different number of days per month.

# Summary

## Types

calendar()

A calendar implementation.

date()

Any map or struct that contains the date fields.

datetime()

Any map or struct that contains the datetime fields.

day()
day_fraction()

The internal time format is used when converting between calendars.

day_of_era()

A tuple representing the `day` and the `era`.

day_of_week()
era()
hour()
iso_days()

The internal date format that is used when converting between calendars.

microsecond()

Microseconds with stored precision.

minute()
month()
naive_datetime()

Any map or struct that contains the naive datetime fields.

second()
std_offset()

The time zone standard offset in seconds (typically not zero in summer times).

time()

Any map or struct that contains the time fields.

time_zone()

The time zone ID according to the IANA tz database (for example, `Europe/Zurich`).

time_zone_database()

Specifies the time zone database for calendar operations.

utc_offset()

The time zone UTC offset in seconds for standard time.

week()
year()
zone_abbr()

The time zone abbreviation (for example, `CET` or `CEST` or `BST`).

## Callbacks

date_to_string(year, month, day)

Converts the date into a string according to the calendar.

datetime_to_string( year, month, day, hour, minute, second, microsecond, time_zone, zone_abbr, utc_offset, std_offset )

Converts the datetime (with time zone) into a string according to the calendar.

day_of_era(year, month, day)

Calculates the day and era from the given `year`, `month`, and `day`.

day_of_week(year, month, day, starting_on)

Calculates the day of the week from the given `year`, `month`, and `day`.

day_of_year(year, month, day)

Calculates the day of the year from the given `year`, `month`, and `day`.

day_rollover_relative_to_midnight_utc()

Define the rollover moment for the calendar.

days_in_month(year, month)

Returns how many days there are in the given month of the given year.

iso_days_to_beginning_of_day(iso_days)

Converts the given `iso_days/0` to the first moment of the day.

iso_days_to_end_of_day(iso_days)

Converts the given `iso_days/0` to the last moment of the day.

leap_year?(year)

Returns `true` if the given year is a leap year.

months_in_year(year)

Returns how many months there are in the given year.

naive_datetime_from_iso_days(iso_days)

Converts `iso_days/0` to the calendar's datetime format.

naive_datetime_to_iso_days(year, month, day, hour, minute, second, microsecond)

Converts the datetime (without time zone) into the `iso_days/0` format.

naive_datetime_to_string(year, month, day, hour, minute, second, microsecond)

Converts the naive datetime (without time zone) into a string according to the calendar.

parse_date(t)

Parses the string representation for a date returned by `date_to_string/3` into a date tuple.

parse_naive_datetime(t)

Parses the string representation for a naive datetime returned by `naive_datetime_to_string/7` into a naive datetime tuple.

parse_time(t)

Parses the string representation for a time returned by `time_to_string/4` into a time tuple.

parse_utc_datetime(t)

Parses the string representation for a datetime returned by `datetime_to_string/11` into a datetime tuple.

quarter_of_year(year, month, day)

Calculates the quarter of the year from the given `year`, `month`, and `day`.

time_from_day_fraction(day_fraction)

Converts `day_fraction/0` to the calendar's time format.

time_to_day_fraction(hour, minute, second, microsecond)

Converts the given time to the `day_fraction/0` format.

time_to_string(hour, minute, second, microsecond)

Converts the time into a string according to the calendar.

valid_date?(year, month, day)

Should return `true` if the given date describes a proper date in the calendar.

valid_time?(hour, minute, second, microsecond)

Should return `true` if the given time describes a proper time in the calendar.

year_of_era(year, month, day)

Calculates the year and era from the given `year`.

## Functions

compatible_calendars?(calendar, calendar)

Returns `true` if two calendars have the same moment of starting a new day, `false` otherwise.

get_time_zone_database()

Gets the current time zone database.

put_time_zone_database(database)

Sets the current time zone database.

strftime(date_or_time_or_datetime, string_format, user_options \\ [])

Formats the given date, time, or datetime into a string.

truncate(microsecond_tuple, atom)

Returns a microsecond tuple truncated to a given precision (`:microsecond`, `:millisecond`, or `:second`).

# Types

Link to this type

# calendar()

View Source

    @type calendar() :: module()

A calendar implementation.

Link to this type

# date()

View Source

    @type date() :: %{
      optional(any()) => any(),
      calendar: calendar(),
      year: year(),
      month: month(),
      day: day()
    }

Any map or struct that contains the date fields.

Link to this type

# datetime()

View Source

    @type datetime() :: %{
      optional(any()) => any(),
      calendar: calendar(),
      year: year(),
      month: month(),
      day: day(),
      hour: hour(),
      minute: minute(),
      second: second(),
      microsecond: microsecond(),
      time_zone: time_zone(),
      zone_abbr: zone_abbr(),
      utc_offset: utc_offset(),
      std_offset: std_offset()
    }

Any map or struct that contains the datetime fields.

Link to this type

# day()

View Source

    @type day() :: pos_integer()

Link to this type

# day_fraction()

View Source

    @type day_fraction() ::
      {parts_in_day :: non_neg_integer(), parts_per_day :: pos_integer()}

The internal time format is used when converting between calendars.

It represents time as a fraction of a day (starting from midnight). `parts_in_day` specifies how much of the day is already passed, while `parts_per_day` signifies how many parts are there in a day.

Link to this type

# day_of_era()

View Source

    @type day_of_era() :: {day :: non_neg_integer(), era()}

A tuple representing the `day` and the `era`.

Link to this type

# day_of_week()

View Source

    @type day_of_week() :: non_neg_integer()

Link to this type

# era()

View Source

    @type era() :: non_neg_integer()

Link to this type

# hour()

View Source

    @type hour() :: non_neg_integer()

Link to this type

# iso_days()

View Source

    @type iso_days() :: {days :: integer(), day_fraction()}

The internal date format that is used when converting between calendars.

This is the number of days including the fractional part that has passed of the last day since `0000-01-01+00:00T00:00.000000` in ISO 8601 notation (also known as midnight 1 January BC 1 of the proleptic Gregorian calendar).

Link to this type

# microsecond()

View Source

    @type microsecond() :: {value :: non_neg_integer(), precision :: non_neg_integer()}

Microseconds with stored precision.

The precision represents the number of digits that must be used when representing the microseconds to external format. If the precision is `0`, it means microseconds must be skipped.

Link to this type

# minute()

View Source

    @type minute() :: non_neg_integer()

Link to this type

# month()

View Source

    @type month() :: pos_integer()

Link to this type

# naive_datetime()

View Source

    @type naive_datetime() :: %{
      optional(any()) => any(),
      calendar: calendar(),
      year: year(),
      month: month(),
      day: day(),
      hour: hour(),
      minute: minute(),
      second: second(),
      microsecond: microsecond()
    }

Any map or struct that contains the naive datetime fields.

Link to this type

# second()

View Source

    @type second() :: non_neg_integer()

Link to this type

# std_offset()

View Source

    @type std_offset() :: integer()

The time zone standard offset in seconds (typically not zero in summer times).

It must be added to `utc_offset/0` to get the total offset from UTC used for "wall time".

Link to this type

# time()

View Source

    @type time() :: %{
      optional(any()) => any(),
      hour: hour(),
      minute: minute(),
      second: second(),
      microsecond: microsecond()
    }

Any map or struct that contains the time fields.

Link to this type

# time_zone()

View Source

    @type time_zone() :: String.t()

The time zone ID according to the IANA tz database (for example, `Europe/Zurich`).

Link to this type

# time_zone_database()

View Source

    @type time_zone_database() :: module()

Specifies the time zone database for calendar operations.

Many functions in the `DateTime` module require a time zone database. By default, this module uses the default time zone database returned by `Calendar.get_time_zone_database/0`, which defaults to `Calendar.UTCOnlyTimeZoneDatabase`. This database only handles `Etc/UTC` datetimes and returns `{:error, :utc_only_time_zone_database}` for any other time zone.

Other time zone databases (including ones provided by packages) can be configured as default either via configuration:

    config :elixir, :time_zone_database, CustomTimeZoneDatabase

or by calling `Calendar.put_time_zone_database/1`.

See `Calendar.TimeZoneDatabase` for more information on custom time zone databases.

Link to this type

# utc_offset()

View Source

    @type utc_offset() :: integer()

The time zone UTC offset in seconds for standard time.

See also `std_offset/0`.

Link to this type

# week()

View Source

    @type week() :: pos_integer()

Link to this type

# year()

View Source

    @type year() :: integer()

Link to this type

# zone_abbr()

View Source

    @type zone_abbr() :: String.t()

The time zone abbreviation (for example, `CET` or `CEST` or `BST`).

# Callbacks

Link to this callback

# date_to_string(year, month, day)

View Source

    @callback date_to_string(year(), month(), day()) :: String.t()

Converts the date into a string according to the calendar.

Link to this callback

# datetime_to_string( year, month, day, hour, minute, second, microsecond, time_zone, zone_abbr, utc_offset, std_offset )

View Source

    @callback datetime_to_string(
      year(),
      month(),
      day(),
      hour(),
      minute(),
      second(),
      microsecond(),
      time_zone(),
      zone_abbr(),
      utc_offset(),
      std_offset()
    ) :: String.t()

Converts the datetime (with time zone) into a string according to the calendar.

Link to this callback

# day_of_era(year, month, day)

View Source

    @callback day_of_era(year(), month(), day()) :: day_of_era()

Calculates the day and era from the given `year`, `month`, and `day`.

Link to this callback

# day_of_week(year, month, day, starting_on)

View Source

    @callback day_of_week(year(), month(), day(), starting_on :: :default | atom()) ::
      {day_of_week(), first_day_of_week :: non_neg_integer(),
       last_day_of_week :: non_neg_integer()}

Calculates the day of the week from the given `year`, `month`, and `day`.

`starting_on` represents the starting day of the week. All calendars must support at least the `:default` value. They may also support other values representing their days of the week.

Link to this callback

# day_of_year(year, month, day)

View Source

    @callback day_of_year(year(), month(), day()) :: non_neg_integer()

Calculates the day of the year from the given `year`, `month`, and `day`.

Link to this callback

# day_rollover_relative_to_midnight_utc()

View Source

    @callback day_rollover_relative_to_midnight_utc() :: day_fraction()

Define the rollover moment for the calendar.

This is the moment, in your calendar, when the current day ends and the next day starts.

The result of this function is used to check if two calendars roll over at the same time of day. If they do not, we can only convert datetimes and times between them. If they do, this means that we can also convert dates as well as naive datetimes between them.

This day fraction should be in its most simplified form possible, to make comparisons fast.

## Examples

- If in your calendar a new day starts at midnight, return `{0, 1}`.
- If in your calendar a new day starts at sunrise, return `{1, 4}`.
- If in your calendar a new day starts at noon, return `{1, 2}`.
- If in your calendar a new day starts at sunset, return `{3, 4}`.

Link to this callback

# days_in_month(year, month)

View Source

    @callback days_in_month(year(), month()) :: day()

Returns how many days there are in the given month of the given year.

Link to this callback

# iso_days_to_beginning_of_day(iso_days)

View Source (since 1.15.0)

    @callback iso_days_to_beginning_of_day(iso_days()) :: iso_days()

Converts the given `iso_days/0` to the first moment of the day.

Link to this callback

# iso_days_to_end_of_day(iso_days)

View Source (since 1.15.0)

    @callback iso_days_to_end_of_day(iso_days()) :: iso_days()

Converts the given `iso_days/0` to the last moment of the day.

Link to this callback

# leap_year?(year)

View Source

    @callback leap_year?(year()) :: boolean()

Returns `true` if the given year is a leap year.

A leap year is a year of a longer length than normal. The exact meaning is up to the calendar. A calendar must return `false` if it does not support the concept of leap years.

Link to this callback

# months_in_year(year)

View Source

    @callback months_in_year(year()) :: month()

Returns how many months there are in the given year.

Link to this callback

# naive_datetime_from_iso_days(iso_days)

View Source

    @callback naive_datetime_from_iso_days(iso_days()) ::
      {year(), month(), day(), hour(), minute(), second(), microsecond()}

Converts `iso_days/0` to the calendar's datetime format.

Link to this callback

# naive_datetime_to_iso_days(year, month, day, hour, minute, second, microsecond)

View Source

    @callback naive_datetime_to_iso_days(
      year(),
      month(),
      day(),
      hour(),
      minute(),
      second(),
      microsecond()
    ) ::
      iso_days()

Converts the datetime (without time zone) into the `iso_days/0` format.

Link to this callback

# naive_datetime_to_string(year, month, day, hour, minute, second, microsecond)

View Source

    @callback naive_datetime_to_string(
      year(),
      month(),
      day(),
      hour(),
      minute(),
      second(),
      microsecond()
    ) ::
      String.t()

Converts the naive datetime (without time zone) into a string according to the calendar.

Link to this callback

# parse_date(t)

View Source (since 1.10.0)

    @callback parse_date(String.t()) :: {:ok, {year(), month(), day()}} | {:error, atom()}

Parses the string representation for a date returned by `date_to_string/3` into a date tuple.

Link to this callback

# parse_naive_datetime(t)

View Source (since 1.10.0)

    @callback parse_naive_datetime(String.t()) ::
      {:ok, {year(), month(), day(), hour(), minute(), second(), microsecond()}}
      | {:error, atom()}

Parses the string representation for a naive datetime returned by `naive_datetime_to_string/7` into a naive datetime tuple.

The given string may contain a timezone offset but it is ignored.

Link to this callback

# parse_time(t)

View Source (since 1.10.0)

    @callback parse_time(String.t()) ::
      {:ok, {hour(), minute(), second(), microsecond()}} | {:error, atom()}

Parses the string representation for a time returned by `time_to_string/4` into a time tuple.

Link to this callback

# parse_utc_datetime(t)

View Source (since 1.10.0)

    @callback parse_utc_datetime(String.t()) ::
      {:ok, {year(), month(), day(), hour(), minute(), second(), microsecond()},
       utc_offset()}
      | {:error, atom()}

Parses the string representation for a datetime returned by `datetime_to_string/11` into a datetime tuple.

The returned datetime must be in UTC. The original `utc_offset` it was written in must be returned in the result.

Link to this callback

# quarter_of_year(year, month, day)

View Source

    @callback quarter_of_year(year(), month(), day()) :: non_neg_integer()

Calculates the quarter of the year from the given `year`, `month`, and `day`.

Link to this callback

# time_from_day_fraction(day_fraction)

View Source

    @callback time_from_day_fraction(day_fraction()) ::
      {hour(), minute(), second(), microsecond()}

Converts `day_fraction/0` to the calendar's time format.

Link to this callback

# time_to_day_fraction(hour, minute, second, microsecond)

View Source

    @callback time_to_day_fraction(hour(), minute(), second(), microsecond()) ::
      day_fraction()

Converts the given time to the `day_fraction/0` format.

Link to this callback

# time_to_string(hour, minute, second, microsecond)

View Source

    @callback time_to_string(hour(), minute(), second(), microsecond()) :: String.t()

Converts the time into a string according to the calendar.

Link to this callback

# valid_date?(year, month, day)

View Source

    @callback valid_date?(year(), month(), day()) :: boolean()

Should return `true` if the given date describes a proper date in the calendar.

Link to this callback

# valid_time?(hour, minute, second, microsecond)

View Source

    @callback valid_time?(hour(), minute(), second(), microsecond()) :: boolean()

Should return `true` if the given time describes a proper time in the calendar.

Link to this callback

# year_of_era(year, month, day)

View Source

    @callback year_of_era(year(), month(), day()) :: {year(), era()}

Calculates the year and era from the given `year`.

# Functions

Link to this function

# compatible_calendars?(calendar, calendar)

View Source (since 1.5.0)

    @spec compatible_calendars?(calendar(), calendar()) :: boolean()

Returns `true` if two calendars have the same moment of starting a new day, `false` otherwise.

If two calendars are not compatible, we can only convert datetimes and times between them. If they are compatible, this means that we can also convert dates as well as naive datetimes between them.

Link to this function

# get_time_zone_database()

View Source (since 1.8.0)

    @spec get_time_zone_database() :: time_zone_database()

Gets the current time zone database.

Link to this function

# put_time_zone_database(database)

View Source (since 1.8.0)

    @spec put_time_zone_database(time_zone_database()) :: :ok

Sets the current time zone database.

Link to this function

# strftime(date_or_time_or_datetime, string_format, user_options \\ [])

View Source (since 1.11.0)

    @spec strftime(map(), String.t(), keyword()) :: String.t()

Formats the given date, time, or datetime into a string.

The datetime can be any of the `Calendar` types (`Time`, `Date`, `NaiveDateTime`, and `DateTime`) or any map, as long as they contain all of the relevant fields necessary for formatting. For example, if you use `%Y` to format the year, the datetime must have the `:year` field. Therefore, if you pass a `Time`, or a map without the `:year` field to a format that expects `%Y`, an error will be raised.

Examples of common usage:

    iex> Calendar.strftime(~U[2019-08-26 13:52:06.0Z], "%y-%m-%d %I:%M:%S %p")
    "19-08-26 01:52:06 PM"

    iex> Calendar.strftime(~U[2019-08-26 13:52:06.0Z], "%a, %B %d %Y")
    "Mon, August 26 2019"

## User Options

- `:preferred_datetime` - a string for the preferred format to show datetimes, it can't contain the `%c` format and defaults to `"%Y-%m-%d %H:%M:%S"` if the option is not received

- `:preferred_date` - a string for the preferred format to show dates, it can't contain the `%x` format and defaults to `"%Y-%m-%d"` if the option is not received

- `:preferred_time` - a string for the preferred format to show times, it can't contain the `%X` format and defaults to `"%H:%M:%S"` if the option is not received

- `:am_pm_names` - a function that receives either `:am` or `:pm` and returns the name of the period of the day, if the option is not received it defaults to a function that returns `"am"` and `"pm"`, respectively

- `:month_names` - a function that receives a number and returns the name of the corresponding month, if the option is not received it defaults to a function that returns the month names in English

- `:abbreviated_month_names` - a function that receives a number and returns the abbreviated name of the corresponding month, if the option is not received it defaults to a function that returns the abbreviated month names in English

- `:day_of_week_names` - a function that receives a number and returns the name of the corresponding day of week, if the option is not received it defaults to a function that returns the day of week names in English

- `:abbreviated_day_of_week_names` - a function that receives a number and returns the abbreviated name of the corresponding day of week, if the option is not received it defaults to a function that returns the abbreviated day of week names in English

## Formatting syntax

The formatting syntax for the `string_format` argument is a sequence of characters in the following format:

    %<padding><width><format>

where:

- `%`: indicates the start of a formatted section
- `<padding>`: set the padding (see below)
- `<width>`: a number indicating the minimum size of the formatted section
- `<format>`: the format itself (see below)

### Accepted padding options

- `-`: no padding, removes all padding from the format
- `_`: pad with spaces
- `0`: pad with zeroes

### Accepted string formats

The accepted formats for `string_format` are:

Format | Description | Examples (in ISO)
--- | --- | ---
a | Abbreviated name of day | Mon
A | Full name of day | Monday
b | Abbreviated month name | Jan
B | Full month name | January
c | Preferred date+time representation | 2018-10-17 12:34:56
d | Day of the month | 01, 31
f | Microseconds (does not support width and padding modifiers) | 000000, 999999, 0123
H | Hour using a 24-hour clock | 00, 23
I | Hour using a 12-hour clock | 01, 12
j | Day of the year | 001, 366
m | Month | 01, 12
M | Minute | 00, 59
p | "AM" or "PM" (noon is "PM", midnight as "AM") | AM, PM
P | "am" or "pm" (noon is "pm", midnight as "am") | am, pm
q | Quarter | 1, 2, 3, 4
s | Number of seconds since the Epoch, 1970-01-01 00:00:00+0000 (UTC) | 1565888877
S | Second | 00, 59, 60
u | Day of the week | 1 (Monday), 7 (Sunday)
x | Preferred date (without time) representation | 2018-10-17
X | Preferred time (without date) representation | 12:34:56
y | Year as 2-digits | 01, 01, 86, 18
Y | Year | -0001, 0001, 1986
z | +hhmm/-hhmm time zone offset from UTC (empty string if naive) | +0300, -0530
Z | Time zone abbreviation (empty string if naive) | CET, BRST
% | Literal "%" character | %

Any other character will be interpreted as an invalid format and raise an error.

## Examples

Without user options:

    iex> Calendar.strftime(~U[2019-08-26 13:52:06.0Z], "%y-%m-%d %I:%M:%S %p")
    "19-08-26 01:52:06 PM"

    iex> Calendar.strftime(~U[2019-08-26 13:52:06.0Z], "%a, %B %d %Y")
    "Mon, August 26 2019"

    iex> Calendar.strftime(~U[2020-04-02 13:52:06.0Z], "%B %-d, %Y")
    "April 2, 2020"

    iex> Calendar.strftime(~U[2019-08-26 13:52:06.0Z], "%c")
    "2019-08-26 13:52:06"

With user options:

    iex> Calendar.strftime(~U[2019-08-26 13:52:06.0Z], "%c", preferred_datetime: "%H:%M:%S %d-%m-%y")
    "13:52:06 26-08-19"

    iex> Calendar.strftime(
    ...>  ~U[2019-08-26 13:52:06.0Z],
    ...>  "%A",
    ...>  day_of_week_names: fn day_of_week ->
    ...>    {"segunda-feira", "terça-feira", "quarta-feira", "quinta-feira",
    ...>    "sexta-feira", "sábado", "domingo"}
    ...>    |> elem(day_of_week - 1)
    ...>  end
    ...>)
    "segunda-feira"

    iex> Calendar.strftime(
    ...>  ~U[2019-08-26 13:52:06.0Z],
    ...>  "%B",
    ...>  month_names: fn month ->
    ...>    {"січень", "лютий", "березень", "квітень", "травень", "червень",
    ...>    "липень", "серпень", "вересень", "жовтень", "листопад", "грудень"}
    ...>    |> elem(month - 1)
    ...>  end
    ...>)
    "серпень"

Link to this function

# truncate(microsecond_tuple, atom)

View Source (since 1.6.0)

    @spec truncate(microsecond(), :microsecond | :millisecond | :second) :: microsecond()

Returns a microsecond tuple truncated to a given precision (`:microsecond`, `:millisecond`, or `:second`).