为什么此日期功能仅在2016年12月30日之前有效?

时间:2016-12-30 18:15:50

标签: php

我有这段代码:

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>';

}

到目前为止,它产生了:

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

但是今天(2016年12月31日),所有这些都搞砸了:

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

2016年12月31日的错误是什么?为什么我的代码搞砸了?谢谢。

2 个答案:

答案 0 :(得分:6)

您的代码失败了:

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

今天,2016年12月30日输出:

2016-03-01T00:00:00+0000
嗯......为什么PHP会这样说&#34;二月&#34;是&#34; 2016-03-01&#34;?好吧,它与PHP如何填补缺失信息的空白有关:在没有明确给定绝对时间值的情况下,PHP使用当前日期和时间的值。

因此,由于它在12月30日东部时间13:29,以下在语义上是等价的:

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

显然,后者,解释日期是假的。 PHP(通过底层库,实际上)使用2月30日= 2月29日+ 1天= 3月1日的逻辑来转换解释日期。(更糟糕的是,在像2017年的非闰年,PHP将计算2月30日= 2月28日+ 2天= 3月2日,输出将是&#34; 2017-03-02T00:00:00 + 0000&#34;!)

因此,在您要求PHP进行日期转换时,在这些情况下,您需要做的是明确的。这意味着您要求strtotime('February');而不是要求:{/ p>

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

(或同等的。重点是:不要让PHP用当前日期的值填写缺失的信息。)

除此之外:这些错误特别难以追踪,因为代码&#34;工作&#34;当当前月份的日期小于要求的月份的天数时。根据我的经验,除非你绝对需要,否则最好避免strtotime,当你绝对需要它时,你尽可能多地提供明确的信息。

答案 1 :(得分:1)

不确定为什么会这样,但它很容易解决:

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";

}

你总是知道这个月的第一天将是&#34; 01&#34;所以直接指定它。

编辑:显然别人知道它为什么会发生!