Calendar.DateTime

DateTime provides a struct which represents a certain time and date in a certain time zone.

The functions in this module can be used to create and transform DateTime structs.

Summary

advance!(date_time, seconds)

Like advance without exclamation points. Instead of returning a tuple with :ok and the result, the result is returned untagged. Will raise an error in case no correct result can be found based on the arguments

advance(date_time, seconds)

Takes a DateTime and an integer. Returns the date_time advanced by the number of seconds found in the seconds argument

diff(first_dt, second_dt)

The difference between two DateTime structs. In seconds and microseconds

from_erl!(date_time, time_zone, usec \\ nil)

Like from_erl/2 without "!", but returns the result directly without a tag. Will raise if date is ambiguous or invalid! Only use this if you are sure the date is valid. Otherwise use "from_erl" without the "!"

from_erl(date_time, timezone, usec \\ nil)

Takes an Erlang-style date-time tuple and additionally a timezone name. Returns a tuple with a tag and a DateTime struct

from_erl_total_off(erl_dt, timezone, total_off, usec \\ nil)

Like from_erl, but also takes an argument with the total UTC offset. (Total offset is standard offset + UTC offset)

from_micro_erl_total_off(arg1, timezone, total_off)

Like from_erl_total_off/4 but takes a 7 element datetime tuple with microseconds instead of a "normal" erlang style tuple

from_naive(ndt, timezone)

Takes an NaiveDateTime and a time zone identifier and returns a DateTime

gregorian_seconds(date_time)

Takes a DateTime and returns an integer of gregorian seconds starting with year 0. This is done via the Erlang calendar module

gregorian_seconds_and_usec(date_time)
now!(timezone)

Takes a timezone name a returns a DateTime with the current time in that timezone. Timezone names must be in the TZ data format

now(timezone)

Deprecated version of now!/1 with an exclamation point. Works the same way as now!/1

now_utc()

Like DateTime.now!("Etc/UTC")

shift_zone!(date_time, timezone)

Like shift_zone without "!", but does not check that the time zone is valid and just returns a DateTime struct instead of a tuple with a tag

shift_zone(date_time, timezone)

Takes a DateTime and the name of a new timezone. Returns a DateTime with the equivalent time in the new timezone

to_date(dt)

Takes a DateTime struct and returns a Date struct representing the date part of the provided DateTime

to_date_and_time(dt)

Returns a tuple with a Date struct and a Time struct

to_erl(datetime)

Takes a DateTime struct and returns an erlang style datetime tuple

to_micro_erl(datetime)

Takes a DateTime struct and returns an Ecto style datetime tuple. This is like an erlang style tuple, but with microseconds added as an additional element in the time part of the tuple

to_naive(dt)

Takes a DateTime and returns a NaiveDateTime

to_time(dt)

Takes a DateTime struct and returns a Time struct representing the time part of the provided DateTime

Functions

advance(date_time, seconds)

Takes a DateTime and an integer. Returns the date_time advanced by the number of seconds found in the seconds argument.

If seconds is negative, the time is moved back.

The advancement is done in UTC. The datetime is converted to UTC, then advanced, then converted back.

NOTE: this ignores leap seconds. The calculation is based on the (wrong) assumption that there are no leap seconds.

Examples

# Advance 2 seconds
iex> from_erl!({{2014,10,2},{0,29,10}}, "America/New_York",123456) |> advance(2)
{:ok, %Calendar.DateTime{abbr: "EDT", day: 2, hour: 0, min: 29, month: 10,
      sec: 12, std_off: 3600, timezone: "America/New_York", usec: 123456,
      utc_off: -18000, year: 2014}}

# Advance 86400 seconds (one day)
iex> from_erl!({{2014,10,2},{0,29,10}}, "America/New_York",123456) |> advance(86400)
{:ok, %Calendar.DateTime{abbr: "EDT", day: 3, hour: 0, min: 29, month: 10,
      sec: 10, std_off: 3600, timezone: "America/New_York", usec: 123456,
      utc_off: -18000, year: 2014}}

# Go back 62 seconds
iex> from_erl!({{2014,10,2},{0,0,0}}, "America/New_York",123456) |> advance(-62)
{:ok, %Calendar.DateTime{abbr: "EDT", day: 1, hour: 23, min: 58, month: 10,
      sec: 58, std_off: 3600, timezone: "America/New_York", usec: 123456, utc_off: -18000,
      year: 2014}}

