It seems that you can\'t use strotime
if you need reliable and accurate date manipulation. For example, if the month has 31 days, then it appears that str
The main issue is that 2013/09/31
does not exist so a better approach would be to use first day
or last day
of the previous month.
$date = new DateTime("2013-10-31 00:00:01");
$date->modify("last day last month");
echo $date->format("Y/n/j"); // 2013/9/30
When date is 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
You say:-
It seems that you can't use strotime if you need reliable and accurate date manipulation
You can, you just need to know how it behaves. Your question prompted me to run a couple of tests.
PHP does not merely subtract 30 days from the date when subtracting a month, although it appears that it does from the single case you are looking at. In fact my test here suggests that it adds 31 days to the start of the previous month (the result of 3rd March suggests this to me) in this case.
$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));
}
There are 31 days between 2013-11-12 and 2013-10-12 and PHP calculates the month subtraction correctly as can be seen here.
$date = new \DateTime('2013-11-12 00:00:01');
var_dump($date);
$interval = new DateInterval('P1M');
$date->sub($interval);
var_dump($date);
In your particular case 2013-10-31 - 1 month is 2013-09-31 which does not exist. Any date/time function needs to return a valid date, which 2013-09-31 is not.
In this case PHP, as I stated above, seems to add 31 days to the start of the previous month to arrive at a valid date.
Once you know the expected behaviour, you can program accordingly. If the current behaviour does not fit your use case then you could extend the DateTime class to provide behaviour that works for you.
I have assumed that strtotime
and DateTime::sub()
behave the same, this test suggests they do.
$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);
}