Эликсир понизить, чтобы найти первую доступную дату в диапазоне с Timex
У меня есть список дат, которые представляют бронирование для комнаты. Я хочу узнать, где находится первый доступный день в диапазоне дат.
# let us assume today is the 1st Feb
today = #DateTime<2019-02-01 00:00:00Z>
# here are our booked days
booked_days = [
#DateTime<2019-02-08 00:00:00Z>,
#DateTime<2019-02-05 00:00:00Z>,
#DateTime<2019-02-03 00:00:00Z>,
#DateTime<2019-02-02 00:00:00Z>
]
Мы хотели бы вернуться сюда #DateTime<2019-02-04 00:00:00Z>
потому что это первая доступная дата.
Я смотрел на что-то подобное, используя Enum.reduce_while
в комбинации с Timex.Interval
но без везения как reduce_while
похоже, возвращает интервал после первого звонка.
today = Timex.now() |> Timex.beginning_of_day()
first_available =
Enum.reduce_while(booked_days, today, fn from, until ->
interval = Timex.Interval.new(from: from, until: until)
duration = Timex.Interval.duration(interval, :days)
if duration <= 1,
do: {:cont, from},
else: {:halt, interval}
end)
3 ответа
Во-первых, вы можете отсортировать даты в порядке возрастания. Затем выполните итерации дат и проверьте пустые интервалы между датами и верните дату, если дата больше или равна дате.
sorted_dates = Enum.sort(booked_days , fn a, b -> Timex.compare(a, b, :days)<0 end)
get_next_booking_date(sorted_dates, today)
def get_next_booking_date([], _from_date) do
nil
end
def get_next_booking_date([_last_booking_date], _from_date) do
# You can add a day to last booking date and return that date or return nil depending on your need
# Timex.add(_last_booking_date, Timex.Duration.from_days(1))
nil
end
def get_next_booking_date([next, next2 | rest], from_date) do
# add a day to the current day and check if there's an empty interval and that the empty slot is greater than from date
temp_next = Timex.add(next, Timex.Duration.from_days(1))
if Timex.compare(temp_next, next2, :days) == -1 and Timex.compare(temp_next, from_date) >= 0 do
temp_next
else
get_next_booking_date([next2 | rest], from)
end
end
Хотя ответ @Badu правильный, я бы выложил решение с желаемым Enum.reduce_while/3
,
Elixir в настоящее время имеет отличную встроенную поддержку дат, во-первых, поэтому я сомневаюсь, что следую, зачем вам нужно Timex
, И вам лучше иметь дело с датами, а не с датами, когда речь идет о забронированных днях (если вы не разрешаете заказы с почасовой оплатой.) Но если вы хотите DateTime
s, здесь вы идете:
# Please next time make your input available to copy-paste
[today | booked_days] =
[1, 8, 5, 3, 2]
|> Enum.map(& {{2019, 02, &1}, {0, 0, 0}}
|> NaiveDateTime.from_erl!()
|> DateTime.from_naive!("Etc/UTC"))
booked_days
|> Enum.sort(& Date.compare(&1, &2) == :lt)
|> Enum.reduce_while(today, fn d, curr ->
if Date.diff(d, curr) == 1,
do: {:cont, d},
else: {:halt, DateTime.add(curr, 3600 * 24)}
end)
#⇒ #DateTime<2019-02-04 00:00:00Z>
Вот версия без Timex
Создать массив элементов с [[1st date, 2nd date], [2nd date, 3rd date], .. [ith, (i-1)st]...]
(используя zip по смещению 1), затем найдите положение, в котором они отличаются более чем на 1 день.
defmodule DateGetter do
def get_next_date(booked_dates) do
sorted = Enum.sort(booked_dates)
date = sorted
|> Enum.zip(Enum.drop sorted, 1) # or use Enum.chunk_every for large sizes
|> Enum.find(fn {d1, d2} -> DateTime.diff(d2, d1) > 86400 end)
case date do
nil ->
{:error, "no dates found"}
{date1, date2} ->
(DateTime.to_unix(date1) + 86400) |> DateTime.from_unix
end
end
end
# Sample input:
booked_dates =
[2,5,3,8]
|> Enum.map(fn d ->
DateTime.from_iso8601("2015-01-0#{d} 01:00:00Z")
|> elem(1)
end)
DateGetter.get_next_date booked_dates
#> {:ok, #DateTime<2015-01-04 01:00:00Z>}