# Advance 10 seconds just before DST "spring forward" so we go from 1:59:59 to 3:00:09
iex> from_erl!({{2015,3,8},{1,59,59}}, "America/New_York",123456) |> advance(10)
{:ok, %Calendar.DateTime{abbr: "EDT", day: 8, hour: 3, min: 0, month: 3,
      sec: 9, std_off: 3600, timezone: "America/New_York", usec: 123456,
      utc_off: -18000, year: 2015}}

# Go back too far so that year would be before 0
iex> from_erl!({{2014,10,2},{0,0,0}}, "America/New_York",123456) |> advance(-999999999999)
{:error, :function_clause_error}
advance!(date_time, seconds)

Like advance without exclamation points. Instead of returning a tuple with :ok and the result, the result is returned untagged. Will raise an error in case no correct result can be found based on the arguments.

diff(first_dt, second_dt)

The difference between two DateTime structs. In seconds and microseconds.

Leap seconds are ignored.

Returns tuple with {:ok, seconds, microseconds}

Examples

# March 30th 2014 02:00:00 in Central Europe the time changed from
# winter time to summer time. This means that clocks were set forward
# and an hour skipped. So between 01:00 and 4:00 there were 2 hours
# not 3. Two hours is 7200 seconds.
iex> diff(from_erl!({{2014,3,30},{4,0,0}}, "Europe/Stockholm"), from_erl!({{2014,3,30},{1,0,0}}, "Europe/Stockholm"))
{:ok, 7200, 0}

# The first DateTime is 40 seconds after the second DateTime
iex> diff(from_erl!({{2014,10,2},{0,29,50}}, "Etc/UTC"), from_erl!({{2014,10,2},{0,29,10}}, "Etc/UTC"))
{:ok, 40, 0}

# The first DateTime is 40 seconds before the second DateTime
iex> diff(from_erl!({{2014,10,2},{0,29,10}}, "Etc/UTC"), from_erl!({{2014,10,2},{0,29,50}}, "Etc/UTC"))
{:ok, -40, 0}

# The first DateTime is 30 microseconds after the second DateTime
iex> diff(from_erl!({{2014,10,2},{0,29,0}}, "Etc/UTC", 31), from_erl!({{2014,10,2},{0,29,0}}, "Etc/UTC", 1))
{:ok, 0, 30}

# The first DateTime is 2 microseconds after the second DateTime
iex> diff(from_erl!({{2014,10,2},{0,29,0}}, "Etc/UTC", 0), from_erl!({{2014,10,2},{0,29,0}}, "Etc/UTC", 2))
{:ok, 0, -2}

# The first DateTime is 9.999998 seconds after the second DateTime
iex> diff(from_erl!({{2014,10,2},{0,29,10}}, "Etc/UTC", 0), from_erl!({{2014,10,2},{0,29,0}}, "Etc/UTC", 2))
{:ok, 9, 999998}

# The first DateTime is 9.999998 seconds before the second DateTime
iex> diff(from_erl!({{2014,10,2},{0,29,0}}, "Etc/UTC", 2), from_erl!({{2014,10,2},{0,29,10}}, "Etc/UTC", 0))
{:ok, -9, 999998}

iex> diff(from_erl!({{2014,10,2},{0,29,0}}, "Etc/UTC", 0), from_erl!({{2014,10,2},{0,29,10}}, "Etc/UTC", 2))
{:ok, -10, 2}

iex> diff(from_erl!({{2014,10,2},{0,29,1}}, "Etc/UTC", 100), from_erl!({{2014,10,2},{0,29,0}}, "Etc/UTC", 200))
{:ok, 0, 999900}

iex> diff(from_erl!({{2014,10,2},{0,29,0}}, "Etc/UTC", 10), from_erl!({{2014,10,2},{0,29,0}}, "Etc/UTC", 999999))
{:ok, 0, -999989}
from_erl(date_time, timezone, usec \\ nil)

Takes an Erlang-style date-time tuple and additionally a timezone name. Returns a tuple with a tag and a DateTime struct.

The tag can be :ok, :ambiguous or :error. :ok is for an unambigous time. :ambiguous is for a time that could have different UTC offsets and/or standard offsets. Usually when switching from summer to winter time.

An erlang style date-time tuple has the following format: {{year, month, day}, {hour, minute, second}}

Examples

Normal, non-ambigous time

iex> from_erl({{2014, 9, 26}, {17, 10, 20}}, "America/Montevideo")
{:ok, %Calendar.DateTime{day: 26, hour: 17, min: 10, month: 9, sec: 20,
                        year: 2014, timezone: "America/Montevideo",
                        abbr: "UYT",
                        utc_off: -10800, std_off: 0, usec: nil} }

Switching from summer to wintertime in the fall means an ambigous time.

