Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why This Date Function Only Works Until 30 December 2016?

Tags:

php

I have this code :

for ($i=1; $i<=12; $i++) {

    $monthNum  = $i;
    $monthName = date('F', mktime(0, 0, 0, $monthNum, 10));

    $date_established = strtotime($monthName);
    $date_established = strtotime('first day of this month', $date_established);
    $date_established = date('Y-m-d', $date_established);
    $date_established_end = date("Y-m-t", strtotime($date_established));

    echo $i .'::'. $monthName . '::' . $date_established . '::' . $date_established_end . '<br>';

}

and so far, it produces :

1::January::2016-01-01::2016-01-31
2::February::2016-02-01::2016-02-28
3::March::2016-03-01::2016-03-31
4::April::2016-04-01::2016-04-30
5::May::2016-05-01::2016-05-31
6::June::2016-06-01::2016-06-30
7::July::2016-07-01::2016-07-31
8::August::2016-08-01::2016-08-31
9::September::2016-09-01::2016-09-30
10::October::2016-10-01::2016-10-31
11::November::2016-11-01::2016-11-30
12::December::2016-12-01::2016-12-31

but today (31 December 2016), all messed up like this :

1::January::2016-01-01::2016-01-31
2::February::2016-03-01::2016-03-31 
3::March::2016-03-01::2016-03-31 << duplicate
4::April::2016-05-01::2016-05-31
5::May::2016-05-01::2016-05-31 << duplicate
6::June::2016-07-01::2016-07-31
7::July::2016-07-01::2016-07-31 << duplicate
8::August::2016-08-01::2016-08-31
9::September::2016-10-01::2016-10-31
10::October::2016-10-01::2016-10-31 << duplicate
11::November::2016-12-01::2016-12-31
12::December::2016-12-01::2016-12-31 << duplicate

what's wrong with 31 December 2016? why my code messed up? thank you.

like image 869
Saint Robson Avatar asked Mar 10 '23 06:03

Saint Robson


2 Answers

Your code fails along these lines:

$february = strtotime('February');
echo date(DATE_ISO8601, $february);

which today, December 30, 2016 outputs:

2016-03-01T00:00:00+0000

Hmmm... why is PHP saying that "February" is "2016-03-01"? Well, it has to do with how PHP fills in the gaps for missing information: in the absence of explicitly given absolute time values, PHP uses the current date and time's values.

Thus, since it's December 30 at 13:29 Eastern, the following are semantically equivalent:

strtotime('February');                       // you asked for
strtotime('February 30, 2016 13:29:47 EST'); // PHP interprets as
//                  ^^^^^^^^^^^^^^^^^^^^^ filled in from current time

Clearly that latter, interpretted date is bogus. PHP (via the underlying library, actually) shifts the interpretted date using the logic that Feb 30 = Feb 29 + 1 day = Mar 1. (Worse, in a non-leap year like 2017, PHP would calculate Feb 30 = Feb 28 + 2 days = Mar 2, and the output would be "2017-03-02T00:00:00+0000"!)

So, what you need to do, under these circumstances, is be explicit when you're asking PHP for date conversion. That means instead of asking for strtotime('February'); you ask for:

$year = date('Y');
$date_established = strtotime("$monthName 1, $year");

(or equivalent. The point being: don't let PHP fill in the missing information with the current date's values.)

Aside: These bugs are particularly hard to track down, because the code "works" when the current day of the month is less than the number of days in the month asked for. In my experience, it's best to avoid strtotime unless you absolutely need, and when you do absolutely need it, you give it as much explicit information as possible.

like image 187
bishop Avatar answered Mar 23 '23 08:03

bishop


Not sure why this is happening, but it's easy enough to work around:

for ($i=1; $i<=12; $i++) {

    $monthNum  = sprintf("%02d", $i);
    $monthName = date('F', mktime(0, 0, 0, $monthNum, 10));

    $date_established = date("Y-$monthNum-01");
    $date_established_end = date("Y-m-t", strtotime($date_established));

    echo "$i :: $monthName :: $date_established :: $date_established_end <br>\n";

}

You always know that the first of the month is going to be "01" so just specify it directly.

Edit: clearly someone else knows why it's happening!

like image 26
miken32 Avatar answered Mar 23 '23 09:03

miken32