Search code examples
phpdatedatetimephp-carbon

Carbon and PHP DateTime parse single letters as dates, but not multi-character strings


While using the nesbot/carbon library I've noticed that it accepts single letters as valid dates and parses them in a way that maps to a relative time range. Specifically, single letters are parsed (case-insensitively) into time offsets, ranging from -12 hours to +12 hours relative to the current time. However, it does not accept single digits as strings or more than one letter in the string.

You can test it here: https://play.phpsandbox.io/embed/nesbot/carbon

<?php
    echo "a: ";
    echo Carbon::parse("a")->getTimestamp(); // now -1 hour
    echo "\r\n b: ";
    echo Carbon::parse("b")->getTimestamp(); // now -2 hours
    echo "\r\n c: ";
    echo Carbon::parse("c")->getTimestamp(); // now -3 hours
    echo "\r\n C: ";
    echo Carbon::parse("C")->getTimestamp(); // now -3 hours (case-insensitive)
    echo "\r\n d: ";
    echo Carbon::parse("d")->getTimestamp(); // now -4 hours
    echo "\r\n m: ";
    echo Carbon::parse("m")->getTimestamp(); // now -12 hours
    echo "\r\n n: ";
    echo Carbon::parse("n")->getTimestamp(); // now +1 hour
    echo "\r\n x: ";
    echo Carbon::parse("x")->getTimestamp(); // now +11 hours
    echo "\r\n Y: ";
    echo Carbon::parse("Y")->getTimestamp(); // now +12 hours
    echo "\r\n z: ";
    echo Carbon::parse("z")->getTimestamp(); // now
    echo "\r\n 1: ";
    echo Carbon::parse("1")->getTimestamp(); // error
    echo "\r\n aa: ";
    echo Carbon::parse("aa")->getTimestamp(); // error

Error message: Carbon\Exceptions\InvalidFormatException with message 'Could not parse 'aa': Failed to parse time string (aa) at position 0 (a): The timezone could not be found in the database'

I also checked how the standard php DateTime object reacts and it always returns UTC now for a single letter but throws an error when trying to parse more than a single letter.

You can test it here: https://onlinephp.io/

<?php
$date = new DateTime('a'); // UTC now
echo $date->format('Y-m-d H:i:s');
echo "\n";
$date = new DateTime('b'); // UTC now
echo $date->format('Y-m-d H:i:s');
echo "\n";
$date = new DateTime('B'); // UTC now
echo $date->format('Y-m-d H:i:s');
echo "\n";
$date = new DateTime('m'); // UTC now
echo $date->format('Y-m-d H:i:s');
echo "\n";
$date = new DateTime('n'); // UTC now
echo $date->format('Y-m-d H:i:s');
echo "\n";
$date = new DateTime('z'); // UTC now
echo $date->format('Y-m-d H:i:s');
echo "\n";
$date = new DateTime('aa'); // error
echo $date->format('Y-m-d H:i:s');
echo "\n";

Error message: Fatal error: Uncaught Exception: Failed to parse time string (aa) at position 0 (a): The timezone could not be found in the database in /home/user/scripts/code.php:21

This behavior is a bit unexpected, as one might assume parsing single letters would throw an error. Is it a bug? or is it common for datetime libraries to work like that?


Solution

  • No, It's not bug.

    Characters we get Military Time Zones

    Military time zones use single-letter identifiers to represent time zones relative to Coordinated Universal Time (UTC).

    • 'A' to 'M' (excluding 'J'): UTC+1 to UTC+12
    • 'N' to 'Y': UTC−1 to UTC−12
    • 'Z': UTC+0

    Military time zone


    When you pass multi-character strings like 'aa' or digits like '1', Carbon interprets them as time zone identifiers. Since these are not valid time zones, it throws an InvalidFormatException