I have an application that involves absence records for employees.
I need to ensure that the start and end dates for each record don\'t overlap.
So for example,
As a modification to the accepted answer, here's an overlaps scope that will work for DBs that don't understand DATEDIFF
scope :overlaps, ->(start_date, end_date) do
where "((start_date <= ?) and (end_date >= ?))", end_date, start_date
end
This draws on the solution for Determine Whether Two Date Ranges Overlap
There is a gem called validates_overlap which allows you to easily validate date range overlaps. You can also use scopes on the validation.
Use the gem validates_overlap.
gem 'validates_overlap'
For example, if you have a model called Meeting
with a start_date
and end_date
fields of type date
, you can easily validate that they don't overlap.
class Meeting < ActiveRecord::Base
validates :start_date, :end_date, overlap: true
end
Another more realistic example, say a Meeting
belongs_to a User
, you can scope it out, so it only validates meetings for a particular user.
class Meeting < ActiveRecord::Base
belongs_to User
validates :start_date, :end_date, overlap: { scope: 'user_id',
message_content: 'overlaps with Users other meetings.' }
end
I use these:
scope :overlaps, ->(start_date, end_date) do
where "(DATEDIFF(start_date, ?) * DATEDIFF(?, end_date)) >= 0", end_date, start_date
end
def overlaps?
overlaps.exists?
end
# Others are models to be compared with your current model
# you can get these with a where for example
def overlaps
siblings.overlaps start_date, end_date
end
validate :not_overlap
def not_overlap
errors.add(:key, 'message') if overlaps?
end
# -1 is when you have a nil id, so you will get all persisted user absences
# I think -1 could be omitted, but did not work for me, as far as I remember
def siblings
user.absences.where('id != ?', id || -1)
end
Source: https://makandracards.com/makandra/984-test-if-two-date-ranges-overlap-in-ruby-or-rails
While juanpastas solution is correct, it will be valid for creation of records, but can lead to false negative validations on updates.
If you need to edit an existing record, say the range is 2014-03-13..2014-06-12 and you want to reduce it to 2014-03-13..2014-04-12, you'll get an overlap error because it is checking AGAINST itself.
def siblings
Absence_users.where('user_id = ? AND id != ?', user_id, self)
end
will obviate that shortcoming. (Dave T's addition should also be followed, being DB-agnostic.)