#
View Source DateTime (Elixir v1.15.7)

A datetime implementation with a time zone.

This datetime can be seen as a snapshot of a date and time at a given time zone. For such purposes, it also includes both UTC and Standard offsets, as well as the zone abbreviation field used exclusively for formatting purposes. Note future datetimes are not necessarily guaranteed to exist, as time zones may change any time in the future due to geopolitical reasons. See the "Datetimes as snapshots" section for more information.

Remember, comparisons in Elixir using `==/2`, `>/2`, `</2` and friends are structural and based on the DateTime struct fields. For proper comparison between datetimes, use the `compare/2` function. The existence of the `compare/2` function in this module also allows using `Enum.min/2` and `Enum.max/2` functions to get the minimum and maximum datetime of an `Enum`. For example:

    iex> Enum.min([~U[2022-01-12 00:01:00.00Z], ~U[2021-01-12 00:01:00.00Z]], DateTime)
    ~U[2021-01-12 00:01:00.00Z]

Developers should avoid creating the `DateTime` struct directly and instead rely on the functions provided by this module as well as the ones in third-party calendar libraries.

## Time zone database

Many functions in this 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 can also be configured. Here are some available options and libraries:

- `time_zone_info`
- `tz`
- `tzdata`
- `zoneinfo` - recommended for embedded devices

To use them, first make sure it is added as a dependency in `mix.exs`. It can then be configured either via configuration:

    config :elixir, :time_zone_database, Tz.TimeZoneDatabase

or by calling `Calendar.put_time_zone_database/1`:

    Calendar.put_time_zone_database(Tz.TimeZoneDatabase)

See the proper names in the library installation instructions.

## Datetimes as snapshots

In the first section, we described datetimes as a "snapshot of a date and time at a given time zone". To understand precisely what we mean, let's see an example.

Imagine someone in Poland wants to schedule a meeting with someone in Brazil in the next year. The meeting will happen at 2:30 AM in the Polish time zone. At what time will the meeting happen in Brazil?

You can consult the time zone database today, one year before, using the API in this module and it will give you an answer that is valid right now. However, this answer may not be valid in the future. Why? Because both Brazil and Poland may change their timezone rules, ultimately affecting the result. For example, a country may choose to enter or abandon "Daylight Saving Time", which is a process where we adjust the clock one hour forward or one hour back once per year. Whenener the rules change, the exact instant that 2:30 AM in Polish time will be in Brazil may change.

In other words, whenever working with future DateTimes, there is no guarantee the results you get will always be correct, until the event actually happens. Therefore, when you ask for a future time, the answers you get are a snapshot that reflects the current state of the time zone rules. For datetimes in the past, this is not a problem, because time zone rules do not change for past events.

To make matters worse, it may be that the 2:30 AM in Polish time does not actually even exist or it is ambiguous. If a certain time zone observes "Daylight Saving Time", they will move their clock forward once a year. When this happens, there is a whole hour that does not exist. Then, when they move the clock back, there is a certain hour that will happen twice. So if you want to schedule a meeting when this shift back happens, you would need to explicitly say which of the 2:30 AM you precisely mean. Applications that are date and time sensitive, need to take these scenarios into account and correctly communicate them to users.

The good news is: Elixir contains all of the building blocks necessary to tackle those problems. The default timezone database used by Elixir, `Calendar.UTCOnlyTimeZoneDatabase`, only works with UTC, which does not observe those issues. Once you bring a proper time zone database, the functions in this module will query the database and return the relevant information. For example, look at how `DateTime.new/4` returns different results based on the scenarios described in this section.

# Summary

## Types

t()

## Functions

add(datetime, amount_to_add, unit \\ :second, time_zone_database \\ Calendar.get_time_zone_database())

Adds a specified amount of time to a `DateTime`.

after?(datetime1, datetime2)

Returns true if the first datetime is strictly later than the second.

before?(datetime1, datetime2)

Returns true if the first datetime is strictly earlier than the second.

compare(datetime1, datetime2)

Compares two datetime structs.

convert(datetime, calendar)

Converts a given `datetime` from one calendar to another.

convert!(datetime, calendar)

Converts a given `datetime` from one calendar to another.

diff(datetime1, datetime2, unit \\ :second)

Subtracts `datetime2` from `datetime1`.

from_gregorian_seconds(seconds, arg \\ {0, 0}, calendar \\ Calendar.ISO)

Converts a number of gregorian seconds to a `DateTime` struct.

from_iso8601(string, format_or_calendar \\ Calendar.ISO)

Parses the extended "Date and time of day" format described by ISO 8601:2019.

from_iso8601(string, calendar, format)

Converts to ISO8601 specifying both a calendar and a mode.

from_naive(naive_datetime, time_zone, time_zone_database \\ Calendar.get_time_zone_database())

Converts the given `NaiveDateTime` to `DateTime`.

from_naive!(naive_datetime, time_zone, time_zone_database \\ Calendar.get_time_zone_database())

Converts the given `NaiveDateTime` to `DateTime`.

from_unix(integer, unit \\ :second, calendar \\ Calendar.ISO)

Converts the given Unix time to `DateTime`.

from_unix!(integer, unit \\ :second, calendar \\ Calendar.ISO)

Converts the given Unix time to `DateTime`.

new(date, time, time_zone \\ "Etc/UTC", time_zone_database \\ Calendar.get_time_zone_database())

Builds a datetime from date and time structs.

new!(date, time, time_zone \\ "Etc/UTC", time_zone_database \\ Calendar.get_time_zone_database())

Builds a datetime from date and time structs, raising on errors.

now(time_zone, time_zone_database \\ Calendar.get_time_zone_database())

Returns the current datetime in the provided time zone.

now!(time_zone, time_zone_database \\ Calendar.get_time_zone_database())

Returns the current datetime in the provided time zone or raises on errors

shift_zone(datetime, time_zone, time_zone_database \\ Calendar.get_time_zone_database())

Changes the time zone of a `DateTime`.

shift_zone!(datetime, time_zone, time_zone_database \\ Calendar.get_time_zone_database())

Changes the time zone of a `DateTime` or raises on errors.

to_date(datetime)

Converts a `DateTime` into a `Date`.

to_gregorian_seconds(datetime)

