• Brian Boorman (google)

    $theFrist

    Ftfy.

  • (nodebb)

    I expect the "weird feb issue" was how to deal with questions like "what date is one calendar month before 31 March?".

  • (nodebb) in reply to Andrew Taylor

    "what date is one calendar month before 31 March?"

    0 March.

    https://en.wikipedia.org/wiki/List_of_non-standard_dates#March_0

  • (nodebb)

    "Weird Feb" was a guy in accounting who had an issue with herpes.

  • P (unregistered)

    So, where's the WTF? PHP dev not reading yhe language documentation? That stuff happens more than cache misses.

  • Naomi (unregistered) in reply to Steve_The_Cynic

    Today I learned! This was an interesting read; thank you for sharing!

  • my name is missing (unregistered)

    There are three hard things in software development: naming things, date handling and off by one errors.

  • my name is missing (unregistered) in reply to my name is missing

    Argh, two things.

  • Brian (unregistered) in reply to my name is missing

    An off-by-one error in the stock off-by-one-error joke. Brillant! It's been a while since I've had such a good laugh on this site.

  • MiserableOldGit (unregistered)

    Date arithmetic can be a total bastard for other reasons. Having worked with things like defined benefit pensions "months" and "years" aren't what we might think they are, they are what the actuary says they are (subsequently amended by whatever the last court case ruled). You simply can't assume a year is 365 days, or 365.25 days, or what it really is, without checking ... same for a month. And it is not going to be consistent within any given calculation, let alone chunk of code.

    If something looks wrong, it probably isn't, it just could probably do with better variable/constant names. Although as half the test cases contradict each other you can never be sure it's right.

    Granted, I'm sure most of these date WTFs are because an inexperienced coder didn't know or trust the inbuilt date libraries, or maybe an experienced one who never forgave java.util.Date or something, but I've had to hand craft stuff a lot because "under these circumstances the leap day is not applied unless blah blah blah", for instance.

  • Jens (unregistered)

    The simplest fix is probably: 'date("Y-m", strtotime("first day of now - 1 month"))'. This works, whether leap year or not and also at the beginning of the year.

  • Anon (unregistered)

    The problem can be sen with: echo date("Y-m-d", strtotime("-1 month", strtotime("2020-03-31")));

    A possible WTF fix is: echo date("Y-m-d", strtotime("-1 month", strtotime(date("Y-m-01", strtotime("2020-03-31")))));

  • Rhialto (unregistered)

    strtotime is a complex and delicate creature. It has been subject to many subtle (and buggy) changes over time. It will do its best : in the absence of any actual time it assumes NOW, and parses the inputted parameter to "calculate" a relative date (the current version automatically applies DST and whatever timezone is in effect - well, it does for the version I'm using! And that can be a serious PITA when I'm manipulating DATETIME). So "date('Y-m', strtotime('-1 month'))" will give you this day, last month (effectively). The "weird feb" thing might have been because of issues around its awareness and handling of leap years.

    There are lots of W(hy)TF's in this code snippet, but there is little or no mystery to the logic behind "...The “fix” is to prepend a “0” to the front of the dates...". Yes, the returned, formatted, month will be "01", "02",,, "12" etc and there are a million and one reasons as to why I (or anybody else) would want this. That "$date->format('m')-1" works is a benefit / curse / abuse / elegant / etc / etc of all that is holy about ... whatever. The fact remains that it is an arithmetic operation on a string and if you ask PHP to subtract 1 from "02" it will give you 1 (we will quietly ignore what happens if you subtract 1 from "01" or 2 from "02" etc).

    Like I said, there are many (major and minor) WTF's here ...

  • Hasseman (unregistered)

    I remeber cache invalidation as the second hard thing to do. Maybe we have three hard things now?

    I worked with date for many years with historic time zones. What was the time difference between GMT and Riad between 1984-1988. 3 hours and 7 minutes. before and after it was just 3 hours!

    Always keep time/date as system type until you need to display it. Then use local display.

  • Hasseman (unregistered)

    If what I'm heard is correct Julius Cesar changed July to contain 31 days by remove one from February. Augustus did the same with August month to get the same status as Cesar

  • scragar (unregistered)

    I'll never understand why people don't just use first day of and last day of.

        echo date("Y-m-d", strtotime("-1 month", strtotime("2020-03-31")));
        // 2020-03-02
        echo date("Y-m-d", strtotime("first day of -1 month", strtotime("2020-03-31")));
        // 2020-02-01
    

    It's a really simple solution that avoids the need for messing around when we don't care about the day and it doesn't have horrible problems with PHP trying to work out the 31st of February or whatever.

  • 🤷 (unregistered) in reply to MiserableOldGit

    Date arithmetic can be a total bastard for other reasons.

    For example because of "banker's years", which have 12 months with 30 days each. "Because it's easier to calculate interest this way."

  • (nodebb)

    Another WTF is that the block of code after the Feb issue comment overwrites all the $theXXXMonth variables that were set above, so there was no reason for all that formatting and splitting.

  • ooOOooGa (unregistered) in reply to Barry Margolin 0

    Yeah, that is what I see as really strange. The progression from good to bad code.

    First it does the job almost right:

    $theFirst = date("Y-m", strtotime("-1 months"));

    Then it adds in something strange and pointless:

    list($theFirstYear, $theFirstMonth) = explode('-', $theFirst);

    $dateArray = array( ... ,$theFirstYear.'-'.$theFirstMonth);

    And finally it gets overwritten to do something completely bizarre.

    $date = new DateTime();

    $theFirstMonth = $date->format('m')-1;

    if(strlen($theFirstMonth) < 2){

    $theFirstMonth = '0'.$theFirstMonth;
    

    }

  • tbo (unregistered)

    Every time I see a PHP article, I know there are going to be a lot of people commenting who don't understand the language.

    All versions of this code are buggy. Here's why:

    The original code is the first block. This doesn't work because months have different days. "March 30th minus one month" is February 30, which doesn't exist, so you get March 1 (in a leap year) or March 2nd (in a non-leap year), which is the appropriate number of days after the last day of the month that does exist. Note that this isn't strictly a February issue; if you ask for one month before May 31st, it will tell you May 1.

    Because of this, they changed it to try to get the month and subtract one. It makes a date, gets the numeric month value, and subtracts from it. Adding the zero on makes perfect sense, because whether you get the month as "01" or "1", you're getting a string, and once you subtract an integer from a string, you're left with an integer, so you need to add the zero back. What this code misses is that when it comes to months, 1-1 is 12, not 0, and 1-2 is 11, not -1, and so on. The line should really something like:

    $theFirstMonth = ($date->format('m')-2+12)%12+1;

    You want to subtract a month, but you want a result from 1-12, not 0,11, so you're going to add 1, so subtract 2, not 1. Then add 12, in case the number is negative, then modulo it, then add the 1 back in to correct the range back to 1-12 from 0-11.

    Or a more sane version that's longer:

    $theFirstMonth = ($date_format('m') - 1) % 12;
    if ($theFirstMonth == 0) $theFirstMonth = 12;
    

    Either way, add the leading zero back after this.

  • tbo (unregistered)

    Sorry, $date->format, not $date_format above.

  • tbo (unregistered) in reply to scragar

    To be honest, I'm a little surprised it keeps track of the fact that it's on "February 30th" long enough that "first day of" actually returns the right month.

  • sizer99 (google) in reply to Barry Margolin 0

    Yeah, there's a lot of historic stuff in here. Not working? Completely rewrite the logic but leave the old code in there just in case!

  • Anon (unregistered) in reply to sizer99

    Please don't leave old code around. That's what source control is for.

  • James (unregistered) in reply to tbo

    I'm a little surprised you assume first day is applied after -1 month.

  • Shut the fuck up (unregistered) in reply to P

    Shut the fuck up

  • X (unregistered)

    Dates are hard; Printing a formatted string containing just numbers should not be, but in Fortran it is. (Or at least, the format specifier syntax makes it hard to read.)

  • tbo (unregistered) in reply to James

    Wanna see something interesting? Guess what each of these makes:

    echo date('Y-m-d', strtotime('+4 weeks'), "\n"; echo date('Y-m-d', strtotime('first day of'), "\n"; echo date('Y-m-d', strtotime('first day of +4 weeks'), "\n";

  • löchlein deluxe (unregistered) in reply to tbo

    You want to subtract a month, but you want a result from 1-12, not 0,11, so you're going to add 1, so subtract 2, not 1. Then add 12, in case the number is negative, then modulo it, then add the 1 back in to correct the range back to 1-12 from 0-11.

    Nyooooo math, nyo-ho-ho-hoooo math: https://www.youtube.com/watch?v=UIKGV2cTgqA

    Anyway, I'm disappointed there wasn't a "the leading 0 causes the number to be interpreted as octal, hijinx ensue" part to this.

  • scragar (unregistered) in reply to tbo

    echo date('Y-m-d', strtotime('+4 weeks'), "\n"; echo date('Y-m-d', strtotime('first day of'), "\n"; echo date('Y-m-d', strtotime('first day of +4 weeks'), "\n";

    Parse error: syntax error, unexpected ';', expecting ')' in /scratch/AhTDv on line 3
    

    Assuming the brackets are fixed then yes you get unexpected results if you assume the order of inputs to matter. strtotime does modifications to existing parts of the date in order, then applies "first day of", not the other way around. Effectively it's doing this:

    day_of_month = 13; // Initial state
    day_of_month += 4*7; // +4 weeks
    day_of_month = 1; // first day of
    

    What does work if you want to mess with this sort of thing is modification of other parts that would change the day like hours or minutes

    echo date('Y-m-d', strtotime('first day of +168 hours')), "\n";
    // 2020-03-08
    echo date('Y-m-d', strtotime('first day of +2880 minutes')), "\n";
    // 2020-03-03
    

    Although honestly if you're looking for the first of the month + a given number of days or weeks you should probably write it taking into account that you already know what day of the month you want and maybe should use something like mktime instead.

  • Richard Wells (unregistered) in reply to MiserableOldGit

    "Date arithmetic can be a total bastard for other reasons."

    Not to mention that fact that if you go back far enough, there was a several hundred year period in which the world did not agree on what date it was.

Leave a comment on “Dating for Three Months”

Log In or post as a guest

Replying to comment #:

« Return to Article