Rails - Is a date between two dates?
I'm creating a system that needs to make sure planned "Trips" do not over lap. To do this I am trying to validate the start_date and end_date to make sure they are not within the start and end dates for another trip.
class TripdateValidator < ActiveModel::EachValidator
def validate_each(object, attribute, value)
# Check value exists as end_date is optional
if value != nil
parsed_date = Date.parse(value)
# is the start开发者_JS百科 date within the range of anything in the db
trips = Trip.where(['start_date >= ? AND end_date <= ? AND user_id = ?', parsed_date, parsed_date, object.user_id])
# overlapping other trips, aghhh
object.errors[attribute] << 'oh crap' if trips
end
end
end
The date is coming from a type="date" field which is powered by jQuery UI Datepicker and contains the format 2011-01-01. This gets to rails as 2011-01-30 00:00:00 UTC which I don't fully understand.
If I try to use Date.parse() on this value it gives the error:
TypeError in TripsController#create
$_ value need to be String (nil given)
Rails.root:
/Users/phil/Sites/travlrapp.com Application Trace | Framework Trace | Full Traceapp/models/trip.rb:29:in
validate_each' app/controllers/trips_controller.rb:75:in
create' app/controllers/trips_controller.rb:74:in `create'
Whenever I run the query, nothing is returned. Could this be a date format issue, is my logic broken or am I doing something really stupid? Been stuck on this a while and google is no help.
Edit People are focusing on the Date.parse error but that is not the main problem. Where im stuck is that I don't understand how to do date comparisons when everything is in totally different formats.
I have swapped Date.parse() for Chronic.parse() and now I am getting the following SQL queries generated:
SELECT "trips".* FROM "trips" WHERE (start_date >= '2011-01-26 00:00:00.000000' AND end_date <= '2011-01-26 00:00:00.000000' AND user_id = 19)
This returns nothing. The dates I am testing against are:
start: 2011-02-17 00:00:00.000000 end: 2011-02-21 00:00:00.000000
Seeing as I think dates are being formatted properly now it seems more like a logic problem. How the heck can I validate overlapping dates >.
Squeel power FTW!!! I'm sure you will love it
$ gem install squeel
model.rb
Model.where{date_colum > 10.years.ago & date_column < DateTime.now}
For those that land here, check the Rails guides on Range conditions:
Client.where(created_at: (Time.now.midnight - 1.day)..Time.now.midnight)
That will produce the following SQL:
SELECT * FROM clients WHERE (clients.created_at BETWEEN '2008-12-21 00:00:00' AND '2008-12-22 00:00:00')
So you can search for fields between two dates using a range.
I'm responding to your edit, mostly, and I think you're not getting any results for two reasons:
First: I suspect your parser is giving you DateTime objects, while you really just want plain Date objects. Pass a DateTime into Rails's SQL query generator, and it gives you a 'YYYY-MM-DD hh.mm.ss.uuu...'
string, while the database expects just a plain 'YYYY-MM-DD'
string for date column comparison; the DB date parser fails, gives back NULL, all comparisons with NULL fail, etc., and you get back an empty result set.
Change parsed_date
to parsed_date.to_date
and you should be okay on that front.
Second: Take a look at your SQL, this chunk in particular start_date >= ? AND end_date <= ?
. It looks to me like it'll only detect a collision if the trip you're checking against has a start date that's later than its end date.
So try start_date <= ? AND end_date >= ?
or ? BETWEEN start_date AND end_date
, which I personally prefer, as it's a bit more readable.
Hope this helps!
For overlap validation you can try one of these gems https://github.com/chrisb87/range_validator or https://github.com/robinbortlik/validates_overlap
I prefer:
Model.where{date_colum >> (10.years.ago..Datetime.now)}
Have you tried with the TimeZone
class:
parsed_date = Time.zone.parse(value)
Here's the docs:
http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html#method-i-parse
I would log the "value" and see if it's remotely close to something that Date.parse() can handle. If it looks parsable see if Date.parse works on it in irb and if so check it in console env (./script/console or rails c) for your app. That should track down the problem.
How about this?
overlaps = Trip.where(
"(DATEDIFF(start_date, ?) * DATEDIFF(?, end_date)) >= 0",
self.end_date,
self.start_date
)
It basically says this:
If each start date looks toward the other's end date, are they looking in the same direction? Then they overlap.
You can use cover method
it's returns true if data between selected range otherwise false
精彩评论