NB: My 1st meditation. I don't 'think' this is a SPW post - I don't have a problem, just want to share this.

In the last couple of weeks I've spent some spare time (at work) developing a nice little personal information manager. It was working fine yesterday, I did some more work on it today, and was happy to call it complete (for now), zip it up and start thinking about other things. This is where the problem struck!

I was happily using my new calendar and diary functions and wanted to skip forward and view the calendar for Feburary 2001. Suddenly - white screen (my app is CGI-based for ease of accessibility). I managed to locate the source of the problem and correct it, but it raised an interesting question...

I was using Date::Calc::Add_Delta_YMD(), specifically:
my @DateToday = Date::Calc::Today_and_Now(); # ($year,$month,$d +ay, $hour,$min,$sec) my @MonthAhead = Date::Calc::Add_Delta_YMD($DateToday[0],$DateToday[1] +,$DateToday[2],0,+1,0);
Now, this is fine if you want to work out the date say: one month from January 6 2001. But if you want to work out the date one month from January 29 2000 - it falls down (there is no February 29 2001 - or April/May/September/etc 31st). I was using strict and warnings and I didn't get an error - just a blank screen.

Luckily, I didn't need to know the exact date $x months from now, just the month, and from there I do other calculations. But what if you do? - well I suppose one way is to remember this problem and do a quick check to see if the date exists and adjust if necessary.

But, my question is this - what is the date a month from January 30th? How do you decide how to adjust for this variable? Perhaps it varies from situation to situation (TIMTOWTDI) or maybe I am being thoroughly stupid.

I would love to hear if anyone else has been stumped by such a problem.

Replies are listed 'Best First'.
Re: Year /^\d+$/ bug!
by footpad (Abbot) on Nov 30, 2000 at 03:28 UTC
    I've run into this a couple of different times in my work and all I can tell is that both are correct...depending on the business needs at hand.

    For example, a billing statement often, by law, needs to provide a set number of days. When implementing that sort of system, you find out what the client's accountants use when balancing the books and go from there.

    On the other hand, a calender is generally looking for something to the effect of Dec 30 to Jan 30.

    To handle the "Feb 30" problem, I usually decrement the previous result by one day until I have a valid date. The next search then becomes Mar 28 (or 29, depending on the year).

    If you're not completely comfortable forcing either approach, then use gaspodethewonderdog's idea: implement both and then let the end user/developer choose which they prefer via a property or configuration setting.

    --f
      For example, a billing statement often, by law, needs to provide a set number of days.

      I think that is the point - it's all the granularity you are working with. If you are dealing with days, then adding 30 days to Jan 31st should bring you into March. If you are dealing with months, then adding 1 month to Jan 31st should bring up into Feburary.

      In my opinion, that is the 'correct' default behavior for a module where you aren't sure what the end developer will need, perhaps generating warnings if you can't do an exact mapping (Jan 31st -> Feb 31st).

      =Blue
      ...you might be eaten by a grue...

Re: Year /^\d+$/ bug!
by Blue (Hermit) on Nov 30, 2000 at 00:44 UTC
    I'd write that up and send it to the module's author. Sounds buggy to me. At the very least, it should return an error code that there is no Feb 29th. On a more Perlish level, it should try and 'do the right thing'.

    My personal opinion on the 'right thing' is that +1 month refers to the next month, so wrapping Feb 29th (non-leap year) to March 1st seems wrong. (I would have a differnt feeling for adding a month worth of days.) But truncating it down to Feb 28th also seems wrong. Perhaps an error code is the best result.

    But in any case, let the author know. Sounds like they didn't come across that condition, so never checked for it. Feedback is important to keeping the quality of modules high.

    =Blue
    ...you might be eaten by a grue...

      Hmmm, I just tried it again and it seems to work:
      perl -MDate::Calc -MData::Dumper -e "print Dumper(\Date::Calc::Add_Delta_YMD(2001,1,29,0,+1,0));" and it printed:
      $VAR1 = \2001; $VAR2 = \2; $VAR3 = \28;
      I tried it several different ways and all of them seemed to work now. Maybe the author of Date::Calc saw my post, wrote a fix, hacked my box and replaced by buggy module with a new version!

      Or maybe I didn't test it properly before I posted.

      Well, as far as Date::Calc is concerned (if we are to believe it) we should truncate it down to Feb 28th.

      I don't quite feel at ease about this, but my world view is now secure again - I can say with confidence the exact date in one month's time!
        The business answer is to truncate and stay at the end of the month. I asked an accountant who audits code more than a year ago since the guys I worked with couldn't agree. His point was that If you are in January, adding one month should NEVER get you March. =)

        --
        $you = new YOU;
        honk() if $you->love(perl)

        Ah yes, but can you tell "1 day" later when daylight savings is involved. 8) 23 hours one way, 25 the other.

        Those wacky humans...

        =Blue
        ...you might be eaten by a grue...

Re: Year /^\d+$/ bug!
by chipmunk (Parson) on Nov 30, 2000 at 00:53 UTC
    As a point of reference, the ADD_MONTHS function in Oracle would return the last day of February in that situation. Subtracting a month from March 30 would also return February 29.

    I think that approach makes the most sense. The programmer can choose whether or not to verify the dates before adding/subtracting months (if an error were returned, the programmer would be forced to check each date). And, I agree with Blue that adding one month should always return a date in the next month, and should not wrap over to the month after that.

      Yes, I definitely agree that it shouldn't wrap over to the next month or you will lose a month - could be costly if your your company pays your check on the 31st of each month! :) Still, something seems fishy.
Re: Year /^\d+$/ bug!
by AgentM (Curate) on Nov 30, 2000 at 01:55 UTC
    I suppose that specifying a month is just as vague as specifying a millenium (100 or 99 years?). I doubt this terminology was brought up to describe change in time, since there is no means of determining EXACT time. For this reason, you are forced to ignore it. Instead, since clarity comes before everything (as we have so graciously learned from quality M$ software), instead of some option specifying 1 month=30 days or 1 month=30 or 31 days, you should specifically choose 30 days and make it clear that you are not calling it a month or anything else- it's just 30 days-or 30*24 hours (shit- now is a day 24 or 23 5/6 hours?! scientist bastards!) The same goes for a year- make it simply 52 weeks or 30 days. Allowing options like "make millienium end on Jan 1, 2001" can only serve to complicate your program beyond user-friendliness and its really not useful. Sidenote: you mention strict and warnings but for CGI, but to snatch stderr errors, you should be using CGI::Carp. I have found it an indispensable tool for debugging.
    AgentM Systems nor Nasca Enterprises nor Bone::Easy nor Macperl is responsible for the comments made by AgentM. Remember, you can build any logical system with NOR.
Re: Year /^\d+$/ bug!
by gaspodethewonderdog (Monk) on Nov 30, 2000 at 00:37 UTC
    What you are asking is... is one month from January 30th February (28|29) or is it March (1|2). That's more of a stylistic question, and I guess maybe an option for the user to check. Personally I think I am more of the mind that this is almost a nonsensical question because no logic, math or anything else can really dictate what the correct answer is. The best you can do is just give it a good guess! :)
      I don't know - I think that there must be an answer - if the following statements are true...

      1. May 8th, is exactly one month after April 8th
      2. June 8th is exactly one month after May 8th

      that then there must be a definition of "month" that works regardless of a month being 28/29/20/31 days long?

      If so, this definition must surely apply to dates at the end of the month as equally as it does for dates at the beginning of the month.

      I think that it's obvious what the answer isn't, but I think that there is an answer.