Elixir减少使用Timex查找范围内的第一个可用日期

时间:2019-02-27 17:48:53

标签: recursion elixir reduce 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_whileTimex.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 :(得分:2)

尽管@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>

答案 1 :(得分:1)

首先,您可以按升序对日期进行排序。 然后对日期进行迭代,并检查日期之间的空间隔,如果日期大于或等于起始日期,则返回日期。

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

答案 2 :(得分:0)

此处没有Timex版本

使用[[1st date, 2nd date], [2nd date, 3rd date], .. [ith, (i-1)st]...](使用按偏移量1的zip格式)创建元素数组,然后找出两者相差超过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>}