Converts a `DateTime` struct to a number of gregorian seconds and microseconds.

to_iso8601(datetime, format \\ :extended, offset \\ nil)

Converts the given datetime to ISO 8601:2019 format.

to_naive(datetime)

Converts the given `datetime` into a `NaiveDateTime`.

to_string(datetime)

Converts the given `datetime` to a string according to its calendar.

to_time(datetime)

Converts a `DateTime` into `Time`.

to_unix(datetime, unit \\ :second)

Converts the given `datetime` to Unix time.

truncate(datetime, precision)

Returns the given datetime with the microsecond field truncated to the given precision (`:microsecond`, `:millisecond` or `:second`).

utc_now(calendar_or_time_unit \\ Calendar.ISO)

Returns the current datetime in UTC.

utc_now(time_unit, calendar)

Returns the current datetime in UTC, supporting a specific calendar and precision.

# Types

Link to this type

# t()

View Source

    @type t() :: %DateTime{
      calendar: Calendar.calendar(),
      day: Calendar.day(),
      hour: Calendar.hour(),
      microsecond: Calendar.microsecond(),
      minute: Calendar.minute(),
      month: Calendar.month(),
      second: Calendar.second(),
      std_offset: Calendar.std_offset(),
      time_zone: Calendar.time_zone(),
      utc_offset: Calendar.utc_offset(),
      year: Calendar.year(),
      zone_abbr: Calendar.zone_abbr()
    }

# Functions

Link to this function

# add(datetime, amount_to_add, unit \\ :second, time_zone_database \\ Calendar.get_time_zone_database())

View Source (since 1.8.0)

    @spec add(
      Calendar.datetime(),
      integer(),
      :day | :hour | :minute | System.time_unit(),
      Calendar.time_zone_database()
    ) :: t()

Adds a specified amount of time to a `DateTime`.

Accepts an `amount_to_add` in any `unit`. `unit` can be `:day`, `:hour`, `:minute`, `:second` or any subsecond precision from `System.time_unit/0`. It defaults to `:second`. Negative values will move backwards in time.

This function always consider the unit to be computed according to the `Calendar.ISO`.

This function uses relies on a contiguous representation of time, ignoring the wall time and timezone changes. For example, if you add one day when there are summer time/daylight saving time changes, it will also change the time forward or backward by one hour, so the elapsed time is precisely 24 hours. Similarly, adding just a few seconds to a datetime just before "spring forward" can cause wall time to increase by more than an hour.

While this means this function is precise in terms of elapsed time, its result may be misleading in certain use cases. For example, if a user requests a meeting to happen every day at 15:00 and you use this function to compute all future meetings by adding day after day, this function may change the meeting time to 14:00 or 16:00 if there are changes to the current timezone. Computing of recurring datetimes is not currently supported in Elixir's standard library but it is available by third-party libraries.

### Examples

    iex> dt = DateTime.from_naive!(~N[2018-11-15 10:00:00], "Europe/Copenhagen", FakeTimeZoneDatabase)
    iex> dt |> DateTime.add(3600, :second, FakeTimeZoneDatabase)
    #DateTime<2018-11-15 11:00:00+01:00 CET Europe/Copenhagen>

    iex> DateTime.add(~U[2018-11-15 10:00:00Z], 3600, :second)
    ~U[2018-11-15 11:00:00Z]

When adding 3 seconds just before "spring forward" we go from 1:59:59 to 3:00:02:

    iex> dt = DateTime.from_naive!(~N[2019-03-31 01:59:59.123], "Europe/Copenhagen", FakeTimeZoneDatabase)
    iex> dt |> DateTime.add(3, :second, FakeTimeZoneDatabase)
    #DateTime<2019-03-31 03:00:02.123+02:00 CEST Europe/Copenhagen>

When adding 1 day during "spring forward", the hour also changes:

    iex> dt = DateTime.from_naive!(~N[2019-03-31 01:00:00], "Europe/Copenhagen", FakeTimeZoneDatabase)
    iex> dt |> DateTime.add(1, :day, FakeTimeZoneDatabase)
    #DateTime<2019-04-01 02:00:00+02:00 CEST Europe/Copenhagen>

This operation merges the precision of the naive date time with the given unit:

    iex> result = DateTime.add(~U[2014-10-02 00:29:10Z], 21, :millisecond)
    ~U[2014-10-02 00:29:10.021Z]
    iex> result.microsecond
    {21000, 3}

Link to this function

# after?(datetime1, datetime2)

View Source (since 1.15.0)

    @spec after?(Calendar.datetime(), Calendar.datetime()) :: boolean()

Returns true if the first datetime is strictly later than the second.

## Examples

    iex> DateTime.after?(~U[2022-02-02 11:00:00Z], ~U[2021-01-01 11:00:00Z])
    true
    iex> DateTime.after?(~U[2021-01-01 11:00:00Z], ~U[2021-01-01 11:00:00Z])
    false
    iex> DateTime.after?(~U[2021-01-01 11:00:00Z], ~U[2022-02-02 11:00:00Z])
    false

Link to this function

# before?(datetime1, datetime2)

View Source (since 1.15.0)

    @spec before?(Calendar.datetime(), Calendar.datetime()) :: boolean()

Returns true if the first datetime is strictly earlier than the second.

## Examples

    iex> DateTime.before?(~U[2021-01-01 11:00:00Z], ~U[2022-02-02 11:00:00Z])
    true
    iex> DateTime.before?(~U[2021-01-01 11:00:00Z], ~U[2021-01-01 11:00:00Z])
    false
    iex> DateTime.before?(~U[2022-02-02 11:00:00Z], ~U[2021-01-01 11:00:00Z])
    false

Link to this function

# compare(datetime1, datetime2)

View Source (since 1.4.0)

    @spec compare(Calendar.datetime(), Calendar.datetime()) :: :lt | :eq | :gt

Compares two datetime structs.

Returns `:gt` if the first datetime is later than the second and `:lt` for vice versa. If the two datetimes are equal `:eq` is returned.

Note that both UTC and Standard offsets will be taken into account when comparison is done.

