skip to Main Content

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 "rn b: ";
    echo Carbon::parse("b")->getTimestamp(); // now -2 hours
    echo "rn c: ";
    echo Carbon::parse("c")->getTimestamp(); // now -3 hours
    echo "rn C: ";
    echo Carbon::parse("C")->getTimestamp(); // now -3 hours (case-insensitive)
    echo "rn d: ";
    echo Carbon::parse("d")->getTimestamp(); // now -4 hours
    echo "rn m: ";
    echo Carbon::parse("m")->getTimestamp(); // now -12 hours
    echo "rn n: ";
    echo Carbon::parse("n")->getTimestamp(); // now +1 hour
    echo "rn x: ";
    echo Carbon::parse("x")->getTimestamp(); // now +11 hours
    echo "rn Y: ";
    echo Carbon::parse("Y")->getTimestamp(); // now +12 hours
    echo "rn z: ";
    echo Carbon::parse("z")->getTimestamp(); // now
    echo "rn 1: ";
    echo Carbon::parse("1")->getTimestamp(); // error
    echo "rn aa: ";
    echo Carbon::parse("aa")->getTimestamp(); // error

Error message: CarbonExceptionsInvalidFormatException 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?

3

Answers


  1. ‘aa’ value is not a pseudo constant default value built-in to parser datetime class stuff

    Login or Signup to reply.
  2. Those letters are interpreted as timezone.

    Your comparison between DateTime and Carbon is not accurate as using getTimestamp (which is a UTC value) vs. a format not having any timezone and you make the assertion that the date-string is UTC (as per your code comment), but it’s actually wrong.

    Using this snippet:

    $strings = [
        'a',
        'b',
        'B',
        'm',
        'n',
        'z',
        '1',
        'aa',
    ];
    
    foreach ($strings as $string) {
        echo "$string:n";
        try {
            $date = new DateTime($string);
            echo $date->format('Y-m-d H:i:s e') . "n";
        } catch (Throwable $e) {
            echo "Error: " . $e->getMessage() . "n";
        }
        try {
            $date = Carbon::parse($string);
            echo $date->format('Y-m-d H:i:s e') . "n";
        } catch (Throwable $e) {
            echo "Error: " . $e->getMessage() . "n";
        }
        echo "n";
    }
    

    I see matching results for DateTime and Carbon:

    a:
    2024-09-20 11:26:15 A
    2024-09-20 11:26:15 A
    
    b:
    2024-09-20 11:26:15 B
    2024-09-20 11:26:15 B
    
    B:
    2024-09-20 11:26:15 B
    2024-09-20 11:26:15 B
    
    m:
    2024-09-20 11:26:15 M
    2024-09-20 11:26:15 M
    
    n:
    2024-09-20 11:26:15 N
    2024-09-20 11:26:15 N
    
    z:
    2024-09-20 11:26:15 Z
    2024-09-20 11:26:15 Z
    
    1:
    Error: Failed to parse time string (1) at position 0 (1): Unexpected character
    Error: Could not parse '1': Failed to parse time string (1) at position 0 (1): Unexpected character
    
    aa:
    Error: Failed to parse time string (aa) at position 0 (a): The timezone could not be found in the database
    Error: Could not parse 'aa': Failed to parse time string (aa) at position 0 (a): The timezone could not be found in the database
    

    Either it’s relevant or not is a question for PHP team (Carbon just inherit the behavior here), and either it’s right or wrong, it’s unlikely to change.

    I would suggest to have validation ahead in your code to be a bit more strict about accepted values. It’s generally wiser as the string for DateTime constructor is quite permissive.

    Login or Signup to reply.
  3. 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

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search