iex> from_erl({{2014, 3, 9}, {1, 1, 1}}, "America/Montevideo")
{:ambiguous, %Calendar.AmbiguousDateTime{possible_date_times:
  [%Calendar.DateTime{day: 9, hour: 1, min: 1, month: 3, sec: 1,
                     year: 2014, timezone: "America/Montevideo",
                     abbr: "UYST", utc_off: -10800, std_off: 3600},
   %Calendar.DateTime{day: 9, hour: 1, min: 1, month: 3, sec: 1,
                     year: 2014, timezone: "America/Montevideo",
                     abbr: "UYT", utc_off: -10800, std_off: 0},
  ]}
}

iex> from_erl({{2014, 9, 26}, {17, 10, 20}}, "Non-existing timezone")
{:error, :timezone_not_found}

The time between 2:00 and 3:00 in the following example does not exist because of the one hour gap caused by switching to DST.

iex> from_erl({{2014, 3, 30}, {2, 20, 02}}, "Europe/Copenhagen")
{:error, :invalid_datetime_for_timezone}

Time with fractional seconds. This represents the time 17:10:20.987654321

iex> from_erl({{2014, 9, 26}, {17, 10, 20}}, "America/Montevideo", 987654)
{:ok, %Calendar.DateTime{day: 26, hour: 17, min: 10, month: 9, sec: 20,
                        year: 2014, timezone: "America/Montevideo",
                        abbr: "UYT",
                        utc_off: -10800, std_off: 0, usec: 987654} }
from_erl!(date_time, time_zone, usec \\ nil)

Like from_erl/2 without "!", but returns the result directly without a tag. Will raise if date is ambiguous or invalid! Only use this if you are sure the date is valid. Otherwise use "from_erl" without the "!".

Example:

iex> from_erl!({{2014, 9, 26}, {17, 10, 20}}, "America/Montevideo")
%Calendar.DateTime{day: 26, hour: 17, min: 10, month: 9, sec: 20, year: 2014, timezone: "America/Montevideo", abbr: "UYT", utc_off: -10800, std_off: 0}
from_erl_total_off(erl_dt, timezone, total_off, usec \\ nil)

Like from_erl, but also takes an argument with the total UTC offset. (Total offset is standard offset + UTC offset)

The result will be the same as from_erl, except if the datetime is ambiguous. When the datetime is ambiguous (for instance during change from DST to non-DST) the total_offset argument is use to try to disambiguise the result. If successful the matching result is returned tagged with :ok. If the total_offset argument does not match either, an error will be returned.

Examples:

iex> from_erl_total_off({{2014, 9, 26}, {17, 10, 20}}, "America/Montevideo", -10800, 2)
{:ok, %Calendar.DateTime{day: 26, hour: 17, min: 10, month: 9, sec: 20,
                        year: 2014, timezone: "America/Montevideo",
                        abbr: "UYT",
                        utc_off: -10800, std_off: 0, usec: 2} }

iex> from_erl_total_off({{2014, 3, 9}, {1, 1, 1}}, "America/Montevideo", -7200, 2)
{:ok, %Calendar.DateTime{day: 9, hour: 1, min: 1, month: 3, sec: 1,
              year: 2014, timezone: "America/Montevideo", usec: 2,
                     abbr: "UYST", utc_off: -10800, std_off: 3600}
}
from_micro_erl_total_off(arg1, timezone, total_off)

Like from_erl_total_off/4 but takes a 7 element datetime tuple with microseconds instead of a "normal" erlang style tuple.

Examples:

iex> from_micro_erl_total_off({{2014, 3, 9}, {1, 1, 1, 2}}, "America/Montevideo", -7200)
{:ok, %Calendar.DateTime{day: 9, hour: 1, min: 1, month: 3, sec: 1,
              year: 2014, timezone: "America/Montevideo", usec: 2,
                     abbr: "UYST", utc_off: -10800, std_off: 3600}
}
from_naive(ndt, timezone)

Takes an NaiveDateTime and a time zone identifier and returns a DateTime

iex> Calendar.NaiveDateTime.from_erl!({{2014,10,15},{2,37,22}}) |> from_naive "UTC"
{:ok, %Calendar.DateTime{abbr: "UTC", day: 15, usec: nil, hour: 2, min: 37, month: 10, sec: 22, std_off: 0, timezone: "UTC", utc_off: 0, year: 2014}}
gregorian_seconds(date_time)

Takes a DateTime and returns an integer of gregorian seconds starting with year 0. This is done via the Erlang calendar module.

Examples

