0

I'm using PHP's DateTime object to manipulate dates. I found a strange case where the result date seems wrong:

$dt = new DateTime('2020-02-29 23:59:59');
$dt->modify('-1 year');
echo $dt->format('Y-m-d H:i:s');

This code gives 2019-03-01 23:59:59 instead of 2019-02-28 23:59:59. I believe it is due to 2020 having a Feb. 29th yet I have no clue how to fix this issue.


Note that the modifier can be anything, not just -1 year. A solution that would isolate the year number minus one wouldn't fit my needs.

pistou
  • 2,799
  • 5
  • 33
  • 60
  • 1
    What exactly makes you think that this is the _wrong_ date? 1 year normally equals 365 days. If you do `$dt->modify('-365 days');`, you'll get exactly what you're getting now. If your application has different needs, you have to compensate on your end. – El_Vanja Nov 19 '20 at 09:35
  • 4
    Vague statements like "-1 year" really have no definitive answer, and you can't really expect PHP to magically get to the result you *mean*. If you want "the end of the same month last year", that's a calculation you'd have to explicitly do manually. – deceze Nov 19 '20 at 09:35
  • Related/duplicate? https://stackoverflow.com/questions/1686724/how-to-find-the-last-day-of-the-month-from-date – Sumurai8 Nov 19 '20 at 09:44
  • I don't agree with you: `new DateTime('2020-02-01)->modify('+1 month');` gives "2020-03-01" (actually equals to +29days), not "2020-02-01 + 30 days" or "2020-02-01 + 365/12" (or even translate days in seconds..). One month is one month; PHP *knows* if it's 28, 29, 30 or 31 days long. It just doesn't understand leap year transitions. – pistou Nov 19 '20 at 09:46
  • 3
    In my mind, PHP gives you the correct date. Both those days are the day after feb 28. If it gave you feb 28, it would be "-1 year and a day". – M. Eriksson Nov 19 '20 at 09:47
  • 1
    @pistou And yet... `$dt = new DateTime('2020-03-31 23:59:59'); $dt->modify('-1 month');` results in `2020-03-02 23:59:59`. DateTime in php is notoriously finicky, and you are much better figuring out steps that guarantee the correct day to be selected instead of finding the perfect modify string to use in `->modify(..)`. A year or month does not always refer to the same amount of time, and it will usually fail in weird and interesting ways. – Sumurai8 Nov 19 '20 at 09:52
  • 1
    In that example, it selects `2020-02-31`, which it normalises to `2020-03-02`. You would be better off in that case to select the first day of the month, modify it, then select the last day of the month. But without a proper description of the larger goal you try to achieve it is impossible for me to answer the question (or find a suitable duplicate). – Sumurai8 Nov 19 '20 at 09:54
  • 3
    It really boils down to correctly defining the needs of your app. If what you need is "the end of the same month last year" as deceze suggested, then make your app do that, not something that is not a certain equivalent. The PHP folks obviously had some choices to make when implementing those and, while you might not agree with them, you have to work with them the way they are. – El_Vanja Nov 19 '20 at 09:55
  • What I acually wanted in provided "same *period* of the year before" – pistou Nov 19 '20 at 09:58
  • 1
    FYI: https://stackoverflow.com/a/3602421/10468523 – Steven Chen Nov 19 '20 at 09:59

1 Answers1

2

I figured out myself a work around that seems to work.

I noted that things work well with first day of the month:

$dt = new DateTime('2020-02-01 00:00:01');
$dt->modify('-1 year');
echo $dt->format('Y-m-d H:i:s'); // 2019-02-01 00:00:01

From this statement, I just added a few steps when I need to manipulate "end of the month" dates:

$dt = new DateTime('2020-02-29 23:59:59');
$dt->modify('first day of this month');
$dt->modify('-1 year');
$dt->modify('last day of this month');
echo $dt->format('Y-m-d H:i:s'); // 2020-02-28 23:59:59
pistou
  • 2,799
  • 5
  • 33
  • 60
  • 1
    This is brilliant in its simplicity. It accounts for months of different lengths and even leap years. Well done. – Vincent Dec 24 '21 at 20:02