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
精彩评论