iex> from_erl!({{2014,9,26},{17,10,20}}, "UTC") |> gregorian_seconds
63578970620
gregorian_seconds_and_usec(date_time)
now(timezone)

Deprecated version of now!/1 with an exclamation point. Works the same way as now!/1.

In the future now/1 will return a tuple with {:ok, [DateTime]}

now!(timezone)

Takes a timezone name a returns a DateTime with the current time in that timezone. Timezone names must be in the TZ data format.

Examples

iex > DateTime.now! "UTC"
%Calendar.DateTime{abbr: "UTC", day: 15, hour: 2,
 min: 39, month: 10, sec: 53, std_off: 0, timezone: "UTC", utc_off: 0,
 year: 2014}

iex > DateTime.now! "Europe/Copenhagen"
%Calendar.DateTime{abbr: "CEST", day: 15, hour: 4,
 min: 41, month: 10, sec: 1, std_off: 3600, timezone: "Europe/Copenhagen",
 utc_off: 3600, year: 2014}
now_utc()

Like DateTime.now!("Etc/UTC")

shift_zone(date_time, timezone)

Takes a DateTime and the name of a new timezone. Returns a DateTime with the equivalent time in the new timezone.

Examples

iex> from_erl!({{2014,10,2},{0,29,10}}, "America/New_York",123456) |> shift_zone("Europe/Copenhagen")
{:ok, %Calendar.DateTime{abbr: "CEST", day: 2, hour: 6, min: 29, month: 10, sec: 10, timezone: "Europe/Copenhagen", utc_off: 3600, std_off: 3600, year: 2014, usec: 123456}}

iex> {:ok, nyc} = from_erl {{2014,10,2},{0,29,10}},"America/New_York"; shift_zone(nyc, "Invalid timezone")
{:invalid_time_zone, nil}
shift_zone!(date_time, timezone)

Like shift_zone without "!", but does not check that the time zone is valid and just returns a DateTime struct instead of a tuple with a tag.

Example

iex> from_erl!({{2014,10,2},{0,29,10}},"America/New_York") |> shift_zone! "Europe/Copenhagen"
%Calendar.DateTime{abbr: "CEST", day: 2, hour: 6, min: 29, month: 10, sec: 10,
                  timezone: "Europe/Copenhagen", utc_off: 3600, std_off: 3600, year: 2014}
to_date(dt)

Takes a DateTime struct and returns a Date struct representing the date part of the provided DateTime.

iex> from_erl!({{2014,10,15},{2,37,22}}, "UTC") |> Calendar.DateTime.to_date
%Calendar.Date{day: 15, month: 10, year: 2014}
to_date_and_time(dt)

Returns a tuple with a Date struct and a Time struct.

iex> from_erl!({{2014,10,15},{2,37,22}}, "UTC") |> Calendar.DateTime.to_date_and_time
{%Calendar.Date{day: 15, month: 10, year: 2014}, %Calendar.Time{usec: nil, hour: 2, min: 37, sec: 22}}
to_erl(datetime)

Takes a DateTime struct and returns an erlang style datetime tuple.

Examples

iex> from_erl!({{2014,10,15},{2,37,22}}, "Etc/UTC") |> Calendar.DateTime.to_erl
{{2014, 10, 15}, {2, 37, 22}}
to_micro_erl(datetime)

Takes a DateTime struct and returns an Ecto style datetime tuple. This is like an erlang style tuple, but with microseconds added as an additional element in the time part of the tuple.

If the datetime has its usec field set to nil, 0 will be used for usec.

Examples

iex> from_erl!({{2014,10,15},{2,37,22}}, "Etc/UTC", 999999) |> Calendar.DateTime.to_micro_erl
{{2014, 10, 15}, {2, 37, 22, 999999}}

iex> from_erl!({{2014,10,15},{2,37,22}}, "Etc/UTC", nil) |> Calendar.DateTime.to_micro_erl
{{2014, 10, 15}, {2, 37, 22, 0}}
to_naive(dt)

Takes a DateTime and returns a NaiveDateTime

iex> Calendar.DateTime.from_erl!({{2014,10,15},{2,37,22}}, "UTC", 0.55) |> to_naive
%Calendar.NaiveDateTime{day: 15, usec: 0.55, hour: 2, min: 37, month: 10, sec: 22, year: 2014}
to_time(dt)

Takes a DateTime struct and returns a Time struct representing the time part of the provided DateTime.

iex> from_erl!({{2014,10,15},{2,37,22}}, "UTC") |> Calendar.DateTime.to_time
%Calendar.Time{usec: nil, hour: 2, min: 37, sec: 22}