# Very simple Date math goes randomly bonkers

I have a "timesheet" in my app that simply displays the week range
based on a passed in date (params[:date]).
The view iterates through the Date::ABBR_DAYNAMES and shows a link to
the corresponding date.

Trouble is that about 40% of the time the calculated dates go bonkers.
Am I missing something?

Model:
# Get a date range of the week that a particular date falls in
# This is broken out like mad for diagnostics
def self.get_work_week_date_range(date)
monday = date.monday
sunday = monday - 1.day
eow = date.end_of_week
saturday = eow - 1.day

dates = [sunday, saturday]

logger.info
(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
logger.info(">>>>> #{date}.monday = #{monday}")
logger.info(">>>>> #{monday}-1.day = #{sunday}")
logger.info(">>>>> #{date}.end_of_week = #{eow}")
logger.info(">>>>> #{eow}-1.day = #{saturday}")
logger.info(">>>>> #{date} == [#{dates[0]}, #{dates[1]}]")
logger.info
(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")

dates
end

Logger output:

2008-12-08.monday = 2008-12-08
2008-12-08-1.day = 2008-12-07
2008-12-08.end_of_week = 2008-12-14
2008-12-14-1.day = 2008-12-13
2008-12-08 == [2008-12-07, 2008-12-13]

... n times later ...

I have a "timesheet" in my app that simply displays the week range
based on a passed in date (params[:date]).
The view iterates through the Date::ABBR_DAYNAMES and shows a link to
the corresponding date.

What do you pass to get_work_week_date_range? Is it a string? Or an
instance of Date? Or Time?

If it worked once it should always work. Which suggests that maybe
you're passing in something different from time to time? One time a
Date, one time a Time?

Maybe?

Put together a test and explicitly pass the *same* argument (class and
value) and see if you can duplicate it.

What do you pass to get_work_week_date_range? Is it a string? Or an
instance of Date? Or Time?

I pull the date in from the params hash, and immediately parse it to a
Date:
Date.parse(params[:date]) rescue Date.today

If it worked once it should always work. Which suggests that maybe
you're passing in something different from time to time? One time a
Date, one time a Time?

You're right, that's why I've pounded my head against the wall for a
week!
This method is only called from one other method, and I've extracted
it really only for testing.

Put together a test and explicitly pass the *same* argument (class and
value) and see if you can duplicate it.

All tests pass

it "should know how to get the week based on a date" do
TimeCard.get_work_week_date_range(Date.parse("2008-08-05")).should
== [Date.parse("2008-08-03"), Date.parse("2008-08-09")]
TimeCard.get_work_week_date_range(Date.parse("2008-01-01")).should
== [Date.parse("2007-12-30"), Date.parse("2008-01-05")]
TimeCard.get_work_week_date_range(Date.parse("2008-02-29")).should
== [Date.parse("2008-02-24"), Date.parse("2008-03-01")]
TimeCard.get_work_week_date_range(Date.parse("2007-02-28")).should
== [Date.parse("2007-02-25"), Date.parse("2007-03-03")]
end

Perhaps the key thing to note is that 1772-05-19 is about 236 years
and 7 months ago, which (get your calculators out) is about 86400, ie
the number of seconds in a day. Whereas on an instance of Time, +/- 1
means +/- 1 second, on instances of Date/DateTime +/- means +/- 1
second. That is I suspect the basic problem.

On newer versions of rails, things like 1.day are smart enough to do
the right thing whether or not you add a date or a Time to them (the +
method on Date is overriden to check if the argument is a thing like
1.day).
Somewhere this has gone wrong. If you have any plugins that mess with
dates (or if you've being doing stuff like that yourself) then I'd
have a look at those - it could be something like once some random
model is loaded some extensions to Date are pulled in at that point
and from then onwards calculations are borked.

Fred

Perhaps the key thing to note is that 1772-05-19 is about 236 years
and 7 months ago, which (get your calculators out) is about 86400, ie
the number of seconds in a day. Whereas on an instance of Time, +/- 1
means +/- 1 second, on instances of Date/DateTime +/- means +/- 1
second. That is I suspect the basic problem.

Man, good call.

On newer versions of rails, things like 1.day are smart enough to do
the right thing whether or not you add a date or a Time to them (the +
method on Date is overriden to check if the argument is a thing like
1.day).

I'm using 2.2.2, but...

Somewhere this has gone wrong. If you have any plugins that mess with
dates (or if you've being doing stuff like that yourself) then I'd
have a look at those - it could be something like once some random
model is loaded some extensions to Date are pulled in at that point
and from then onwards calculations are borked.

... I do monkeypatch Date, but I ensured that I wasn't actually
overriding any existing method.

Thanks for you help Fred, this smells like the right track.

It was the facets/time library. It was loaded in a totally separate
part of the app, in a method that had not much to do with anything.

Thanks again Frederick; that one got your a WWR recommendation