Home > OS >  Laravel comparing Carbon with string date working? is it newly supported?
Laravel comparing Carbon with string date working? is it newly supported?

Time:07-23

I have the following code working in Laravel 8 (which uses Carbon by default) with PHP7.

$carbon = new Carbon();
$test1 = 1;
$test2 = 1;
if ($carbon > '1970-01-01 00:00:00') {
    $test1 = 2;
}
if ($carbon > '2030-01-01 00:00:00') {
    $test2 = 2;
}
# ends with $test1===2 and $test2===1

The problem is that when I searched this usage I found many posts indicating they had to something much more complicated.

Comparing a date with Carbon

https://laracasts.com/discuss/channels/general-discussion/how-to-compare-a-string-with-carbon-date-if-more-than-or-less-than

However all those posts are about 3-4 years old. Is this a newly supported feature of Carbon or my working code was just pure luck and there are cases not working?

EDIT: I found in the greaterThan function implementation that even they are using raw string in the example for their compare-target date. This seems so weird considering all the content of other online posts.

    /**
     * Determines if the instance is greater (after) than another
     *
     * @example
     * ```
     * Carbon::parse('2018-07-25 12:45:16')->greaterThan('2018-07-25 12:45:15'); // true
     * Carbon::parse('2018-07-25 12:45:16')->greaterThan('2018-07-25 12:45:16'); // false
     * Carbon::parse('2018-07-25 12:45:16')->greaterThan('2018-07-25 12:45:17'); // false
     * ```
     *
     * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
     *
     * @return bool
     */
    public function greaterThan($date): bool
    {
        return $this > $date;
    }

CodePudding user response:

It's not new, and it's not even a Carbon feature, any class having a __toString method can be compared to an other string.

And when you compare strings with <, <=, > or >=, PHP just sorts them alphabetically (alphabet here is actually the order of the characters in the ASCII table).

So here is what actually happens:

class Foo
{
  public function __construct($input)
  {
    $this->input = $input;
  }
  public function __toString()
  {
    return $this->input;
  }
}

var_dump(new Foo('A') < 'B'); // true
var_dump(new Foo('0') < '1'); // true
var_dump(new Foo('C') < 'B'); // false
var_dump(new Foo('a') < 'B'); // false, lower case = higher ASCII rank
var_dump(new Foo('2-0') < '1-9'); // false

As you see with last example, digits in strings also are characters in the ASCII table and there order in the table matches the numerical order (0 to 9).

And by default when you convert a Carbon object to string (explicitly or implicitly like here) it returns an ISO-8601 string.

This format is handy as until year 9999, dates will all have the same length and the same number of digits for each component, so sorting strings will match the intent of sorting dates.

If you are 100% you won't customize the __toString method of the Carbon object and 100% sure you always compare to a ISO-8601 string, comparing date with string like you do is fine, and I would even say, it's the cleanest way.

But as you see it implies some prerequisites.

Last thing: you can also compare 2 Carbon object:

var_dump(new Carbon('2030-01-01 00:00:00') > new Carbon('1970-01-01 00:00:00')); // true

This actually relies on a other feature: PHP provides native comparison of DateTime objects and Carbon extends DateTime

When the prerequisites above are not certain or if you want to be more explicit, I recommend you actually parse the string and compare objects to rely on this native PHP DateTime comparison.

Note that what you found in the doc of greaterThan recommend comparing Carbon <> Carbon, not Carbon <> string

Because: comparing different types is always at the risk that you/the next developer don't fully control/understand the implicit cast happening.

  • Related