## Examples

    iex> dt1 = %DateTime{year: 2000, month: 2, day: 29, zone_abbr: "AMT",
    ...>                 hour: 23, minute: 0, second: 7, microsecond: {0, 0},
    ...>                 utc_offset: -14400, std_offset: 0, time_zone: "America/Manaus"}
    iex> dt2 = %DateTime{year: 2000, month: 2, day: 29, zone_abbr: "CET",
    ...>                 hour: 23, minute: 0, second: 7, microsecond: {0, 0},
    ...>                 utc_offset: 3600, std_offset: 0, time_zone: "Europe/Warsaw"}
    iex> DateTime.compare(dt1, dt2)
    :gt

Link to this function

# convert(datetime, calendar)

View Source (since 1.5.0)

    @spec convert(Calendar.datetime(), Calendar.calendar()) ::
      {:ok, t()} | {:error, :incompatible_calendars}

Converts a given `datetime` from one calendar to another.

If it is not possible to convert unambiguously between the calendars (see `Calendar.compatible_calendars?/2`), an `{:error, :incompatible_calendars}` tuple is returned.

## Examples

Imagine someone implements `Calendar.Holocene`, a calendar based on the Gregorian calendar that adds exactly 10,000 years to the current Gregorian year:

    iex> dt1 = %DateTime{year: 2000, month: 2, day: 29, zone_abbr: "AMT",
    ...>                 hour: 23, minute: 0, second: 7, microsecond: {0, 0},
    ...>                 utc_offset: -14400, std_offset: 0, time_zone: "America/Manaus"}
    iex> DateTime.convert(dt1, Calendar.Holocene)
    {:ok, %DateTime{calendar: Calendar.Holocene, day: 29, hour: 23,
                    microsecond: {0, 0}, minute: 0, month: 2, second: 7, std_offset: 0,
                    time_zone: "America/Manaus", utc_offset: -14400, year: 12000,
                    zone_abbr: "AMT"}}

Link to this function

# convert!(datetime, calendar)

View Source (since 1.5.0)

    @spec convert!(Calendar.datetime(), Calendar.calendar()) :: t()

Converts a given `datetime` from one calendar to another.

If it is not possible to convert unambiguously between the calendars (see `Calendar.compatible_calendars?/2`), an ArgumentError is raised.

## Examples

Imagine someone implements `Calendar.Holocene`, a calendar based on the Gregorian calendar that adds exactly 10,000 years to the current Gregorian year:

    iex> dt1 = %DateTime{year: 2000, month: 2, day: 29, zone_abbr: "AMT",
    ...>                 hour: 23, minute: 0, second: 7, microsecond: {0, 0},
    ...>                 utc_offset: -14400, std_offset: 0, time_zone: "America/Manaus"}
    iex> DateTime.convert!(dt1, Calendar.Holocene)
    %DateTime{calendar: Calendar.Holocene, day: 29, hour: 23,
              microsecond: {0, 0}, minute: 0, month: 2, second: 7, std_offset: 0,
              time_zone: "America/Manaus", utc_offset: -14400, year: 12000,
              zone_abbr: "AMT"}

Link to this function

# diff(datetime1, datetime2, unit \\ :second)

View Source (since 1.5.0)

    @spec diff(
      Calendar.datetime(),
      Calendar.datetime(),
      :day | :hour | :minute | System.time_unit()
    ) :: integer()

Subtracts `datetime2` from `datetime1`.

The answer can be returned in any `:day`, `:hour`, `:minute`, or any `unit` available from `System.time_unit/0`. The unit is measured according to `Calendar.ISO` and defaults to `:second`.

Fractional results are not supported and are truncated.

## Examples

    iex> dt1 = %DateTime{year: 2000, month: 2, day: 29, zone_abbr: "AMT",
    ...>                 hour: 23, minute: 0, second: 7, microsecond: {0, 0},
    ...>                 utc_offset: -14400, std_offset: 0, time_zone: "America/Manaus"}
    iex> dt2 = %DateTime{year: 2000, month: 2, day: 29, zone_abbr: "CET",
    ...>                 hour: 23, minute: 0, second: 7, microsecond: {0, 0},
    ...>                 utc_offset: 3600, std_offset: 0, time_zone: "Europe/Warsaw"}
    iex> DateTime.diff(dt1, dt2)
    18000
    iex> DateTime.diff(dt2, dt1)
    -18000
    iex> DateTime.diff(dt1, dt2, :hour)
    5
    iex> DateTime.diff(dt2, dt1, :hour)
    -5

Link to this function

# from_gregorian_seconds(seconds, arg \\ {0, 0}, calendar \\ Calendar.ISO)

View Source (since 1.11.0)

    @spec from_gregorian_seconds(integer(), Calendar.microsecond(), Calendar.calendar()) ::
      t()

Converts a number of gregorian seconds to a `DateTime` struct.

The returned `DateTime` will have `UTC` timezone, if you want other timezone, please use `DateTime.shift_zone/3`.

## Examples

    iex> DateTime.from_gregorian_seconds(1)
    ~U[0000-01-01 00:00:01Z]
    iex> DateTime.from_gregorian_seconds(63_755_511_991, {5000, 3})
    ~U[2020-05-01 00:26:31.005Z]
    iex> DateTime.from_gregorian_seconds(-1)
    ~U[-0001-12-31 23:59:59Z]

Link to this function

# from_iso8601(string, format_or_calendar \\ Calendar.ISO)

View Source (since 1.4.0)

    @spec from_iso8601(String.t(), Calendar.calendar() | :extended | :basic) ::
      {:ok, t(), Calendar.utc_offset()} | {:error, atom()}

Parses the extended "Date and time of day" format described by ISO 8601:2019.

Since ISO 8601 does not include the proper time zone, the given string will be converted to UTC and its offset in seconds will be returned as part of this function. Therefore offset information must be present in the string.

As specified in the standard, the separator "T" may be omitted if desired as there is no ambiguity within this function.

Note leap seconds are not supported by the built-in Calendar.ISO.

