Calculate next weekday in Ruby on Rails
I had to advance a Time to the next weekday for a client Ruby on Rails project. I admit I dreaded writing that code, I somehow imagined awful hacks atop awful hacks.
I'm happy to report it didn't turn out quite so bad after all:
def next_weekday(original_date)
one_day = 60 * 60 * 24 # in Rails just say 1.day
weekdays = 1..5 # Monday is wday 1
result = original_date
result += one_day until result > original_date && weekdays.member?(result.wday)
result
end
And the test that drove it:
def test_next_weekday_skips_weekend
original_date = Time.at(1165606025) # a Friday
assert_equal(5, original_date.wday, "this test should give a Friday")
new_date = next_weekday(original_date)
assert(
  new_date > original_date,
  "new date should be after the original date"
)
assert_equal(
  1, new_date.wday,
  "new date should be a Monday since we gave a Friday"
)
end
Please feel free to embarrass me with your improvements in the comments.
7 comments:
Here's my crack at it. :-)
def next_weekday(original_date)
one_day = 60 * 60 * 24
result = original_date + one_day
original_date.wday == 5 && result += one_day * 2
result
end
Nice code with a small flaw.
When doing addition on a date in ruby with the '+' symbol, one day is equal to the integer 1. Instead of using one_day = 60 * 60 * 24, use 1. That is assuming you are using a Date object for original_date. If you are using a Time object for original_date your code is correct for generating the same time at the next weekday. You might want to add code to this method to check the class type of the object being passed in to be on the safe side.
Had I learned to read as a young child I would have noticed you did specify that this was a Time object.
I would still recommend adding a check on the object being passed though.
Something along the lines of
one_day = 60 * 60 * 24 if original_date.class == Time else one_day = 1
Just in case some n00b like myself tried to throw a Date at it figuring that this method was intended to work on a date based on your choice of parameter name.
Stephen, Ruby's Date implementation is very heavyweight for day-to-day use (it handles lots of odd cases in past dates, different calendars, etc.), so it's much less used than Time. I wrote this for a Rails app, where Time is the standard way to represent a date.
But you're absolutely right that this code doesn't check its assumption. Good point.
Here's a slightly more concise version
def next_weekday (timeobj)
begin
timeobj += (60*60*24)
end until timeobj != 6 && timeobj != 0
timeobj
end
Because the until condition is at the end that loop will run once, adding one day, no matter what. If the next day is a weekday it quits and returns the day.
If it's not a weekday it will loop until it hits the next monday and then return the day. Hopefully this is handy for someone out there!
def next_weekday (timeobj)
begin
timeobj += (60*60*24)
end until timeobj.wday != 6 && timeobj.wday != 0
timeobj
end
a tiny revision to actually make this work...
This seems to be working for me...
original_date += 1.days until (1..5).member?(original_date.wday)
This is just a refactor of the previous comment, please feel free to let me know if it is incorrect!
Post a Comment