N°5143 Fix FunctionExpression for DATE_FORMAT and formats %j, %k and %l

This commit is contained in:
Pierre Goiffon
2022-05-02 16:57:16 +02:00
parent 603934bac1
commit 84e35e27b8
2 changed files with 94 additions and 23 deletions

View File

@@ -2762,25 +2762,96 @@ class FunctionExpression extends Expression
return $iRet;
case 'DATE_FORMAT':
if (count($this->m_aArgs) != 2)
{
if (count($this->m_aArgs) != 2) {
throw new \Exception("Function {$this->m_sVerb} requires 2 arguments");
}
$oDate = new DateTime($this->m_aArgs[0]->Evaluate($aArgs));
$sFormat = $this->m_aArgs[1]->Evaluate($aArgs);
$sFormat = str_replace(
array('%y', '%x', '%w', '%W', '%v', '%T', '%S', '%r', '%p', '%M', '%l', '%k', '%I', '%h', '%b', '%a', '%D', '%c', '%e', '%Y', '%d', '%m', '%H', '%i', '%s'),
array('y', 'o', 'w', 'l', 'W', 'H:i:s', 's', 'h:i:s A', 'A', 'F', 'g', 'H', 'h', 'h','M', 'D', 'jS', 'n', 'j', 'Y', 'd', 'm', 'H', 'i', 's'),
$sFormat);
if (preg_match('/%j/', $sFormat))
{
$sFormat = str_replace('%j', date_format($oDate, 'z') + 1, $sFormat);
}
if (preg_match('/%[fUuVX]/', $sFormat))
{
$sFormatForMysqlDateFormat = $this->m_aArgs[1]->Evaluate($aArgs);
if (preg_match('/%[fUuVX]/', $sFormatForMysqlDateFormat)) {
throw new NotYetEvaluatedExpression("Expression ".$this->RenderExpression().' cannot be evaluated (known limitation)');
}
$sRet = date_format($oDate, $sFormat);
if (preg_match('/%j/', $sFormatForMysqlDateFormat)) {
$sFormatForMysqlDateFormat = str_replace('%j', 'z', $sFormatForMysqlDateFormat);
$sRet = date_format($oDate, $sFormatForMysqlDateFormat);
$sRet++;
/** @noinspection PhpUnnecessaryLocalVariableInspection */
$sRet = str_pad($sRet, 3, '0', STR_PAD_LEFT);
return $sRet;
}
/**
* @var string[] $aFormatsForMysqlDateFormat
* @link https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-format
*/
//@formatter:off we want to keep every single item on its own line to ease comp between MySQL and PHP formats !
$aFormatsForMysqlDateFormat = [
'%y',
'%x',
'%w',
'%W',
'%v',
'%T',
'%S',
'%r',
'%p',
'%M',
'%l',
'%k',
'%I',
'%h',
'%b',
'%a',
'%D',
'%c',
'%e',
'%Y',
'%d',
'%m',
'%H',
'%i',
'%s'
];
//@formatter:on
/**
* @var string[] $aFormatsForPhpDateFormat
* @link https://www.php.net/manual/en/datetime.format.php
*/
//@formatter:off we want to keep every single item on its own line to ease comp between MySQL and PHP formats !
$aFormatsForPhpDateFormat = [
'y',
'o',
'w',
'l',
'W',
'H:i:s',
's',
'h:i:s A',
'A',
'F',
'g',
'G',
'h',
'h',
'M',
'D',
'jS',
'n',
'j',
'Y',
'd',
'm',
'H',
'i',
's'
];
//@formatter:on
$sFormatForPhpDateFormat = str_replace($aFormatsForMysqlDateFormat, $aFormatsForPhpDateFormat, $sFormatForMysqlDateFormat);
/** @noinspection PhpUnnecessaryLocalVariableInspection */
$sRet = date_format($oDate, $sFormatForPhpDateFormat);
return $sRet;
case 'TO_DAYS':

View File

@@ -455,7 +455,11 @@ class ExpressionEvaluateTest extends iTopDataTestCase
}
/**
* Systematically check all supported format specs, for a given date
* For a given date,
* for all different formats (1st array element returned by {@see static::TimeFormatsProvider}),
* compare value returned by :
* * DATE_FORMAT() SQL function,
* * FunctionExpression('DATE_FORMAT', ...) result
*
* @covers FunctionExpression::Evaluate()
* @dataProvider EveryTimeFormatProvider
@@ -481,7 +485,8 @@ class ExpressionEvaluateTest extends iTopDataTestCase
}
$sSelects = "SELECT ".implode(', ', $aSelects);
$aRes = CMDBSource::QueryToArray($sSelects);
$aRow = $aRes[0];
/** @var array $aMysqlDateFormatRsultsForAllFormats format as key, MySQL evaluated result as value */
$aMysqlDateFormatRsultsForAllFormats = $aRes[0];
foreach ($aFormats as $sFormatDesc => $aFormatSpec)
{
$sFormat = $aFormatSpec[0];
@@ -489,13 +494,8 @@ class ExpressionEvaluateTest extends iTopDataTestCase
if ($bProcessed)
{
$oExpression = new FunctionExpression('DATE_FORMAT', array(new ScalarExpression($sDate), new ScalarExpression("%$sFormat")));
$res = $oExpression->Evaluate(array());
if (is_numeric($res)) {
// N°3091 after PHPUnit upgrade from 6 to 8.5 some errors were thrown here
// example : assertEquals was returning false for expected=8 and actual=08
$res = (float) $res;
}
static::assertEquals($aRow[$sFormat], $res, "Format %$sFormat not matching MySQL for '$sDate'");
$itopExpressionResult = $oExpression->Evaluate(array());
static::assertSame($aMysqlDateFormatRsultsForAllFormats[$sFormat], $itopExpressionResult, "Format %$sFormat not matching MySQL for '$sDate'");
}
}
}