## Examples

    iex> {:ok, datetime, 0} = DateTime.from_iso8601("2015-01-23T23:50:07Z")
    iex> datetime
    ~U[2015-01-23 23:50:07Z]

    iex> {:ok, datetime, 9000} = DateTime.from_iso8601("2015-01-23T23:50:07.123+02:30")
    iex> datetime
    ~U[2015-01-23 21:20:07.123Z]

    iex> {:ok, datetime, 9000} = DateTime.from_iso8601("2015-01-23T23:50:07,123+02:30")
    iex> datetime
    ~U[2015-01-23 21:20:07.123Z]

    iex> {:ok, datetime, 0} = DateTime.from_iso8601("-2015-01-23T23:50:07Z")
    iex> datetime
    ~U[-2015-01-23 23:50:07Z]

    iex> {:ok, datetime, 9000} = DateTime.from_iso8601("-2015-01-23T23:50:07,123+02:30")
    iex> datetime
    ~U[-2015-01-23 21:20:07.123Z]

    iex> {:ok, datetime, 9000} = DateTime.from_iso8601("20150123T235007.123+0230", :basic)
    iex> datetime
    ~U[2015-01-23 21:20:07.123Z]

    iex> DateTime.from_iso8601("2015-01-23P23:50:07")
    {:error, :invalid_format}
    iex> DateTime.from_iso8601("2015-01-23T23:50:07")
    {:error, :missing_offset}
    iex> DateTime.from_iso8601("2015-01-23 23:50:61")
    {:error, :invalid_time}
    iex> DateTime.from_iso8601("2015-01-32 23:50:07")
    {:error, :invalid_date}
    iex> DateTime.from_iso8601("2015-01-23T23:50:07.123-00:00")
    {:error, :invalid_format}

Link to this function

# from_iso8601(string, calendar, format)

View Source

    @spec from_iso8601(String.t(), Calendar.calendar(), :extended | :basic) ::
      {:ok, t(), Calendar.utc_offset()} | {:error, atom()}

Converts to ISO8601 specifying both a calendar and a mode.

See `from_iso8601/2` for more information.

## Examples

    iex> {:ok, datetime, 9000} = DateTime.from_iso8601("2015-01-23T23:50:07,123+02:30", Calendar.ISO, :extended)
    iex> datetime
    ~U[2015-01-23 21:20:07.123Z]

    iex> {:ok, datetime, 9000} = DateTime.from_iso8601("20150123T235007.123+0230", Calendar.ISO, :basic)
    iex> datetime
    ~U[2015-01-23 21:20:07.123Z]

Link to this function

# from_naive(naive_datetime, time_zone, time_zone_database \\ Calendar.get_time_zone_database())

View Source (since 1.4.0)

    @spec from_naive(
      Calendar.naive_datetime(),
      Calendar.time_zone(),
      Calendar.time_zone_database()
    ) ::
      {:ok, t()}
      | {:ambiguous, first_datetime :: t(), second_datetime :: t()}
      | {:gap, t(), t()}
      | {:error,
         :incompatible_calendars
         | :time_zone_not_found
         | :utc_only_time_zone_database}

Converts the given `NaiveDateTime` to `DateTime`.

It expects a time zone to put the `NaiveDateTime` in. If the time zone is "Etc/UTC", it always succeeds. Otherwise, the NaiveDateTime is checked against the time zone database given as `time_zone_database`. See the "Time zone database" section in the module documentation.

## Examples

    iex> DateTime.from_naive(~N[2016-05-24 13:26:08.003], "Etc/UTC")
    {:ok, ~U[2016-05-24 13:26:08.003Z]}

When the datetime is ambiguous - for instance during changing from summer to winter time - the two possible valid datetimes are returned in a tuple. The first datetime is also the one which comes first chronologically, while the second one comes last.

    iex> {:ambiguous, first_dt, second_dt} = DateTime.from_naive(~N[2018-10-28 02:30:00], "Europe/Copenhagen", FakeTimeZoneDatabase)
    iex> first_dt
    #DateTime<2018-10-28 02:30:00+02:00 CEST Europe/Copenhagen>
    iex> second_dt
    #DateTime<2018-10-28 02:30:00+01:00 CET Europe/Copenhagen>

When there is a gap in wall time - for instance in spring when the clocks are turned forward - the latest valid datetime just before the gap and the first valid datetime just after the gap.

    iex> {:gap, just_before, just_after} = DateTime.from_naive(~N[2019-03-31 02:30:00], "Europe/Copenhagen", FakeTimeZoneDatabase)
    iex> just_before
    #DateTime<2019-03-31 01:59:59.999999+01:00 CET Europe/Copenhagen>
    iex> just_after
    #DateTime<2019-03-31 03:00:00+02:00 CEST Europe/Copenhagen>

Most of the time there is one, and just one, valid datetime for a certain date and time in a certain time zone.

    iex> {:ok, datetime} = DateTime.from_naive(~N[2018-07-28 12:30:00], "Europe/Copenhagen", FakeTimeZoneDatabase)
    iex> datetime
    #DateTime<2018-07-28 12:30:00+02:00 CEST Europe/Copenhagen>

This function accepts any map or struct that contains at least the same fields as a `NaiveDateTime` struct. The most common example of that is a `DateTime`. In this case the information about the time zone of that `DateTime` is completely ignored. This is the same principle as passing a `DateTime` to `Date.to_iso8601/2`. `Date.to_iso8601/2` extracts only the date-specific fields (calendar, year, month and day) of the given structure and ignores all others.

This way if you have a `DateTime` in one time zone, you can get the same wall time in another time zone. For instance if you have 2018-08-24 10:00:00 in Copenhagen and want a `DateTime` for 2018-08-24 10:00:00 in UTC you can do:

    iex> cph_datetime = DateTime.from_naive!(~N[2018-08-24 10:00:00], "Europe/Copenhagen", FakeTimeZoneDatabase)
    iex> {:ok, utc_datetime} = DateTime.from_naive(cph_datetime, "Etc/UTC", FakeTimeZoneDatabase)
    iex> utc_datetime
    ~U[2018-08-24 10:00:00Z]

If instead you want a `DateTime` for the same point time in a different time zone see the `DateTime.shift_zone/3` function which would convert 2018-08-24 10:00:00 in Copenhagen to 2018-08-24 08:00:00 in UTC.

Link to this function

# from_naive!(naive_datetime, time_zone, time_zone_database \\ Calendar.get_time_zone_database())

View Source (since 1.4.0)

    @spec from_naive!(
      NaiveDateTime.t(),
      Calendar.time_zone(),
      Calendar.time_zone_database()
    ) :: t()

Converts the given `NaiveDateTime` to `DateTime`.

