开发者

Given a date, how can I efficiently calculate the next date in a given sequence (weekly, monthly, annually)?

In my application I have a variety of date sequences, such as Weekly, Monthly and Annually. Given an arbitrary date in the past, I need to calculate the next future date in the sequence.

At the moment I'm using a sub-optimal loop. Here's a simplified example (in Ruby/Rails):

def calculate_next_date(from_date)
  next_date = from_date
  while next_date < Date.today
    next_date += 1.week # (or 1.month)
  end
  next_date
end

Rather than perform a loop (which, although simple, is inefficient especially when given a date in the distant past) I'd like to do this with date arithmetic by ca开发者_StackOverflow社区lculating the number of weeks (or months, years) between the two dates, calculating the remainder and using these values to generate the next date.

Is this the right approach, or am I missing a particularly clever 'Ruby' way of solving this? Or should I just stick with my loop for the simplicity of it all?


Because you tagged this question as ruby-on-rails, I suppose you are using Rails. ActiveSupport introduces the calculation module which provides an helpful #advance method.

date = Date.today
date.advance(:weeks => 1)
date.advance(:days => 7)
# => next week


I have used the recurrence gem in the past for this purpose. There are a few other gems that model recurring events listed here.


If you are using a Time object, you can use Time.to_a to break the time into an array (with fields representing the hour, day, month, etc), adjust the appropriate field, and pass the array back to Time.local or Time.utc to build a new Time object.

If you are using the Date class, date +/- n will give you a date n days later/earlier, and date >>/<< n will give you a date n months later/earlier.

You can use the more generic Date.step instead of your loop. For example,

from_date.step(Date.today, interval) {|d|
    # Each iteration of this block will be passed a value for 'd'
    #   that is 'interval' days after the previous 'd'.
}

where interval is a length of time in days.

If all you are doing is calculating elapsed time, then there is probably a better approach to this. If your date is stored as a Date object, doing date - Date.today will give you the number of days between that date and now. To calculate months, years, etc, you can use something like this:

# Parameter 'old_date' must be a Date object
def months_since(old_date)
    (Date.today.month + (12 * Date.today.year)) - (old_date.month + (12 * old_date.year))
end
def years_since(old_date)
    Date.today.year - old_date.year
end
def days_since(old_date)
    Date.today - old_date
end
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