Эликсир понизить, чтобы найти первую доступную дату в диапазоне с Timex - PullRequest
0 голосов
/ 27 февраля 2019

У меня есть список дат, которые представляют бронирование для комнаты.Я хочу иметь возможность узнать, где находится первый доступный день в диапазоне дат.

# 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 ]

0 голосов
/ 28 февраля 2019

Хотя ответ @Badu правильный, я бы выложил решение с желаемым Enum.reduce_while/3.

В настоящее время в Elixir есть отличная встроенная поддержка дат в первомместо, поэтому я сомневаюсь, что следую, зачем вам нужно Timex.И вам лучше иметь дело с датами , а не с датами , когда речь идет о забронированных днях (если только вы не разрешаете бронирование с почасовой оплатой.) Но еслиВы хотите DateTime с, вот и вы:

# 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>
0 голосов
/ 28 февраля 2019

Вот версия без 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>}
0 голосов
/ 27 февраля 2019

Во-первых, вы можете отсортировать даты в порядке возрастания.Затем выполните итерации дат и проверьте пустые интервалы между датами и верните дату, если дата больше или равна дате.

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
...