File size: 2,274 Bytes
8da2481 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | defmodule Plausible.Stats.DateTimeRange do
@moduledoc """
Defines a struct similar `Date.Range`, but with `DateTime` instead of `Date`.
The structs should be created with the `new!/2` function.
"""
@enforce_keys [:first, :last]
defstruct [:first, :last]
@type t() :: %__MODULE__{
first: %DateTime{},
last: %DateTime{}
}
@doc """
Creates a `DateTimeRange` struct from the given `%Date{}` structs.
The first datetime will become the first date at 00:00:00, and the last datetime
will become the last date at 23:59:59. Both dates will be turned into `%DateTime{}`
structs in the given timezone.
"""
def new!(%Date{} = first, last, timezone) do
first =
case DateTime.new(first, ~T[00:00:00], timezone) do
{:ok, datetime} -> datetime
{:gap, _just_before, just_after} -> just_after
{:ambiguous, _first_datetime, second_datetime} -> second_datetime
end
new!(first, last, timezone)
end
def new!(first, %Date{} = last, timezone) do
last =
case DateTime.new(last, ~T[23:59:59], timezone) do
{:ok, datetime} -> datetime
{:gap, just_before, _just_after} -> just_before
{:ambiguous, first_datetime, _second_datetime} -> first_datetime
end
new!(first, last, timezone)
end
def new!(%DateTime{} = first, %DateTime{} = last, _timezone) do
new!(first, last)
end
def new!(%DateTime{} = first, %DateTime{} = last) do
first = DateTime.truncate(first, :second)
last = DateTime.truncate(last, :second)
if DateTime.before?(first, last) do
%__MODULE__{first: first, last: last}
else
%__MODULE__{first: last, last: first}
end
end
def to_timezone(%__MODULE__{first: first, last: last}, timezone) do
first = DateTime.shift_zone!(first, timezone)
last = DateTime.shift_zone!(last, timezone)
%__MODULE__{first: first, last: last}
end
def to_date_range(datetime_range, timezone) do
%__MODULE__{first: first, last: last} = to_timezone(datetime_range, timezone)
first = DateTime.to_date(first)
last = DateTime.to_date(last)
Date.range(first, last)
end
def length(%__MODULE__{first: first, last: last}, unit) do
DateTime.diff(last, first, unit)
end
end
|