从日期中减去一个月的方法

3

看起来如果您需要可靠和准确的日期操作,就不能使用 strotime。例如,如果某个月有31天,则似乎 strtotime 只是减去了30天,而不是整个一个月。

因此,例如,如果 $event["EndDate"] 等于 "2013-10-31 00:00:01",则以下代码:

echo date("Y/n/j", strtotime('-1 month', strtotime($event["EndDate"]));

输出:2013/09/30的结果应是2013/10/1

问题:现在我知道了如何不做,那么有没有另一种更准确的方法使PHP准确地减去(或加上)整个月份,而不仅仅是30天?


5
如果你正在处理带有日期和时间的事件,那么为自己简化生活,使用DateTime对象会更加方便。 - Mark Baker
2
我认为真正的问题在于它从10月31日减去一个月,返回的是不存在的9月31日,因此被转换成10月1日。如果我使用你的代码输入10月29日,那么预期会得到9月29日。 - Blazemonger
https://dev59.com/n1_Va4cB1Zd3GeqPX-f5 - Lawson
2
由于2013/09/31并不存在,请问您希望返回什么结果。显然,9月31日不可能是任何语言中的日期/时间函数的可接受结果。在这种情况下,我认为你无法争辩说9月30日比10月1日更(不)正确。重要的是行为一致和可预测,我相信它是这样的,尽管我还没有测试过,但我可能会在有时间时进行测试,因为这激起了我的好奇心。 - vascowhite
我运行了那些测试,请看下面的答案。 - vascowhite
显示剩余8条评论
2个回答

3
主要问题在于 2013/09/31 不存在,更好的方法是使用上个月的 第一天最后一天
$date = new DateTime("2013-10-31 00:00:01");
$date->modify("last day last month");
echo $date->format("Y/n/j"); // 2013/9/30

当日期为 2013-10-15

$date = new DateTime("2013-10-15 00:00:01");
$day = $date->format("d");
$year = $date->format("Y");

$date->modify("last day last month");
$month = $date->format("m");

if (checkdate($month, $day, $year)) {
    $date->setDate($year, $month, $day);
}

echo $date->format("Y/n/j"); // 2013-9-15

如果日期是2013年10月15日,那就不好了 - 但我现在看到这个问题了。该死! - Chuck Le Butt
DateTime非常灵活..我会更新我的代码以展示如何实现这一点... - Baba

1
你说:-

如果您需要可靠和准确的日期操作,似乎无法使用strotime

你可以使用,只需了解它的行为方式。您的问题促使我运行了几个测试。

在减去一个月时,PHP并不仅仅是从日期中减去30天,尽管从你所看到的单个案例来看似乎是这样。实际上,我的测试here表明,在这种情况下,它会将31天添加到上个月的开始(3月3日的结果让我这样认为)。

$thirtyOnedayMonths = array(
    1, 3, 5, 7, 8, 10, 12
);

$oneMonth = new \DateInterval('P1M');
$format = 'Y-m-d';

foreach($thirtyOnedayMonths as $month){
    $date = new \DateTime("2013-{$month}-31 00:00:01");
    var_dump($date->format($format));
    $date->sub($oneMonth);
    var_dump($date->format($format));
}

2013年11月12日和2013年10月12日之间相隔31天,PHP正确计算了月份减法,可以在这里看到。

$date = new \DateTime('2013-11-12 00:00:01');
var_dump($date);
$interval = new DateInterval('P1M');
$date->sub($interval);
var_dump($date);

在您的情况下,2013-10-31 - 1个月是2013-09-31,而这个日期并不存在。任何日期/时间函数都需要返回一个有效的日期,而2013-09-31不是有效日期。
在这种情况下,PHP似乎会在上个月的开始日期加上31天来得到一个有效日期。
一旦您知道了预期的行为,就可以相应地编写程序。如果当前的行为与您的用例不符,则可以扩展DateTime类以提供适合您的行为。
我假设strtotime和DateTime :: sub()的行为相同,但this test表明它们确实如此。
$thirtyOnedayMonths = array(
    1, 3, 5, 7, 8, 10, 12
);

$format = 'Y-m-d';

foreach($thirtyOnedayMonths as $month){
    $date = date($format, strtotime('-1 month', strtotime("2013-{$month}-31 00:00:01")));
    var_dump($date);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接