Источник Duration
Структура и функции для работы со временными интервалами.
Структура Duration представляет собой набор единиц измерения времени, позволяющий манипулировать и вычислять временные интервалы.
Единицы измерения даты и времени представлены как целые числа, что позволяет использовать как положительные, так и отрицательные значения.
Микросекунды представлены с помощью кортежа {microsecond, precision}. Это обеспечивает совместимость с другими типами календарей, реализующими время, такими как Time, DateTime и NaiveDateTime.
Сдвиги
Наиболее распространённое использование временных интервалов в стандартной библиотеке Elixir — это «сдвиг» типов календаря.
iex> Date.shift(~D[2016-01-03], month: 2) ~D[2016-03-03]
В примере выше, Date.shift/2 автоматически преобразует единицы в структуру Duration, хотя её можно также задать напрямую:
iex> Date.shift(~D[2016-01-03], Duration.new!(month: 2)) ~D[2016-03-03]
Важно отметить, что сдвиг — это не арифметическая операция. Например, добавление date + 1 month + 1 month не даёт того же результата, что и date + 2 months. Посмотрим на пример:
iex> ~D[2016-01-31] |> Date.shift(month: 1) |> Date.shift(month: 1) ~D[2016-03-29] iex> ~D[2016-01-31] |> Date.shift(month: 2) ~D[2016-03-31]
Как видно выше, результаты различаются, что объясняет, почему операции с временными интервалами называются «сдвигом», а не «сложением». Это происходит потому, что, добавив один месяц к 2016-01-31, мы получим 2016-02-29. Затем добавление ещё одного месяца даёт нам 2016-03-29 вместо 2016-03-31.
В частности, при применении временных интервалов к типам Calendar.ISO:
большие единицы (такие как годы и месяцы) применяются перед меньшими (такими как недели, часы, дни и т. д.)
единицы сворачиваются в месяцы (
:yearи:month), секунды (:week,:day,:hour,:minute,:second) и микросекунды (:microsecond) перед применением1 год эквивалентен 12 месяцам, 1 неделя — 7 дням. Следовательно, 4 недели не эквивалентны 1 месяцу
в случае отсутствия дат результаты округляются до ближайшей корректной даты
Поскольку функции shift/2 учитывают календарь, они гарантированно возвращают допустимые даты/времена, учитывая високосные годы и летнее/зимнее время в соответствующих часовых поясах.
Интервалы
Временные интервалы в Elixir можно комбинировать с операциями над потоками для создания интервалов. Например, чтобы получить следующие три среды, начиная с 17 апреля 2024 года:
iex> ~D[2024-04-17] |> Stream.iterate(&Date.shift(&1, week: 1)) |> Enum.take(3) [~D[2024-04-17], ~D[2024-04-24], ~D[2024-05-01]]
Однако, ещё раз, важно помнить, что сдвиг временного интервала — это не арифметическая операция, поэтому вам может потребоваться использовать функции в этом модуле в зависимости от того, чего вы хотите добиться. Сравните результаты обоих примеров ниже:
# Adding one month after the other iex> date = ~D[2016-01-31] iex> duration = Duration.new!(month: 1) iex> stream = Stream.iterate(date, fn prev_date -> Date.shift(prev_date, duration) end) iex> Enum.take(stream, 3) [~D[2016-01-31], ~D[2016-02-29], ~D[2016-03-29]] # Multiplying durations by an index iex> date = ~D[2016-01-31] iex> duration = Duration.new!(month: 1) iex> stream = Stream.from_index(fn i -> Date.shift(date, Duration.multiply(duration, i)) end) iex> Enum.take(stream, 3) [~D[2016-01-31], ~D[2016-02-29], ~D[2016-03-31]]
Во втором примере постоянно указывается последний день месяца, так как он выполняет операции над временным интервалом, а не сдвигает дату после даты.
Обзор
Типы
- duration()
Тип duration определяет структуру
%Duration{}или список ключевых слов с допустимыми парами единиц измерения временного интервала.- t()
Тип структуры duration.
- unit_pair()
Тип unit_pair определяет пару допустимого ключа единицы измерения временного интервала и значения.
Функции
- add(d1, d2)
Складывает единицы заданных временных интервалов
d1иd2.- from_iso8601(string)
Парсит строку с форматом ISO 8601 для временного интервала в структуру
Duration.- from_iso8601!(string)
Аналогично
from_iso8601/1, но вызывает исключениеArgumentError.- multiply(duration, integer)
Умножает единицы
durationна заданное целое числоinteger.- negate(duration)
Изменяет знак единиц
durationна противоположный.- new!(duration)
Создаёт новую структуру
Durationиз заданногоunit_pairs.- subtract(d1, d2)
Вычитает единицы заданных временных интервалов
d1иd2.- to_iso8601(duration)
Преобразует заданный временной интервал
durationв строку с форматом ISO 8601-2:2019.
Типы
duration()Источник
@type duration() :: t() | [unit_pair()]
Тип duration определяет структуру %Duration{} или список ключевых слов с допустимыми парами единиц измерения временного интервала.
t()Источник
@type t() :: %Duration{
day: integer(),
hour: integer(),
microsecond: {integer(), 0..6},
minute: integer(),
month: integer(),
second: integer(),
week: integer(),
year: integer()
} Тип структуры duration.
unit_pair()Источник
@type unit_pair() ::
{:year, integer()}
| {:month, integer()}
| {:week, integer()}
| {:day, integer()}
| {:hour, integer()}
| {:minute, integer()}
| {:second, integer()}
| {:microsecond, {integer(), 0..6}} Тип unit_pair определяет пару допустимого ключа единицы измерения временного интервала и значения.
Функции
add(d1, d2)Source
@spec add(t(), t()) :: t()
Складывает единицы заданных промежутков времени d1 и d2.
Учитывает максимальную точность в микросекундах из двух.
Примеры
iex> Duration.add(Duration.new!(week: 2, day: 1), Duration.new!(day: 2))
%Duration{week: 2, day: 3}
iex> Duration.add(Duration.new!(microsecond: {400, 3}), Duration.new!(microsecond: {600, 6}))
%Duration{microsecond: {1000, 6}} from_iso8601(string)Source
@spec from_iso8601(String.t()) :: {:ok, t()} | {:error, atom()} Парсит строку интервала времени в формате ISO 8601 в структуру Duration.
Строки интервалов времени, а также отдельные единицы, могут быть опережаемы знаком плюс/минус, чтобы:
-
-PT6H3Mпарсится как%Duration{hour: -6, minute: -3} -
-PT6H-3Mпарсится как%Duration{hour: -6, minute: 3} -
+PT6H3Mпарсится как%Duration{hour: 6, minute: 3} -
+PT6H-3Mпарсится как%Duration{hour: 6, minute: -3}
Обозначения интервалов времени должны быть предоставлены в порядке величины: P[n]Y[n]M[n]W[n]DT[n]H[n]M[n]S.
Только секунды могут быть указаны с десятичной дробью, используя запятую или точку: P1DT4,5S.
Примеры
iex> Duration.from_iso8601("P1Y2M3DT4H5M6S")
{:ok, %Duration{year: 1, month: 2, day: 3, hour: 4, minute: 5, second: 6}}
iex> Duration.from_iso8601("P3Y-2MT3H")
{:ok, %Duration{year: 3, month: -2, hour: 3}}
iex> Duration.from_iso8601("-PT10H-30M")
{:ok, %Duration{hour: -10, minute: 30}}
iex> Duration.from_iso8601("PT4.650S")
{:ok, %Duration{second: 4, microsecond: {650000, 3}}} from_iso8601!(string)Source
@spec from_iso8601!(String.t()) :: t()
То же, что и from_iso8601/1, но вызывает исключение ArgumentError.
Примеры
iex> Duration.from_iso8601!("P1Y2M3DT4H5M6S")
%Duration{year: 1, month: 2, day: 3, hour: 4, minute: 5, second: 6}
iex> Duration.from_iso8601!("P10D")
%Duration{day: 10} multiply(duration, integer)Source
@spec multiply(t(), integer()) :: t()
Умножает duration единицы на заданное integer.
Примеры
iex> Duration.multiply(Duration.new!(day: 1, minute: 15, second: -10), 3)
%Duration{day: 3, minute: 45, second: -30}
iex> Duration.multiply(Duration.new!(microsecond: {200, 4}), 3)
%Duration{microsecond: {600, 4}} negate(duration)Source
@spec negate(t()) :: t()
Изменяет знак duration единиц.
Примеры
iex> Duration.negate(Duration.new!(day: 1, minute: 15, second: -10))
%Duration{day: -1, minute: -15, second: 10}
iex> Duration.negate(Duration.new!(microsecond: {500000, 4}))
%Duration{microsecond: {-500000, 4}} new!(duration)Source
@spec new!(duration()) :: t()
Создает новую структуру Duration из заданного unit_pairs.
Вызывает исключение ArgumentError при использовании недопустимых пар единиц.
Примеры
iex> Duration.new!(year: 1, week: 3, hour: 4, second: 1)
%Duration{year: 1, week: 3, hour: 4, second: 1}
iex> Duration.new!(second: 1, microsecond: {1000, 6})
%Duration{second: 1, microsecond: {1000, 6}}
iex> Duration.new!(month: 2)
%Duration{month: 2} subtract(d1, d2)Source
@spec subtract(t(), t()) :: t()
Вычитает единицы заданных промежутков времени d1 и d2.
Учитывает максимальную точность в микросекундах из двух.
Примеры
iex> Duration.subtract(Duration.new!(week: 2, day: 1), Duration.new!(day: 2))
%Duration{week: 2, day: -1}
iex> Duration.subtract(Duration.new!(microsecond: {400, 6}), Duration.new!(microsecond: {600, 3}))
%Duration{microsecond: {-200, 6}} to_iso8601(duration)Source
@spec to_iso8601(t()) :: String.t()
Преобразует заданный duration в строку формата ISO 8601-2:2019.
Обратите внимание, что эта функция реализует расширение ISO 8601:2019. Это расширение позволяет неделям появляться между месяцами и днями: P3M3W3D, делая его полностью совместимым с любой структурой Duration.
Примеры
iex> Duration.to_iso8601(Duration.new!(year: 3))
"P3Y"
iex> Duration.to_iso8601(Duration.new!(day: 40, hour: 12, minute: 42, second: 12))
"P40DT12H42M12S"
iex> Duration.to_iso8601(Duration.new!(second: 30))
"PT30S"
iex> Duration.to_iso8601(Duration.new!([]))
"PT0S"
iex> Duration.to_iso8601(Duration.new!(second: 1, microsecond: {2_200, 3}))
"PT1.002S"
iex> Duration.to_iso8601(Duration.new!(second: 1, microsecond: {-1_200_000, 4}))
"PT-0.2000S"
© 2012-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.17.2/Duration.html