diff --git a/core/oql/expression.class.inc.php b/core/oql/expression.class.inc.php index 089a70e71..c7dd8493f 100644 --- a/core/oql/expression.class.inc.php +++ b/core/oql/expression.class.inc.php @@ -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': diff --git a/test/core/ExpressionEvaluateTest.php b/test/core/ExpressionEvaluateTest.php index fa29676fb..8df6d632a 100644 --- a/test/core/ExpressionEvaluateTest.php +++ b/test/core/ExpressionEvaluateTest.php @@ -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'"); } } } diff --git a/test/phpunit.xml.dist b/test/phpunit.xml.dist index 645b6bedd..fa2d9d132 100644 --- a/test/phpunit.xml.dist +++ b/test/phpunit.xml.dist @@ -5,6 +5,7 @@ bootstrap="unittestautoload.php" backupGlobals="true" colors="true" + columns="120" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" @@ -18,8 +19,10 @@ > - - + + + + diff --git a/test/postbuild_integration.xml.dist b/test/postbuild_integration.xml.dist index 34aab85da..0a3ce6ef0 100644 --- a/test/postbuild_integration.xml.dist +++ b/test/postbuild_integration.xml.dist @@ -17,6 +17,13 @@ verbose="true" > + + + + + + + postbuild_integration