diff --git a/typo3/sysext/backend/Classes/Form/FormDataProvider/DatabaseRowDateTimeFields.php b/typo3/sysext/backend/Classes/Form/FormDataProvider/DatabaseRowDateTimeFields.php index 90635004e52c36b868b897d12f2239dac4f935a7..2ab245ef663e96bc929c4f01c6ae12687a7365ef 100644 --- a/typo3/sysext/backend/Classes/Form/FormDataProvider/DatabaseRowDateTimeFields.php +++ b/typo3/sysext/backend/Classes/Form/FormDataProvider/DatabaseRowDateTimeFields.php @@ -34,24 +34,32 @@ class DatabaseRowDateTimeFields implements FormDataProviderInterface $dateTimeFormats = QueryHelper::getDateTimeFormats(); foreach ($result['processedTca']['columns'] as $column => $columnConfig) { - if (($columnConfig['config']['type'] ?? '') !== 'datetime') { + if (($columnConfig['config']['type'] ?? '') !== 'datetime' + || !in_array($columnConfig['config']['dbType'] ?? '', $dateTimeTypes, true) + ) { + // it's a UNIX timestamp! We do not modify this here, as it will only be treated as a datetime because + // of eval being set to "date" or "datetime". This is handled in InputTextElement then. continue; } - if (in_array($columnConfig['config']['dbType'] ?? '', $dateTimeTypes, true)) { - $format = $dateTimeFormats[$columnConfig['config']['dbType']] ?? []; - if (!empty($result['databaseRow'][$column]) - && $result['databaseRow'][$column] !== ($format['empty'] ?? null) - ) { - // Create an ISO-8601 date from current field data; the database always contains UTC - // The field value is something like "2016-01-01" or "2016-01-01 10:11:12", so appending "UTC" - // makes date() treat it as a UTC date (which is what we store in the database). - $result['databaseRow'][$column] = date('c', (int)strtotime($result['databaseRow'][$column] . ' UTC')); - } else { - $result['databaseRow'][$column] = $format['reset'] ?? null; - } + // ensure the column's value is set + $result['databaseRow'][$column] = $result['databaseRow'][$column] ?? null; + + // Nullable fields do not need treatment + $isNullable = $columnConfig['config']['nullable'] ?? false; + if ($isNullable && $result['databaseRow'][$column] === null) { + continue; + } + + $format = $dateTimeFormats[$columnConfig['config']['dbType']] ?? []; + $emptyValueFormat = $format['empty'] ?? null; + if (!empty($result['databaseRow'][$column]) && $result['databaseRow'][$column] !== $emptyValueFormat) { + // Create an ISO-8601 date from current field data; the database always contains UTC + // The field value is something like "2016-01-01" or "2016-01-01 10:11:12", so appending "UTC" + // makes date() treat it as a UTC date (which is what we store in the database). + $result['databaseRow'][$column] = date('c', (int)strtotime($result['databaseRow'][$column] . ' UTC')); + } else { + $result['databaseRow'][$column] = $format['reset'] ?? null; } - // its a UNIX timestamp! We do not modify this here, as it will only be treated as a datetime because - // of eval being set to "date" or "datetime". This is handled in InputTextElement then. } return $result; } diff --git a/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/DatabaseRowDateTimeFieldsTest.php b/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/DatabaseRowDateTimeFieldsTest.php index f392d63185d794c5604d352a5af97c6614a059ba..113bc8dfb518a798de245e70354923056f699b3f 100644 --- a/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/DatabaseRowDateTimeFieldsTest.php +++ b/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/DatabaseRowDateTimeFieldsTest.php @@ -46,6 +46,38 @@ final class DatabaseRowDateTimeFieldsTest extends UnitTestCase self::assertEquals($expected, (new DatabaseRowDateTimeFields())->addData($input)); } + /** + * @test + */ + public function addDataSetsTimestampNullForDefaultDateField(): void + { + $input = [ + 'tableName' => 'aTable', + 'processedTca' => [ + 'columns' => [ + 'aField' => [ + 'config' => [ + 'type' => 'datetime', + 'dbType' => 'date', + 'nullable' => true, + ], + ], + ], + ], + ]; + + $expected = $input; + $expected['databaseRow']['aField'] = null; + + $actual = (new DatabaseRowDateTimeFields())->addData($input); + + self::assertEquals($expected, $actual); + + $expected = null; + + self::assertSame($expected, $actual['databaseRow']['aField']); + } + /** * @test */ @@ -69,6 +101,40 @@ final class DatabaseRowDateTimeFieldsTest extends UnitTestCase self::assertEquals($expected, (new DatabaseRowDateTimeFields())->addData($input)); } + /** + * @test + */ + public function addDataSetsTimestampNullForDefaultDateTimeField(): void + { + $input = [ + 'tableName' => 'aTable', + 'processedTca' => [ + 'columns' => [ + 'aField' => [ + 'config' => [ + 'type' => 'datetime', + 'dbType' => 'datetime', + 'nullable' => true, + ], + ], + ], + ], + 'databaseRow' => [ + 'aField' => null, + ], + ]; + $expected = $input; + $expected['databaseRow']['aField'] = null; + + $actual = (new DatabaseRowDateTimeFields())->addData($input); + + self::assertEquals($expected, $actual); + + $expected = null; + + self::assertSame($expected, $actual['databaseRow']['aField']); + } + /** * @test */ @@ -92,6 +158,37 @@ final class DatabaseRowDateTimeFieldsTest extends UnitTestCase self::assertEquals($expected, (new DatabaseRowDateTimeFields())->addData($input)); } + /** + * @test + */ + public function addDataSetsTimestampNullForDefaultTimeField(): void + { + $input = [ + 'tableName' => 'aTable', + 'processedTca' => [ + 'columns' => [ + 'aField' => [ + 'config' => [ + 'type' => 'datetime', + 'dbType' => 'time', + 'nullable' => true, + ], + ], + ], + ], + ]; + $expected = $input; + $expected['databaseRow']['aField'] = null; + + $actual = (new DatabaseRowDateTimeFields())->addData($input); + + self::assertEquals($expected, $actual); + + $expected = null; + + self::assertSame($expected, $actual['databaseRow']['aField']); + } + /** * @test */ @@ -179,6 +276,37 @@ final class DatabaseRowDateTimeFieldsTest extends UnitTestCase date_default_timezone_set($oldTimezone); } + /** + * @test + */ + public function addDataConvertsMidnightTimeStringOfNullableFieldToTimestamp(): 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' => '00:00:00', + ], + ]; + $expected = $input; + $expected['databaseRow']['aField'] = 0; + + self::assertEquals($expected, (new DatabaseRowDateTimeFields())->addData($input)); + date_default_timezone_set($oldTimezone); + } + /** * @test */