背景:

strtotime 是常用的一个函数,在计算增减时间的时候很方便, 比如 strotime("+1 day"),就能得到一个一天后的时间戳。 尽管官方手册不建议用来做运算(+1 day), 但是懒是天性, 能用一行代码解决的就不想再用其他办法, 于是就踩坑了。

测试代码:

php -r "var_dump(date('Y-m-d H:i:s', strtotime('+1 month', strtotime('2021-01-30'))));"

在2021-01-30 的基础上加一个月, 初始写代码的结果是期望得到 2021-02-28 号,然后进行后续逻辑。实际结果如下:
2022-04-01T06:51:34.png

变成了3月2号!!, 完全不是预期的结果!。 但是要是基准日期是7月份,结果又特么对了。 这还只是测试代码, 如果业务中是包装了好几层, 比如每月月底计算下月的数据,就有可能有的月份数据正常, 有的月份数据异常。

原因:
查了下相关资料, 这个是正确结果!!!

strtotime 执行增减日期的步骤:

1:先增减日期 ,2021-01-30 先+1 month 变成 2021-02-30, 2021-07-30 先+1 month 变成 2021-08-30

2:然后再日期规范化, 2021-02-30 不是合法日期, 就规范为 2021-03-02 (要是闰年2020 就是 2020-03-01), 2021-08-30 合法日期 不需要规范。

然后就得出结果。

解决方式:

传说中的优雅 (“first day of ”/"last day of")修正短语。
2022-04-01T06:53:00.png

如果想要循环获取接下来的每个月份,可以使用以下方法:

$date = '2021-01-31 12:00:01';
for ($i = 1; $i <= 24; $i ++)
{
    var_dump(getNextDate($date, $i));
}
function getNextDate($start_time, $month)
{
    $timestamp = strtotime($start_time);
    $next_timestamp = strtotime('+' . $month . ' month', $timestamp);

    $current_month = date('n', $timestamp);
    $should_month = bcmod(bcadd($current_month, $month), 12) ?: 12;
    $calculate_month = date('n', $next_timestamp);

    if ($should_month != $calculate_month) {
        $next_timestamp = strtotime($start_time  . 'last day of +' . $month . ' month');
    }

    return date('Y-m-d 00:00:00', $next_timestamp);
}

结果如下:
2022-04-01T06:56:06.png

标签: none

添加新评论