From 7090d9b67735ba10ca3d2e024cb376b991e26366 Mon Sep 17 00:00:00 2001 From: Daniel Maier <dani-maier@gmx.de> Date: Sat, 5 Mar 2016 19:57:21 +0100 Subject: [PATCH] [BUGFIX] Fix date conversion of neg timestamps Date conversion of TCA fields with eval "date" or "datetime" is now also handled correctly for dates before 1970, thus having a negative timestamp. Timezone offset is now also applied for those negative timestamps, in order to prevent erroneous data for dates before 1970. Resolves: #73871 Releases: master, 7.6 Change-Id: I4de9911dec3de720992da137fe8afcb3ecbfdad7 Reviewed-on: https://review.typo3.org/47115 Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch> Tested-by: Christian Kuhn <lolli@schwarzbu.ch> Reviewed-by: Andreas Wolf <andreas.wolf@typo3.org> Tested-by: Andreas Wolf <andreas.wolf@typo3.org> --- .../Classes/Form/Element/InputTextElement.php | 2 +- .../Form/Element/InputTextElementTest.php | 109 ++++++++++++++++++ .../core/Classes/DataHandling/DataHandler.php | 2 +- .../Unit/DataHandling/DataHandlerTest.php | 38 ++++++ 4 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 typo3/sysext/backend/Tests/Unit/Form/Element/InputTextElementTest.php diff --git a/typo3/sysext/backend/Classes/Form/Element/InputTextElement.php b/typo3/sysext/backend/Classes/Form/Element/InputTextElement.php index 65a16c4c115e..3744941141e0 100644 --- a/typo3/sysext/backend/Classes/Form/Element/InputTextElement.php +++ b/typo3/sysext/backend/Classes/Form/Element/InputTextElement.php @@ -79,7 +79,7 @@ class InputTextElement extends AbstractFormElement } elseif (in_array('date', $evalList)) { $attributes['data-date-type'] = 'date'; } - if ($parameterArray['itemFormElValue'] > 0) { + if (((int)$parameterArray['itemFormElValue']) !== 0) { $parameterArray['itemFormElValue'] += date('Z', $parameterArray['itemFormElValue']); } if (isset($config['range']['lower'])) { diff --git a/typo3/sysext/backend/Tests/Unit/Form/Element/InputTextElementTest.php b/typo3/sysext/backend/Tests/Unit/Form/Element/InputTextElementTest.php new file mode 100644 index 000000000000..f144095c1947 --- /dev/null +++ b/typo3/sysext/backend/Tests/Unit/Form/Element/InputTextElementTest.php @@ -0,0 +1,109 @@ +<?php +namespace typo3\sysext\backend\Tests\Unit\Form\Element; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +use TYPO3\CMS\Backend\Form\Element\InputTextElement; +use TYPO3\CMS\Backend\Form\NodeFactory; +use TYPO3\CMS\Core\Tests\UnitTestCase; + +/** + * Test case + */ +class InputTextElementTest extends UnitTestCase +{ + /** + * @var string Selected timezone backup + */ + protected $timezoneBackup = ''; + + /** + * We're fiddling with hard timestamps in the tests, but time methods in + * the system under test do use timezone settings. Therefore we backup the + * current timezone setting, set it to UTC explicitly and reconstitute it + * again in tearDown() + */ + protected function setUp() + { + $this->timezoneBackup = date_default_timezone_get(); + } + + /** + * Tear down + */ + protected function tearDown() + { + date_default_timezone_set($this->timezoneBackup); + parent::tearDown(); + } + + + /** + * Data provider for renderAppliesCorrectTimestampConversion + * + * @return array + */ + public function renderAppliesCorrectTimestampConversionDataProvider() + { + // Three elements: input (UTC), timezone of output, expected output + return [ + // German standard time (without DST) is one hour ahead of UTC + 'date in 2016 in German timezone' => [ + 1457103519, 'Europe/Berlin', 1457103519 + 3600 + ], + 'date in 1969 in German timezone' => [ + -7200, 'Europe/Berlin', -3600 + ], + // Los Angeles is 8 hours behind UTC + 'date in 2016 in Los Angeles timezone' => [ + 1457103519, 'America/Los_Angeles', 1457103519 - 28800 + ], + 'date in UTC' => [ + 1457103519, 'UTC', 1457103519 + ] + ]; + } + + /** + * @test + * @dataProvider renderAppliesCorrectTimestampConversionDataProvider + * @param int $input + * @param string $serverTimezone + * @param int $expectedOutput + */ + public function renderAppliesCorrectTimestampConversion($input, $serverTimezone, $expectedOutput) + { + date_default_timezone_set($serverTimezone); + $data = [ + 'parameterArray' => [ + 'tableName' => 'table_foo', + 'fieldName' => 'field_bar', + 'fieldConf' => [ + 'config' => [ + 'type' => 'input', + 'dbType' => 'datetime', + 'eval' => 'datetime', + 'default' => '0000-00-00 00:00:00' + ] + ], + 'itemFormElValue' => $input + ] + ]; + /** @var NodeFactory $nodeFactoryProphecy */ + $nodeFactoryProphecy = $this->prophesize(NodeFactory::class)->reveal(); + $subject = new InputTextElement($nodeFactoryProphecy, $data); + $result = $subject->render(); + $this->assertContains('<input type="hidden" name="" value="' . $expectedOutput . '" />', $result['html']); + } +} diff --git a/typo3/sysext/core/Classes/DataHandling/DataHandler.php b/typo3/sysext/core/Classes/DataHandling/DataHandler.php index f6f1b0fefd18..3ec0c8ad4973 100644 --- a/typo3/sysext/core/Classes/DataHandling/DataHandler.php +++ b/typo3/sysext/core/Classes/DataHandling/DataHandler.php @@ -2663,7 +2663,7 @@ class DataHandler case 'date': case 'datetime': $value = (int)$value; - if ($value > 0 && !$this->dontProcessTransformations) { + if ($value !== 0 && !$this->dontProcessTransformations) { $value -= date('Z', $value); } break; diff --git a/typo3/sysext/core/Tests/Unit/DataHandling/DataHandlerTest.php b/typo3/sysext/core/Tests/Unit/DataHandling/DataHandlerTest.php index 725ad8a8c0a7..b7ce5ca2483b 100644 --- a/typo3/sysext/core/Tests/Unit/DataHandling/DataHandlerTest.php +++ b/typo3/sysext/core/Tests/Unit/DataHandling/DataHandlerTest.php @@ -167,6 +167,44 @@ class DataHandlerTest extends \TYPO3\CMS\Core\Tests\UnitTestCase } } + public function dataProviderDatetime() + { + // Three elements: input, timezone of input, expected output (UTC) + return [ + // German standard time (without DST) is one hour ahead of UTC + 'date in 2016 in German timezone' => [ + 1457103519, 'Europe/Berlin', 1457103519 - 3600 + ], + 'date in 1969 in German timezone' => [ + -7200, 'Europe/Berlin', -10800 + ], + // Los Angeles is 8 hours behind UTC + 'date in 2016 in Los Angeles timezone' => [ + 1457103519, 'America/Los_Angeles', 1457103519 + 28800 + ], + 'date in UTC' => [ + 1457103519, 'UTC', 1457103519 + ] + ]; + } + + /** + * @test + * @dataProvider dataProviderDatetime + */ + public function evalCheckValueDatetime($input, $serverTimezone, $expectedOutput) + { + $oldTimezone = date_default_timezone_get(); + date_default_timezone_set($serverTimezone); + + $output = $this->subject->checkValue_input_Eval($input, ['datetime'], ''); + + // set before the assertion is performed, so it is restored even for failing tests + date_default_timezone_set($oldTimezone); + + $this->assertEquals($expectedOutput, $output['value']); + } + /** * Data provider for inputValueCheckRecognizesStringValuesAsIntegerValuesCorrectly * -- GitLab