首页 > 解决方案 > How to compre DateTime with generic times in ruby

问题描述

I'm working on a ruby on rails app that can automatically schedule events. Every event has a start_time datetime and an end_time datetime.

Here is the problem:

In general, events shouldn't be scheduled before 9:00am or after 10:30pm. This is true for edge cases as well. I.e, even if an event ends after 9:00am, it shouldn't start before it. Similarly, if an event starts before 10:30pm, it should not go on passed 10:30pm.

How can I go about making sure that every event fits this criteria, when some of these events are on different dates?

The problem i'm having is that, yes, a DateTime object has a date and a time associated with it. But so does a Time object.

This means that if I have an event with a start and end datetime between 9am and 10:30pm, my comparison will always return false if I'm comparing a time object with a datetime object of a different date.

Here is what I had in mind:

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

Problems with this code:

  1. It doesn't take into account minutes. I.e., if I ever change min_start_time to 9:30 am, then this method won't work anymore.
  2. There are different dates involved. event_start_time and event_end_time, which are DateTimes, obviously have the same date. However, min_start_time and max_end_time, Which are created using Time.new have a different date (I just default it 2020-01-01). This wouldn't be a big problem if I knew beforehand what the dates would be for the events, however I won't in this case.

Any ideas for an algorithm to make this check elegantly?

标签: ruby-on-railsrubydatetime

解决方案


活动不应安排在上午 9:00 之前或晚上 10: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?适用于“可以使用<=>运算符比较的任何对象”。(请参阅范围中的自定义对象


推荐阅读