diff --git a/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/DatabaseRowDateTimeFieldsTest.php b/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/DatabaseRowDateTimeFieldsTest.php index 113bc8dfb518a798de245e70354923056f699b3f..2d82edf168537ced14f15a7525be0d5804b79ca5 100644 --- a/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/DatabaseRowDateTimeFieldsTest.php +++ b/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/DatabaseRowDateTimeFieldsTest.php @@ -154,7 +154,7 @@ final class DatabaseRowDateTimeFieldsTest extends UnitTestCase ], ]; $expected = $input; - $expected['databaseRow']['aField'] = 0; + $expected['databaseRow']['aField'] = '00:00:00'; self::assertEquals($expected, (new DatabaseRowDateTimeFields())->addData($input)); } @@ -279,7 +279,7 @@ final class DatabaseRowDateTimeFieldsTest extends UnitTestCase /** * @test */ - public function addDataConvertsMidnightTimeStringOfNullableFieldToTimestamp(): void + public function addDataConvertsMidnightTimeStringOfNullableFieldToDefaultValue(): void { $oldTimezone = date_default_timezone_get(); date_default_timezone_set('UTC'); @@ -301,7 +301,38 @@ final class DatabaseRowDateTimeFieldsTest extends UnitTestCase ], ]; $expected = $input; - $expected['databaseRow']['aField'] = 0; + $expected['databaseRow']['aField'] = '00:00:00'; + + self::assertEquals($expected, (new DatabaseRowDateTimeFields())->addData($input)); + date_default_timezone_set($oldTimezone); + } + + /** + * @test + */ + public function addDataConvertsMidnightTimeStringOfNullableFieldToNull(): void + { + $oldTimezone = date_default_timezone_get(); + date_default_timezone_set('UTC'); + $input = [ + 'tableName' => 'aTable', + 'processedTca' => [ + 'columns' => [ + 'aField' => [ + 'config' => [ + 'type' => 'datetime', + 'dbType' => 'time', + 'nullable' => true, + ], + ], + ], + ], + 'databaseRow' => [ + 'aField' => null, + ], + ]; + $expected = $input; + $expected['databaseRow']['aField'] = null; self::assertEquals($expected, (new DatabaseRowDateTimeFields())->addData($input)); date_default_timezone_set($oldTimezone); diff --git a/typo3/sysext/core/Classes/DataHandling/DataHandler.php b/typo3/sysext/core/Classes/DataHandling/DataHandler.php index 8c44eca99eb548b3a792480bc2baceb8586c105d..a3a5ef08e97e66b73444dab2aca49f1988a2e480 100644 --- a/typo3/sysext/core/Classes/DataHandling/DataHandler.php +++ b/typo3/sysext/core/Classes/DataHandling/DataHandler.php @@ -2137,12 +2137,14 @@ class DataHandler implements LoggerAwareInterface $isNativeDateTimeField = false; $nativeDateTimeFieldFormat = ''; $nativeDateTimeFieldEmptyValue = ''; + $nativeDateTimeFieldResetValue = ''; $nativeDateTimeType = $tcaFieldConf['dbType'] ?? ''; if (in_array($nativeDateTimeType, QueryHelper::getDateTimeTypes(), true)) { $isNativeDateTimeField = true; $dateTimeFormats = QueryHelper::getDateTimeFormats(); $nativeDateTimeFieldFormat = $dateTimeFormats[$nativeDateTimeType]['format']; $nativeDateTimeFieldEmptyValue = $dateTimeFormats[$nativeDateTimeType]['empty']; + $nativeDateTimeFieldResetValue = $dateTimeFormats[$nativeDateTimeType]['reset']; if (empty($value)) { $value = null; } else { @@ -2184,35 +2186,38 @@ class DataHandler implements LoggerAwareInterface } } - // Set the value to null if we have an empty value for a native field - $res['value'] = $isNativeDateTimeField && !$value ? null : $value; - // Skip range validation, if the default value equals 0 and the input value is 0, "0" or an empty string. // This is needed for timestamp date fields with ['range']['lower'] set. $skipRangeValidation = - isset($tcaFieldConf['default'], $res['value']) + isset($tcaFieldConf['default'], $value) && (int)$tcaFieldConf['default'] === 0 - && ($res['value'] === '' || $res['value'] === '0' || $res['value'] === 0); + && ($value === '' || $value === '0' || $value === 0); // Checking range of value: - if (!$skipRangeValidation && isset($tcaFieldConf['range']) && is_array($tcaFieldConf['range'])) { - if (isset($tcaFieldConf['range']['upper']) && ceil($res['value']) > (int)$tcaFieldConf['range']['upper']) { - $res['value'] = (int)$tcaFieldConf['range']['upper']; + if (!$skipRangeValidation && is_array($tcaFieldConf['range'] ?? null)) { + if (isset($tcaFieldConf['range']['upper']) && ceil($value) > (int)$tcaFieldConf['range']['upper']) { + $value = (int)$tcaFieldConf['range']['upper']; } - if (isset($tcaFieldConf['range']['lower']) && floor($res['value']) < (int)$tcaFieldConf['range']['lower']) { - $res['value'] = (int)$tcaFieldConf['range']['lower']; + if (isset($tcaFieldConf['range']['lower']) && floor($value) < (int)$tcaFieldConf['range']['lower']) { + $value = (int)$tcaFieldConf['range']['lower']; } } // Handle native date/time fields if ($isNativeDateTimeField) { - // Convert the timestamp back to a date/time - $res['value'] = $res['value'] ? gmdate($nativeDateTimeFieldFormat, $res['value']) : $nativeDateTimeFieldEmptyValue; + if ($tcaFieldConf['nullable'] ?? false) { + // Convert the timestamp back to a date/time if not null + $value = $value !== null ? gmdate($nativeDateTimeFieldFormat, $value) : null; + } else { + // Convert the timestamp back to a date/time + $value = $value !== null ? gmdate($nativeDateTimeFieldFormat, $value) : $nativeDateTimeFieldResetValue; + } } else { // Ensure value is always an int if no native field is used - $res['value'] = (int)($res['value'] ?? 0); + $value = (int)$value; } + $res['value'] = $value; return $res; } diff --git a/typo3/sysext/core/Classes/Database/Query/QueryHelper.php b/typo3/sysext/core/Classes/Database/Query/QueryHelper.php index 2af2d9349c37d49279ae646a8b26fca33bd55841..7dee47b7b81ed9a148af597051c7c9f7c72f5db8 100644 --- a/typo3/sysext/core/Classes/Database/Query/QueryHelper.php +++ b/typo3/sysext/core/Classes/Database/Query/QueryHelper.php @@ -196,7 +196,7 @@ class QueryHelper 'time' => [ 'empty' => '00:00:00', 'format' => 'H:i:s', - 'reset' => 0, + 'reset' => '00:00:00', ], ]; } diff --git a/typo3/sysext/core/Tests/Unit/DataHandling/DataHandlerTest.php b/typo3/sysext/core/Tests/Unit/DataHandling/DataHandlerTest.php index 2dd79c7fb82486d6d252f18b85e756f0fec7cd70..53ca88f553921b95b1adb1f25ad5ed37e672198e 100644 --- a/typo3/sysext/core/Tests/Unit/DataHandling/DataHandlerTest.php +++ b/typo3/sysext/core/Tests/Unit/DataHandling/DataHandlerTest.php @@ -536,6 +536,91 @@ final class DataHandlerTest extends UnitTestCase self::assertEquals($expectedOutput, $returnValue['value']); } + public static function inputValueCheckNativeDbTypeDataProvider(): array + { + return [ + 'Datetime at unix epoch' => [ + '1970-01-01T00:00:00Z', + 'datetime', + 'datetime', + false, + '1970-01-01 00:00:00', + ], + 'Default datetime' => [ + '0000-00-00 00:00:00', + 'datetime', + 'datetime', + false, + null, + ], + 'Default date' => [ + '0000-00-00', + 'date', + 'date', + false, + null, + ], + 'Default time' => [ + '00:00:00', + 'time', + 'time', + false, + '00:00:00', + ], + 'Null on nullable time' => [ + null, + 'time', + 'time', + true, + null, + ], + 'Null on not nullable time' => [ + null, + 'time', + 'time', + false, + '00:00:00', + ], + 'Minimum mysql datetime' => [ + '1000-01-01 00:00:00', + 'datetime', + 'datetime', + false, + '1000-01-01 00:00:00', + ], + 'Maximum mysql datetime' => [ + '9999-12-31 23:59:59', + 'datetime', + 'datetime', + false, + '9999-12-31 23:59:59', + ], + ]; + } + + /** + * @param string|null $value + * @param string $dbType + * @param string $format + * @param bool $nullable + * @param mixed|null $expectedOutput + * @dataProvider inputValueCheckNativeDbTypeDataProvider + * @test + */ + public function inputValueCheckNativeDbType(string|null $value, string $dbType, string $format, bool $nullable, $expectedOutput): void + { + $tcaFieldConf = [ + 'input' => [], + 'dbType' => $dbType, + 'format' => $dbType, + 'nullable' => $nullable, + ]; + + $returnValue = $this->subject->_call('checkValueForDatetime', $value, $tcaFieldConf); + + self::assertEquals($expectedOutput, $returnValue['value']); + } + /////////////////////////////////////////// // Tests concerning checkModifyAccessList ///////////////////////////////////////////