View Source Calendar behaviour (Elixir v1.14.0-rc.0)
This module defines the responsibilities for working with calendars, dates, times and datetimes in Elixir.
Currently 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 instead of full-featured datetime API.
For the actual date, time and datetime structures, see Date
,
Time
, NaiveDateTime
and DateTime
.
Note designations for year, month, day, and the like, are overspecified
(i.e. an integer instead of 1..12
for months) because different
calendars may have a different number of days per month, months per year and so on.
Link to this section Summary
Types
A calendar implementation
Any map/struct that contains the date fields
Any map/struct that contains the datetime fields
The internal time format is used when converting between calendars.
A tuple representing the day
and the era
.
The internal date format that is used when converting between calendars.
Microseconds with stored precision.
Any map/struct that contains the naive_datetime fields
The time zone standard offset in seconds (typically not zero in summer times).
Any map/struct that contains the time fields
The time zone ID according to the IANA tz database (for example, Europe/Zurich)
Specifies the time zone database for calendar operations.
The time zone UTC offset in seconds for standard time.
The time zone abbreviation (for example, CET or CEST or BST, and such)
Callbacks
Converts the date into a string according to the calendar.
Converts the datetime (with time zone) into a string according to the calendar.
Calculates the day and era from the given year
, month
, and day
.
Calculates the day of the week from the given year
, month
, and day
.
Calculates the day of the year from the given year
, month
, and day
.
Define the rollover moment for the given calendar.
Returns how many days there are in the given year-month.
Returns true
if the given year is a leap year.
Returns how many months there are in the given year.
Converts iso_days/0
to the Calendar's datetime format.
Converts the given datetime (without time zone) into the iso_days/0
format.
Converts the datetime (without time zone) into a string according to the calendar.
Parses the string representation for a date returned by date_to_string/3
into a date-tuple.
Parses the string representation for a naive datetime returned by
naive_datetime_to_string/7
into a naive-datetime-tuple.
Parses the string representation for a time returned by time_to_string/4
into a time-tuple.
Parses the string representation for a datetime returned by
datetime_to_string/11
into a datetime-tuple.
Calculates the quarter of the year from the given year
, month
, and day
.
Converts day_fraction/0
to the Calendar's time format.
Converts the given time to the day_fraction/0
format.
Converts the time into a string according to the calendar.
Should return true
if the given date describes a proper date in the calendar.
Should return true
if the given time describes a proper time in the calendar.
Calculates the year and era from the given year
.
Functions
Returns true
if two calendars have the same moment of starting a new day,
false
otherwise.
Gets the current time zone database.
Sets the current time zone database.
Formats received datetime into a string.
Returns a microsecond tuple truncated to a given precision (:microsecond
,
:millisecond
or :second
).
Link to this section Types
@type calendar() :: module()
A calendar implementation
@type date() :: %{ optional(any()) => any(), calendar: calendar(), year: year(), month: month(), day: day() }
Any map/struct that contains the date fields
@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/struct that contains the datetime fields
@type day() :: pos_integer()
@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 there fit in a day.
@type day_of_era() :: {day :: non_neg_integer(), era()}
A tuple representing the day
and the era
.
@type day_of_week() :: non_neg_integer()
@type era() :: non_neg_integer()
@type hour() :: non_neg_integer()
@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).
@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.
@type minute() :: non_neg_integer()
@type month() :: pos_integer()
@type naive_datetime() :: %{ optional(any()) => any(), calendar: calendar(), year: year(), month: month(), day: day(), hour: hour(), minute: minute(), second: second(), microsecond: microsecond() }
Any map/struct that contains the naive_datetime fields
@type second() :: non_neg_integer()
@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".
@type time() :: %{ optional(any()) => any(), hour: hour(), minute: minute(), second: second(), microsecond: microsecond() }
Any map/struct that contains the time fields
@type time_zone() :: String.t()
The time zone ID according to the IANA tz database (for example, Europe/Zurich)
@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, it uses the default time zone database returned by
Calendar.get_time_zone_database/0
, which defaults to
Calendar.UTCOnlyTimeZoneDatabase
which 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.
@type utc_offset() :: integer()
The time zone UTC offset in seconds for standard time.
See also std_offset/0
.
@type week() :: pos_integer()
@type year() :: integer()
@type zone_abbr() :: String.t()
The time zone abbreviation (for example, CET or CEST or BST, and such)
Link to this section Callbacks
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 )
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.
@callback day_of_era(year(), month(), day()) :: day_of_era()
Calculates the day and era from the given year
, month
, and day
.
@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
.
The 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.
@callback day_of_year(year(), month(), day()) :: non_neg_integer()
Calculates the day of the year from the given year
, month
, and day
.
@callback day_rollover_relative_to_midnight_utc() :: day_fraction()
Define the rollover moment for the given 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 rollover 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}.
Returns how many days there are in the given year-month.
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.
Returns how many months there are in the given year.
@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.
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 given datetime (without time zone) into the iso_days/0
format.
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 datetime (without time zone) into a string according to the calendar.
Parses the string representation for a date returned by date_to_string/3
into a date-tuple.
@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.
@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.
@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.
@callback quarter_of_year(year(), month(), day()) :: non_neg_integer()
Calculates the quarter of the year from the given year
, month
, and day
.
@callback time_from_day_fraction(day_fraction()) :: {hour(), minute(), second(), microsecond()}
Converts day_fraction/0
to the Calendar's time format.
@callback time_to_day_fraction(hour(), minute(), second(), microsecond()) :: day_fraction()
Converts the given time to the day_fraction/0
format.
@callback time_to_string(hour(), minute(), second(), microsecond()) :: String.t()
Converts the time into a string according to the calendar.
Should return true
if the given date describes a proper date in the calendar.
@callback valid_time?(hour(), minute(), second(), microsecond()) :: boolean()
Should return true
if the given time describes a proper time in the calendar.
Calculates the year and era from the given year
.
Link to this section Functions
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.
@spec get_time_zone_database() :: time_zone_database()
Gets the current time zone database.
@spec put_time_zone_database(time_zone_database()) :: :ok
Sets the current time zone database.
strftime(date_or_time_or_datetime, string_format, user_options \\ [])
View Source (since 1.11.0)Formats received 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.
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 strftime 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 spaces0
: pad with zeroes
Accepted formats
The accepted formats 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 | 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 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 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
...>)
"август"
@spec truncate(microsecond(), :microsecond | :millisecond | :second) :: microsecond()
Returns a microsecond tuple truncated to a given precision (:microsecond
,
:millisecond
or :second
).