2016-12-30 70 views
4

我有这样的代码:为什么此日期功能只能在2016年12月30日前运行?

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

FWIW,2016既是闰年,也是闰秒的一年。 – bishop

+1

请注意,所有错误的日期都在少于31天的月份。 11月31日实际上是12月1日。 – Barmar

+0

我不能再现你的错误,除非我在'$ monthName'赋值中将'10'改为'31'。 – Barmar

回答

6

你代码沿着这些路线失败:

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

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

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

嗯......为什么PHP说, “二月” 是“ 2016年3月1" 日?那么,这与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 

清楚后,interpretted日期是伪造的。 PHP(实际上是通过底层库)使用2月30日= 2月29日+ 1日= 3月1日的逻辑来转换解释日期。(更糟糕的是,在2017年的非闰年中,PHP会计算2月30日= 2月28日+ 2天= 3月2日,输出将为“2017-03-02T00:00:00 + 0000”!)

因此,在这些情况下,您需要做的事情是明确的用于日期转换的PHP。这意味着,而不是要求strtotime('February');你问:(或等效问题的关键是:不要让PHP中填入当前日期的价值观缺失的信息。)

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

旁白:这些错误是特别难以追查的,因为当该月的当天日期小于所要求月份中的天数时,代码“有效”。根据我的经验,最好避免strtotime,除非你绝对需要,而当你确实需要它时,你尽可能多地给它提供明确的信息。

+2

你打败了我。也许更好的选择是使用'mktime()'来创建'$ date_established',而不是'strtotime()'。 – Barmar

+1

@bishop:谢谢你的回答,兄弟。现在我真的明白是什么问题了。你救我,兄弟。谢谢。 –

+0

@RobertHanson不客气!这些都是烦人的错误。 (和往常一样,绿色复选标记表示赞赏。:) – bishop

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

} 

你总是知道在第一个月的将是“01”,所以干脆直接指定。

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

相关问题