Как вычислить DateTime с помощью c раз в ruby - PullRequest
1 голос
/ 15 февраля 2020

Я работаю над приложением ruby на рельсах, которое может автоматически планировать события. Каждое событие имеет start_time datetime и end_time datetime.

Вот проблема:

В общем, события не должны планироваться до 9:00 или после 10:30 пм. Это верно и для крайних случаев. То есть, даже если событие заканчивается после 9:00, оно не должно начинаться раньше. Точно так же, если событие начинается до 22:30, оно не должно go в прошедшем 22:30. соответствует этому критерию, когда некоторые из этих событий происходят в разные даты?

Проблема, с которой я столкнулся, заключается в том, что да, объект DateTime имеет дату и время, связанные с ним. Но так же как и объект Time.

Это означает, что если у меня есть событие с датой начала и endtime между 9:00 и 22:30, мое сравнение всегда будет верните false, если я сравниваю объект time с объектом datetime с другой датой.

Вот что я имел в виду:

def event_out_of_bounds?(event_start_time, event_end_time, min_start_time, max_end_time)

if event_start_time.hour > max_end_time.hour then
  return true
end

if event_end_time.hour > max_end_time.hour then
  return true
end

if event_start_time.hour < min_start_time.hour then
  return true
end

if event_end_time.hour < min_start_time.hour then
  return true
end

return false

end

Проблемы с этим кодом:

  1. Не учитываются минуты. То есть, если я когда-нибудь поменяю min_start_time на 9:30, тогда этот метод больше не будет работать.
  2. Там будут разные даты. event_start_time и event_end_time, , которые представляют собой DateTimes , очевидно, имеют одинаковую дату. Однако , min_start_time и max_end_time, , созданные с помощью Time.new , имеют другую дату (я просто по умолчанию ее 2020-01-01). Это не было бы большой проблемой, если бы я знал заранее, какие даты будут для событий, однако я не буду в этом случае.

Какие-нибудь идеи для алгоритма, чтобы сделать эту проверку элегантно?

Ответы [ 2 ]

2 голосов
/ 15 февраля 2020

события не должны планироваться до 9:00 или после 22:30.

Я мог бы использовать Range#cover? для этого.

require 'active_support/all'

def validate_schedule(s, e)
  min = s.time_zone.local(s.year, s.month, s.day, 9, 0, 0)
  max = s.time_zone.local(s.year, s.month, s.day, 22, 30, 0)
  (min..max).cover?(s..e)
end

# Tests
et = ActiveSupport::TimeZone.new('Eastern Time (US & Canada)')
t = ->(d, h, m) { et.local(2020, 2, d, h, m, 0) }
test = ->(s, e) { puts format('%s %s = %s', s, e, validate_schedule(s, e)) }
test[t[1, 8, 59], t[1, 22, 30]] # false, start too early
test[t[1, 9, 0], t[1, 22, 30]] # true, 10:30 pm ok
test[t[1, 9, 0], t[1, 21, 30]] # true, 9:30 pm also ok
test[t[1, 9, 0], t[1, 22, 31]] # false, end too late
test[t[1, 9, 0], t[2, 22, 30]] # false, no all night parties

Если вы разрешаете многодневные события, просто вычислите max от даты окончания (e) вместо этого:

  max = e.time_zone.local(e.year, e.month, e.day, 22, 30, 0)

Range.cover? работает с "любыми объектами, которые можно сравнить с помощью оператора <=> «. (см. Пользовательские объекты в диапазонах

0 голосов
/ 16 февраля 2020

Подход

Любопытно, что вопрос сформулирован в терминах DateTime объектов, учитывая, что основная проблема, по-видимому, заключается просто в определении того, упадет ли запланированное время спортивного матча. в течение определенного времени. Использование DateTime объектов является лишь одним из способов решения проблемы, но не единственным. В этом смысле вопрос может попасть под заголовок XY problem .

Фактически, я считаю, что использование DateTime объектов усложняет проблему излишне. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *. Каждый раз проще express как просто двухэлементный массив, который содержит часы и минуты. Например, временное окно, которое расширяется с 12:30 (24-часовые часы) до 22:00 этого вечера, может быть выражено как диапазон: 1

window = [12, 30]..[22, 0]

Время события можно определить аналогично: 2

event_time = [15, 45]..[19, 30]

Для удобства я буду ссылаться на двухэлементные массивы, которые являются конечными точками этих диапазонов, просто на "времена".

Важным соображением является то, что время начала и окончания спортивного мероприятия не обязательно должно совпадать с одним днем. Примером может служить турнир, в котором матчи должны проходить в промежутке между 12:30 (24-часовой формат) и 1:00 (AM) следующего дня. Это не необычно. Это временное окно будет выражаться:

window = [12, 30]..[1, 0]

Я предполагаю:

  • время окончания меньше, чем через 24 часа после соответствующего времени начала; и
  • временной интервал одинаков каждый день.

Конечно, можно внести изменения в приведенный ниже код, если одно из предположений не выполняется.

Код

Построим метод valid?, который возвращает true (иначе false), если данное событие попадает в заданное временное окно:

def valid?(event_time, window)
  e = time_to_mins(event_time.begin)..time_to_mins(event_time.end)
  w = time_to_mins(window.begin)..time_to_mins(window.end)
  if w.begin <= w.end
    w.cover?(e)
  else
    (w.begin <= e.begin || e.begin <= w.end) &&
    (w.begin <= e.end   || e.end   <= w.end)
  end
end

def time_to_mins(time_arr)
  60 * time_arr.first + time_arr.last
end

Примеры

time_to_mins [ 0, 10]              #=> 10 
time_to_mins [22, 30]              #=> 1350

window = [ 9, 30]..[22, 30]

valid?([13, 45]..[16, 30], window) #=> true 
valid?([ 8, 45]..[11, 30], window) #=> false 
valid?([22, 15]..[ 0, 15], window) #=> false

window = [12, 30]..[1, 0]

valid?([16, 15]..[19, 30], window) #=> true 
valid?([22, 45]..[ 0, 30], window) #=> true 
valid?([ 0, 15]..[ 0, 45], window) #=> true 
valid?([22, 45]..[ 1, 30], window) #=> false 
valid?([ 0, 15]..[ 1, 45], window) #=> false 

1. При желании, в качестве альтернативы, можно express раз использовать хэши, например, { hour: 12, min: 30, sec: 0 }.

2. Информация для каждого события может храниться в более крупной структуре данных, такой как event = { start_date: [2020, 2, 7], time_block: [[22, 45, 0]..[ 0, 30, 0]], player_1: 'R. Federer', player_2: 'R. Nadal' }, с использованием только event[:time_block] в приведенном выше расчете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...