It expects a time zone to put the NaiveDateTime in. If the time zone is "Etc/UTC", it always succeeds. Otherwise, the NaiveDateTime is checked against the time zone database given as `time_zone_database`. See the "Time zone database" section in the module documentation.

## Examples

    iex> DateTime.from_naive!(~N[2016-05-24 13:26:08.003], "Etc/UTC")
    ~U[2016-05-24 13:26:08.003Z]

    iex> DateTime.from_naive!(~N[2018-05-24 13:26:08.003], "Europe/Copenhagen", FakeTimeZoneDatabase)
    #DateTime<2018-05-24 13:26:08.003+02:00 CEST Europe/Copenhagen>

Link to this function

# from_unix(integer, unit \\ :second, calendar \\ Calendar.ISO)

View Source

    @spec from_unix(integer(), :native | System.time_unit(), Calendar.calendar()) ::
      {:ok, t()} | {:error, atom()}

Converts the given Unix time to `DateTime`.

The integer can be given in different unit according to `System.convert_time_unit/3` and it will be converted to microseconds internally. Up to 253402300799 seconds is supported.

Unix times are always in UTC and therefore the DateTime will be returned in UTC.

## Examples

    iex> {:ok, datetime} = DateTime.from_unix(1_464_096_368)
    iex> datetime
    ~U[2016-05-24 13:26:08Z]

    iex> {:ok, datetime} = DateTime.from_unix(1_432_560_368_868_569, :microsecond)
    iex> datetime
    ~U[2015-05-25 13:26:08.868569Z]

    iex> {:ok, datetime} = DateTime.from_unix(253_402_300_799)
    iex> datetime
    ~U[9999-12-31 23:59:59Z]

    iex> {:error, :invalid_unix_time} = DateTime.from_unix(253_402_300_800)

The unit can also be an integer as in `System.time_unit/0`:

    iex> {:ok, datetime} = DateTime.from_unix(143_256_036_886_856, 1024)
    iex> datetime
    ~U[6403-03-17 07:05:22.320312Z]

Negative Unix times are supported up to -377705116800 seconds:

    iex> {:ok, datetime} = DateTime.from_unix(-377_705_116_800)
    iex> datetime
    ~U[-9999-01-01 00:00:00Z]

    iex> {:error, :invalid_unix_time} = DateTime.from_unix(-377_705_116_801)

Link to this function

# from_unix!(integer, unit \\ :second, calendar \\ Calendar.ISO)

View Source

    @spec from_unix!(integer(), :native | System.time_unit(), Calendar.calendar()) :: t()

Converts the given Unix time to `DateTime`.

The integer can be given in different unit according to `System.convert_time_unit/3` and it will be converted to microseconds internally.

Unix times are always in UTC and therefore the DateTime will be returned in UTC.

## Examples

    # An easy way to get the Unix epoch is passing 0 to this function
    iex> DateTime.from_unix!(0)
    ~U[1970-01-01 00:00:00Z]

    iex> DateTime.from_unix!(1_464_096_368)
    ~U[2016-05-24 13:26:08Z]

    iex> DateTime.from_unix!(1_432_560_368_868_569, :microsecond)
    ~U[2015-05-25 13:26:08.868569Z]

    iex> DateTime.from_unix!(143_256_036_886_856, 1024)
    ~U[6403-03-17 07:05:22.320312Z]

Link to this function

# new(date, time, time_zone \\ "Etc/UTC", time_zone_database \\ Calendar.get_time_zone_database())

View Source (since 1.11.0)

    @spec new(Date.t(), Time.t(), Calendar.time_zone(), Calendar.time_zone_database()) ::
      {:ok, t()}
      | {:ambiguous, first_datetime :: t(), second_datetime :: t()}
      | {:gap, t(), t()}
      | {:error,
         :incompatible_calendars
         | :time_zone_not_found
         | :utc_only_time_zone_database}

Builds a datetime from date and time structs.

It expects a time zone to put the `DateTime` in. If the time zone is not passed it will default to `"Etc/UTC"`, which always succeeds. Otherwise, the `DateTime` is checked against the time zone database given as `time_zone_database`. See the "Time zone database" section in the module documentation.

## Examples

    iex> DateTime.new(~D[2016-05-24], ~T[13:26:08.003], "Etc/UTC")
    {:ok, ~U[2016-05-24 13:26:08.003Z]}

When the datetime is ambiguous - for instance during changing from summer to winter time - the two possible valid datetimes are returned in a tuple. The first datetime is also the one which comes first chronologically, while the second one comes last.

    iex> {:ambiguous, first_dt, second_dt} = DateTime.new(~D[2018-10-28], ~T[02:30:00], "Europe/Copenhagen", FakeTimeZoneDatabase)
    iex> first_dt
    #DateTime<2018-10-28 02:30:00+02:00 CEST Europe/Copenhagen>
    iex> second_dt
    #DateTime<2018-10-28 02:30:00+01:00 CET Europe/Copenhagen>

When there is a gap in wall time - for instance in spring when the clocks are turned forward - the latest valid datetime just before the gap and the first valid datetime just after the gap.

    iex> {:gap, just_before, just_after} = DateTime.new(~D[2019-03-31], ~T[02:30:00], "Europe/Copenhagen", FakeTimeZoneDatabase)
    iex> just_before
    #DateTime<2019-03-31 01:59:59.999999+01:00 CET Europe/Copenhagen>
    iex> just_after
    #DateTime<2019-03-31 03:00:00+02:00 CEST Europe/Copenhagen>

Most of the time there is one, and just one, valid datetime for a certain date and time in a certain time zone.

    iex> {:ok, datetime} = DateTime.new(~D[2018-07-28], ~T[12:30:00], "Europe/Copenhagen", FakeTimeZoneDatabase)
    iex> datetime
    #DateTime<2018-07-28 12:30:00+02:00 CEST Europe/Copenhagen>

Link to this function

# new!(date, time, time_zone \\ "Etc/UTC", time_zone_database \\ Calendar.get_time_zone_database())

View Source (since 1.11.0)

    @spec new!(Date.t(), Time.t(), Calendar.time_zone(), Calendar.time_zone_database()) ::
      t()

Builds a datetime from date and time structs, raising on errors.

