skip to Main Content

I am upgrading a codebase from PHP 7.4 to 8.2. My unit tests picked up an oddity in an area of code that modifies a provided date.

It looks like PHP 8.2 introduced some behaviour with how date functions work when you modify a date using "–" (double arithmetic operators).

public static function getCorrectedTime(
        int $year,
        int $month,
        int $day,
        int $index,
        string $period
    ): int {

        // PHP 7.4 - if $index=0, $timestamp=1625443200, if $index=-1, $timestamp=1625529600 - test passes
        // PHP 8.2 - if $index=0, $timestamp=1625443200, if $index=-1, $timestamp=1625356800 - test fails
        $timestamp = strtotime("{$year}-{$month}-{$day} -{$index} {$period}");
        return $timestamp;
    }

This seems to be the case when using DateTime objects and it’s ->modify() method. It seems like it’s more to do with the double "-" modifier that will be used if $index is a negative number. See scratch pad here: https://3v4l.org/fNR2X

But this method needs to support both positive and negative modifiers being sent to it.

Before I completely refactor this old code, I was trying to hunt down an actual description of the change to PHP 8.2 so I can understand exactly what was changed. I can see nothing about this in the changelog or Google hunt. Is anyone aware?

2

Answers


  1. When you add $index = -1*$index; to your code, and remove the - in the expressions before the interval:

    <?php
    $year=2023;
    $month=5;
    $day=28;
    $index=-1;
    $period='day';
    
    $index = -1*$index;
    // strtotime test
    $dateString = $year . "-" . $month . "-" . $day . " " . $index . " " . $period;
    $timestamp1 = strtotime("{$year}-{$month}-{$day} {$index} {$period}");
    
    // DateTime test
    $dateStamp = new DateTime("{$year}-{$month}-{$day}");
    $dateStamp->modify("{$index} {$period}");
    $timestamp2 = $dateStamp->getTimeStamp();
    
    echo $dateString . PHP_EOL;
    echo "strtotime: " . $timestamp1 . PHP_EOL;
    echo "DateTime: " . $timestamp2;
    

    The output is the same in 8.1.2 and 8.2.6

    Login or Signup to reply.
  2. Using sprintf to force the sign for the index would allow you to have a string with either +/- before the days rather than having a — or -…

    The important part is the format string %+d

    // strtotime test
    $dateString = sprintf('%d-%d-%d %+d %s', $year, $month, $day, $index, $period);
    $timestamp1 = strtotime($dateString);
    
    // DateTime test
    $dateString = sprintf('%+d %s', $index, $period);
    $dateStamp = new DateTime("{$year}-{$month}-{$day}");
    $dateStamp->modify($dateString);
    $timestamp2 = $dateStamp->getTimeStamp();
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search