views:

55

answers:

2

FYI, There is some overlap in the initial description of this question with a question I asked yesterday, but the question is different.

My app has users who have seasonal products. When a user selects a product, we allow him to also select the product's season. We accomplish this by letting him select a start date and an end date for each product.

We're using date_select to generate two sets of drop-downs: one for the start date and one for the end date.

Including years doesn't make sense for our model. So we're using the option: discard_year => true

When you use discard_year => true, Rails sets a year in the database, it just doesn't appear in the views. Rails sets all the years to either 0001 or 0002 in our app. Yes, we could make it 2009 and 2010 or any other pair. But the point is that we want the months and days to function independent of a particular year. If we used 2009 and 2010, then those dates would be wrong next year because we don't expect these records to be updated every year.

My problem is that we need to dynamically evaluate the availability of products based on their relationship to the current month. For example, assume it's March 15. Regardless of the year, I need a method that can tell me that a product available from October to January is not available right now.

If we were using actual years, this would be pretty easy.

For example, in the products model, I can do this:

def is_available?
  (season_start.past? && season_end.future?)
end

I can also evaluate a start_date and an end_date against current_date

However, in setup I've described above where we have arbitrary years that only make sense relative to each other, these methods don't work. For example, is_available? would return false for all my products because their end date is in the year 0001 or 0002.

What I need is a method just like the ones I used as examples above, except that they evaluate against current_month instead of current_date, and past? and future months instead of years.

I have no idea how to do this or whether Rails has any built in functionality that could help. I've gone through all the date and time methods/helpers in the API docs, but I'm not seeing anything equivalent to what I'm describing.

Thanks.

A: 

You might want to consider not using Date objects. What if season_start_month and season_end_month were each an integer 1-12, set with your dropdown? Then when doing your is_available? comparison, you could dynamically create the full date for season_start, doing some math for transitions over December to January. This could use some refactoring, and isn't tested, but should do the trick. Assumes that season_start_month and season_end_month are integers stored in this model:

class Product < ActiveRecord::Base
  def is_available?
    now = Time.now
    season_start < now && now < season_end
  end

  def season_start
    now = Time.now
    current_month = now.month
    season_start_year = season_start_month < current_month ? now.year : now.year - 1
    Time.local(season_start_year, season_start_month)
  end

  def season_end
    now = Time.now
    current_month = now.month
    season_end_year = season_end_month > current_month ? now.year : now.year + 1
    Time.local(season_end_year, season_end_month)
  end
end
Ben
Thanks for this. I ended up implementing the other answer, just because it was easier. But I think this makes sense too.
MikeH
A: 

Set the year of the current date to 1000 and perform a range compare with the start and end dates.

def is_available?
  t = Date.today
  d = Date.new(1000, t.month, t.day)
  (season_start..season_end).include?(d) or 
     (season_start..season_end).include?(d+1.year) 
end

Second comparison above is to address the following scenario:

Lets say today Jan 15 and the season is from Oct - Feb. In our logic, we set the date to Jan 15 1000. This date will not be within Oct 1 1000 - Mar 1 1001. Hence we do the second comparison where we advance the date by a year to Jan 15 1001, which is within the range.

KandadaBoggu
That did it! Thanks. Smart approach.
MikeH
Hey, I wanted to come back and thank you again for this very smart approach. I've been able to use variations on this method to do all kinds of cool stuff all over my app! Huge help.
MikeH
I am glad to hear that :-)
KandadaBoggu