It expects a time zone to put the `DateTime` in. If the time zone is not passed it will default to `"Etc/UTC"`, which always succeeds. Otherwise, the DateTime is checked against the time zone database given as `time_zone_database`. See the "Time zone database" section in the module documentation.

## Examples

    iex> DateTime.new!(~D[2016-05-24], ~T[13:26:08.003], "Etc/UTC")
    ~U[2016-05-24 13:26:08.003Z]

When the datetime is ambiguous - for instance during changing from summer to winter time - an error will be raised.

    iex> DateTime.new!(~D[2018-10-28], ~T[02:30:00], "Europe/Copenhagen", FakeTimeZoneDatabase)
    ** (ArgumentError) cannot build datetime with ~D[2018-10-28] and ~T[02:30:00] because such instant is ambiguous in time zone Europe/Copenhagen as there is an overlap between #DateTime<2018-10-28 02:30:00+02:00 CEST Europe/Copenhagen> and #DateTime<2018-10-28 02:30:00+01:00 CET Europe/Copenhagen>

When there is a gap in wall time - for instance in spring when the clocks are turned forward - an error will be raised.

    iex> DateTime.new!(~D[2019-03-31], ~T[02:30:00], "Europe/Copenhagen", FakeTimeZoneDatabase)
    ** (ArgumentError) cannot build datetime with ~D[2019-03-31] and ~T[02:30:00] because such instant does not exist in time zone Europe/Copenhagen as there is a gap between #DateTime<2019-03-31 01:59:59.999999+01:00 CET Europe/Copenhagen> and #DateTime<2019-03-31 03:00:00+02:00 CEST Europe/Copenhagen>

Most of the time there is one, and just one, valid datetime for a certain date and time in a certain time zone.

    iex> datetime = DateTime.new!(~D[2018-07-28], ~T[12:30:00], "Europe/Copenhagen", FakeTimeZoneDatabase)
    iex> datetime
    #DateTime<2018-07-28 12:30:00+02:00 CEST Europe/Copenhagen>

Link to this function

# now(time_zone, time_zone_database \\ Calendar.get_time_zone_database())

View Source (since 1.8.0)

    @spec now(Calendar.time_zone(), Calendar.time_zone_database()) ::
      {:ok, t()} | {:error, :time_zone_not_found | :utc_only_time_zone_database}

Returns the current datetime in the provided time zone.

By default, it uses the default time_zone returned by `Calendar.get_time_zone_database/0`, which defaults to `Calendar.UTCOnlyTimeZoneDatabase` which only handles "Etc/UTC" datetimes. Other time zone databases can be passed as argument or set globally. See the "Time zone database" section in the module docs.

## Examples

    iex> {:ok, datetime} = DateTime.now("Etc/UTC")
    iex> datetime.time_zone
    "Etc/UTC"

    iex> DateTime.now("Europe/Copenhagen")
    {:error, :utc_only_time_zone_database}

    iex> DateTime.now("bad timezone", FakeTimeZoneDatabase)
    {:error, :time_zone_not_found}

Link to this function

# now!(time_zone, time_zone_database \\ Calendar.get_time_zone_database())

View Source (since 1.10.0)

    @spec now!(Calendar.time_zone(), Calendar.time_zone_database()) :: t()

Returns the current datetime in the provided time zone or raises on errors

See `now/2` for more information.

## Examples

    iex> datetime = DateTime.now!("Etc/UTC")
    iex> datetime.time_zone
    "Etc/UTC"

    iex> DateTime.now!("Europe/Copenhagen")
    ** (ArgumentError) cannot get current datetime in "Europe/Copenhagen" time zone, reason: :utc_only_time_zone_database

    iex> DateTime.now!("bad timezone", FakeTimeZoneDatabase)
    ** (ArgumentError) cannot get current datetime in "bad timezone" time zone, reason: :time_zone_not_found

Link to this function

# shift_zone(datetime, time_zone, time_zone_database \\ Calendar.get_time_zone_database())

View Source (since 1.8.0)

    @spec shift_zone(t(), Calendar.time_zone(), Calendar.time_zone_database()) ::
      {:ok, t()} | {:error, :time_zone_not_found | :utc_only_time_zone_database}

Changes the time zone of a `DateTime`.

Returns a `DateTime` for the same point in time, but instead at the time zone provided. It assumes that `DateTime` is valid and exists in the given time zone and calendar.

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. Other time zone databases can be passed as argument or set globally. See the "Time zone database" section in the module docs.

## Examples

    iex> {:ok, pacific_datetime} = DateTime.shift_zone(~U[2018-07-16 10:00:00Z], "America/Los_Angeles", FakeTimeZoneDatabase)
    iex> pacific_datetime
    #DateTime<2018-07-16 03:00:00-07:00 PDT America/Los_Angeles>

    iex> DateTime.shift_zone(~U[2018-07-16 10:00:00Z], "bad timezone", FakeTimeZoneDatabase)
    {:error, :time_zone_not_found}

Link to this function

# shift_zone!(datetime, time_zone, time_zone_database \\ Calendar.get_time_zone_database())

View Source (since 1.10.0)

    @spec shift_zone!(t(), Calendar.time_zone(), Calendar.time_zone_database()) :: t()

Changes the time zone of a `DateTime` or raises on errors.

See `shift_zone/3` for more information.

## Examples

    iex> DateTime.shift_zone!(~U[2018-07-16 10:00:00Z], "America/Los_Angeles", FakeTimeZoneDatabase)
    #DateTime<2018-07-16 03:00:00-07:00 PDT America/Los_Angeles>

    iex> DateTime.shift_zone!(~U[2018-07-16 10:00:00Z], "bad timezone", FakeTimeZoneDatabase)
    ** (ArgumentError) cannot shift ~U[2018-07-16 10:00:00Z] to "bad timezone" time zone, reason: :time_zone_not_found

Link to this function

# to_date(datetime)

View Source

    @spec to_date(Calendar.datetime()) :: Date.t()

Converts a `DateTime` into a `Date`.

Because `Date` does not hold time nor time zone information, data will be lost during the conversion.

## Examples

    iex> dt = %DateTime{year: 2000, month: 2, day: 29, zone_abbr: "CET",
    ...>                hour: 23, minute: 0, second: 7, microsecond: {0, 0},
    ...>                utc_offset: 3600, std_offset: 0, time_zone: "Europe/Warsaw"}
    iex> DateTime.to_date(dt)
    ~D[2000-02-29]

Link to this function

# to_gregorian_seconds(datetime)

View Source (since 1.11.0)

    @spec to_gregorian_seconds(Calendar.datetime()) :: {integer(), non_neg_integer()}

Converts a `DateTime` struct to a number of gregorian seconds and microseconds.

## Examples

    iex> dt = %DateTime{year: 0000, month: 1, day: 1, zone_abbr: "UTC",
    ...>                hour: 0, minute: 0, second: 1, microsecond: {0, 0},
    ...>                utc_offset: 0, std_offset: 0, time_zone: "Etc/UTC"}
    iex> DateTime.to_gregorian_seconds(dt)
    {1, 0}

    iex> dt = %DateTime{year: 2020, month: 5, day: 1, zone_abbr: "UTC",
    ...>                hour: 0, minute: 26, second: 31, microsecond: {5000, 0},
    ...>                utc_offset: 0, std_offset: 0, time_zone: "Etc/UTC"}
    iex> DateTime.to_gregorian_seconds(dt)
    {63_755_511_991, 5000}

    iex> dt = %DateTime{year: 2020, month: 5, day: 1, zone_abbr: "CET",
    ...>                hour: 1, minute: 26, second: 31, microsecond: {5000, 0},
    ...>                utc_offset: 3600, std_offset: 0, time_zone: "Europe/Warsaw"}
    iex> DateTime.to_gregorian_seconds(dt)
    {63_755_511_991, 5000}

Link to this function

# to_iso8601(datetime, format \\ :extended, offset \\ nil)

View Source

    @spec to_iso8601(Calendar.datetime(), :basic | :extended, nil | integer()) ::
      String.t()

Converts the given datetime to ISO 8601:2019 format.

By default, `DateTime.to_iso8601/2` returns datetimes formatted in the "extended" format, for human readability. It also supports the "basic" format through passing the `:basic` option.

Only supports converting datetimes which are in the ISO calendar, attempting to convert datetimes from other calendars will raise. You can also optionally specify an offset for the formatted string.

WARNING: the ISO 8601 datetime format does not contain the time zone nor its abbreviation, which means information is lost when converting to such format.

### Examples

    iex> dt = %DateTime{year: 2000, month: 2, day: 29, zone_abbr: "CET",
    ...>                hour: 23, minute: 0, second: 7, microsecond: {0, 0},
    ...>                utc_offset: 3600, std_offset: 0, time_zone: "Europe/Warsaw"}
    iex> DateTime.to_iso8601(dt)
    "2000-02-29T23:00:07+01:00"

    iex> dt = %DateTime{year: 2000, month: 2, day: 29, zone_abbr: "UTC",
    ...>                hour: 23, minute: 0, second: 7, microsecond: {0, 0},
    ...>                utc_offset: 0, std_offset: 0, time_zone: "Etc/UTC"}
    iex> DateTime.to_iso8601(dt)
    "2000-02-29T23:00:07Z"

    iex> dt = %DateTime{year: 2000, month: 2, day: 29, zone_abbr: "AMT",
    ...>                hour: 23, minute: 0, second: 7, microsecond: {0, 0},
    ...>                utc_offset: -14400, std_offset: 0, time_zone: "America/Manaus"}
    iex> DateTime.to_iso8601(dt, :extended)
    "2000-02-29T23:00:07-04:00"

    iex> dt = %DateTime{year: 2000, month: 2, day: 29, zone_abbr: "AMT",
    ...>                hour: 23, minute: 0, second: 7, microsecond: {0, 0},
    ...>                utc_offset: -14400, std_offset: 0, time_zone: "America/Manaus"}
    iex> DateTime.to_iso8601(dt, :basic)
    "20000229T230007-0400"

    iex> dt = %DateTime{year: 2000, month: 2, day: 29, zone_abbr: "AMT",
    ...>                hour: 23, minute: 0, second: 7, microsecond: {0, 0},
    ...>                utc_offset: -14400, std_offset: 0, time_zone: "America/Manaus"}
    iex> DateTime.to_iso8601(dt, :extended, 3600)
    "2000-03-01T04:00:07+01:00"

    iex> dt = %DateTime{year: 2000, month: 2, day: 29, zone_abbr: "AMT",
    ...>                hour: 23, minute: 0, second: 7, microsecond: {0, 0},
    ...>                utc_offset: -14400, std_offset: 0, time_zone: "America/Manaus"}
    iex> DateTime.to_iso8601(dt, :extended, 0)
    "2000-03-01T03:00:07+00:00"

    iex> dt = %DateTime{year: 2000, month: 3, day: 01, zone_abbr: "UTC",
    ...>                hour: 03, minute: 0, second: 7, microsecond: {0, 0},
    ...>                utc_offset: 0, std_offset: 0, time_zone: "Etc/UTC"}
    iex> DateTime.to_iso8601(dt, :extended, 0)
    "2000-03-01T03:00:07Z"

    iex> {:ok, dt, offset} = DateTime.from_iso8601("2000-03-01T03:00:07Z")
    iex> "2000-03-01T03:00:07Z" = DateTime.to_iso8601(dt, :extended, offset)

Link to this function

# to_naive(datetime)

View Source

    @spec to_naive(Calendar.datetime()) :: NaiveDateTime.t()

Converts the given `datetime` into a `NaiveDateTime`.

Because `NaiveDateTime` does not hold time zone information, any time zone related data will be lost during the conversion.

## Examples

    iex> dt = %DateTime{year: 2000, month: 2, day: 29, zone_abbr: "CET",
    ...>                hour: 23, minute: 0, second: 7, microsecond: {0, 1},
    ...>                utc_offset: 3600, std_offset: 0, time_zone: "Europe/Warsaw"}
    iex> DateTime.to_naive(dt)
    ~N[2000-02-29 23:00:07.0]

Link to this function

# to_string(datetime)

View Source

    @spec to_string(Calendar.datetime()) :: String.t()

Converts the given `datetime` to a string according to its calendar.

