简体   繁体   中英

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 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 Trace

app/models/trip.rb:29:in
validate_each'
app/controllers/trips_controller.rb:75: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 <= ? 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 >= ? start_date <= ? AND end_date >= ? or ? BETWEEN start_date AND end_date ? BETWEEN start_date AND end_date , which I personally prefer, as it's a bit more readable.

Hope this helps!

我更喜欢:

Model.where{date_colum >> (10.years.ago..Datetime.now)}

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM