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
‘aa’ value is not a pseudo constant default value built-in to parser datetime class stuff
Those letters are interpreted as timezone.
Your comparison between
DateTime
andCarbon
is not accurate as usinggetTimestamp
(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:
I see matching results for
DateTime
andCarbon
: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.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
toUTC+12
N
‘ to ‘Y
‘:UTC−1
toUTC−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 anInvalidFormatException