### Examples

    iex> dt = %DateTime{year: 2000, month: 2, day: 29, zone_abbr: "CET",
    ...>                hour: 23, minute: 0, second: 7, microsecond: {0, 0},
    ...>                utc_offset: 3600, std_offset: 0, time_zone: "Europe/Warsaw"}
    iex> DateTime.to_string(dt)
    "2000-02-29 23:00:07+01:00 CET Europe/Warsaw"

    iex> dt = %DateTime{year: 2000, month: 2, day: 29, zone_abbr: "UTC",
    ...>                hour: 23, minute: 0, second: 7, microsecond: {0, 0},
    ...>                utc_offset: 0, std_offset: 0, time_zone: "Etc/UTC"}
    iex> DateTime.to_string(dt)
    "2000-02-29 23:00:07Z"

    iex> dt = %DateTime{year: 2000, month: 2, day: 29, zone_abbr: "AMT",
    ...>                hour: 23, minute: 0, second: 7, microsecond: {0, 0},
    ...>                utc_offset: -14400, std_offset: 0, time_zone: "America/Manaus"}
    iex> DateTime.to_string(dt)
    "2000-02-29 23:00:07-04:00 AMT America/Manaus"

    iex> dt = %DateTime{year: -100, month: 12, day: 19, zone_abbr: "CET",
    ...>                hour: 3, minute: 20, second: 31, microsecond: {0, 0},
    ...>                utc_offset: 3600, std_offset: 0, time_zone: "Europe/Stockholm"}
    iex> DateTime.to_string(dt)
    "-0100-12-19 03:20:31+01:00 CET Europe/Stockholm"

Link to this function

# to_time(datetime)

View Source

    @spec to_time(Calendar.datetime()) :: Time.t()

Converts a `DateTime` into `Time`.

Because `Time` does not hold date nor time zone information, data will be lost during the conversion.

## Examples

    iex> dt = %DateTime{year: 2000, month: 2, day: 29, zone_abbr: "CET",
    ...>                hour: 23, minute: 0, second: 7, microsecond: {0, 1},
    ...>                utc_offset: 3600, std_offset: 0, time_zone: "Europe/Warsaw"}
    iex> DateTime.to_time(dt)
    ~T[23:00:07.0]

Link to this function

# to_unix(datetime, unit \\ :second)

View Source

    @spec to_unix(Calendar.datetime(), System.time_unit()) :: integer()

Converts the given `datetime` to Unix time.

The `datetime` is expected to be using the ISO calendar with a year greater than or equal to 0.

It will return the integer with the given unit, according to `System.convert_time_unit/3`.

## Examples

    iex> 1_464_096_368 |> DateTime.from_unix!() |> DateTime.to_unix()
    1464096368

    iex> dt = %DateTime{calendar: Calendar.ISO, day: 20, hour: 18, microsecond: {273806, 6},
    ...>                minute: 58, month: 11, second: 19, time_zone: "America/Montevideo",
    ...>                utc_offset: -10800, std_offset: 3600, year: 2014, zone_abbr: "UYST"}
    iex> DateTime.to_unix(dt)
    1416517099

    iex> flamel = %DateTime{calendar: Calendar.ISO, day: 22, hour: 8, microsecond: {527771, 6},
    ...>                minute: 2, month: 3, second: 25, std_offset: 0, time_zone: "Etc/UTC",
    ...>                utc_offset: 0, year: 1418, zone_abbr: "UTC"}
    iex> DateTime.to_unix(flamel)
    -17412508655

Link to this function

# truncate(datetime, precision)

View Source (since 1.6.0)

    @spec truncate(Calendar.datetime(), :microsecond | :millisecond | :second) :: t()

Returns the given datetime with the microsecond field truncated to the given precision (`:microsecond`, `:millisecond` or `:second`).

The given datetime is returned unchanged if it already has lower precision than the given precision.

## Examples

    iex> dt1 = %DateTime{year: 2017, month: 11, day: 7, zone_abbr: "CET",
    ...>                 hour: 11, minute: 45, second: 18, microsecond: {123456, 6},
    ...>                 utc_offset: 3600, std_offset: 0, time_zone: "Europe/Paris"}
    iex> DateTime.truncate(dt1, :microsecond)
    #DateTime<2017-11-07 11:45:18.123456+01:00 CET Europe/Paris>

    iex> dt2 = %DateTime{year: 2017, month: 11, day: 7, zone_abbr: "CET",
    ...>                 hour: 11, minute: 45, second: 18, microsecond: {123456, 6},
    ...>                 utc_offset: 3600, std_offset: 0, time_zone: "Europe/Paris"}
    iex> DateTime.truncate(dt2, :millisecond)
    #DateTime<2017-11-07 11:45:18.123+01:00 CET Europe/Paris>

    iex> dt3 = %DateTime{year: 2017, month: 11, day: 7, zone_abbr: "CET",
    ...>                 hour: 11, minute: 45, second: 18, microsecond: {123456, 6},
    ...>                 utc_offset: 3600, std_offset: 0, time_zone: "Europe/Paris"}
    iex> DateTime.truncate(dt3, :second)
    #DateTime<2017-11-07 11:45:18+01:00 CET Europe/Paris>

Link to this function

# utc_now(calendar_or_time_unit \\ Calendar.ISO)

View Source

    @spec utc_now(Calendar.calendar() | :native | :microsecond | :millisecond | :second) ::
      t()

Returns the current datetime in UTC.

If you want the current time in Unix seconds, use `System.os_time/1` instead.

You can also pass a time unit to automatically truncate the resulting datetime. This is available since v1.15.0.

## Examples

    iex> datetime = DateTime.utc_now()
    iex> datetime.time_zone
    "Etc/UTC"

    iex> datetime = DateTime.utc_now(:second)
    iex> datetime.microsecond
    {0, 0}

Link to this function

# utc_now(time_unit, calendar)

View Source (since 1.15.0)

    @spec utc_now(:native | :microsecond | :millisecond | :second, Calendar.calendar()) ::
      t()

Returns the current datetime in UTC, supporting a specific calendar and precision.

If you want the current time in Unix seconds, use `System.os_time/1` instead.

## Examples

    iex> datetime = DateTime.utc_now(:microsecond, Calendar.ISO)
    iex> datetime.time_zone
    "Etc/UTC"

    iex> datetime = DateTime.utc_now(:second, Calendar.ISO)
    iex> datetime.microsecond
    {0, 0}