TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\Domain;
|
||||
|
||||
use TYPO3\CMS\Core\Database\Query\QueryHelper;
|
||||
use TYPO3\CMS\Core\Schema\Field\DateTimeFieldType;
|
||||
use TYPO3\CMS\Core\Utility\MathUtility;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final readonly class DateTimeFactory
|
||||
{
|
||||
public static function createFromDatabaseValue(int|string|null $value, DateTimeFieldType $fieldInformation): ?\DateTimeImmutable
|
||||
{
|
||||
return self::fromDatabase(
|
||||
$value,
|
||||
$fieldInformation->isNullable(),
|
||||
$fieldInformation->getFormat(),
|
||||
$fieldInformation->getPersistenceType(),
|
||||
);
|
||||
}
|
||||
|
||||
public static function createFromDatabaseValueAndTCAConfig(int|string|null $value, array $fieldConfig): ?\DateTimeImmutable
|
||||
{
|
||||
$persistenceType = in_array($fieldConfig['dbType'] ?? null, QueryHelper::getDateTimeTypes(), true) ? $fieldConfig['dbType'] : null;
|
||||
$isNative = $persistenceType !== null;
|
||||
$isNullable = (bool)($fieldConfig['nullable'] ?? $isNative);
|
||||
$format = self::getFormatFromTCAConfig($fieldConfig);
|
||||
return self::fromDatabase(
|
||||
$value,
|
||||
$isNullable,
|
||||
$format,
|
||||
$persistenceType
|
||||
);
|
||||
}
|
||||
|
||||
public static function getFormatFromTCAConfig(array $fieldConfig): string
|
||||
{
|
||||
$format = $fieldConfig['format'] ?? null;
|
||||
$persistenceType = in_array($fieldConfig['dbType'] ?? null, QueryHelper::getDateTimeTypes(), true) ? $fieldConfig['dbType'] : null;
|
||||
// A native time field must not be formatted as date
|
||||
if (($format === 'datetime' || $format === 'datetimesec' || $format === 'date') && $persistenceType === 'time') {
|
||||
return 'timesec';
|
||||
}
|
||||
// A native date field must not be formatted as time
|
||||
if (($format === 'time' || $format === 'timesec' || $format === 'datetime' || $format === 'datetimesec') && $persistenceType === 'date') {
|
||||
return 'date';
|
||||
}
|
||||
if (in_array($format, ['datetime', 'date', 'time', 'timesec', 'datetimesec'], true)) {
|
||||
return $format;
|
||||
}
|
||||
if ($persistenceType !== null) {
|
||||
return $persistenceType === 'time' ? 'timesec' : $persistenceType;
|
||||
}
|
||||
return 'datetime';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a DateTimeImmutable object from a unix timestamp in server localtime
|
||||
*
|
||||
* Alternative to \DateTimeImmutable('@…') which forces UTC timezone
|
||||
*/
|
||||
public static function createFromTimestamp(int $timestamp): \DateTimeImmutable
|
||||
{
|
||||
// Create a new DateTime object in current timezone
|
||||
//
|
||||
// Note: As documented by PHP, `\DateTime` or `\DateTimeImmutable`
|
||||
// objects created from timestamps (e.g., '@12345678') as the
|
||||
// first constructor argument will use UTC as timezone instead of localtime,
|
||||
// therefore we must not initialize with a timestamp directly.
|
||||
$datetime = new \DateTimeImmutable();
|
||||
|
||||
// Apply timestamp (which will not change the objects timezone)
|
||||
return $datetime->setTimestamp($timestamp);
|
||||
}
|
||||
|
||||
private static function fromDatabase(
|
||||
int|string|null $value,
|
||||
bool $isNullable,
|
||||
string $format,
|
||||
?string $persistenceType
|
||||
): ?\DateTimeImmutable {
|
||||
if ($value === null || $value === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$emptyFormat = QueryHelper::getDateTimeFormats()[$persistenceType ?? '']['empty'] ?? null;
|
||||
// A regular empty value is null for nullable fields
|
||||
$emptyValue = $isNullable ? null : ($emptyFormat ?? 0);
|
||||
// A legacy empty value is "0000-00-00" or "0000-00-00 00:00:00" stored
|
||||
// in a nullable native DATE or DATETIME field (which should already use
|
||||
// a proper `null` value, but still has a legacy empty value set).
|
||||
$legacyEmptyValue = $persistenceType === 'date' || $persistenceType === 'datetime' ? $emptyFormat : null;
|
||||
|
||||
if (MathUtility::canBeInterpretedAsInteger($value)) {
|
||||
$value = (int)$value;
|
||||
}
|
||||
if ($value === $emptyValue || $value === $legacyEmptyValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$datetime = match (true) {
|
||||
is_int($value) && ($format === 'time' || $format === 'timesec') => new \DateTimeImmutable(
|
||||
// time(sec) is stored as elapsed seconds in DB and has no defined date associated.
|
||||
// Per convention we map to 1970-01-01 for the sake of a reliable date.
|
||||
// We still want a PHP localtime timezone in the DateTime object set,
|
||||
// therefore we interpret the second as UTC time on 1970-01-01T00:00:00
|
||||
// and map the resulting value to PHP localtime
|
||||
gmdate(DateTimeFormat::ISO8601_LOCALTIME, $value)
|
||||
),
|
||||
// Unix timestamp
|
||||
is_int($value) => self::createFromTimestamp($value),
|
||||
// The database always contains server localtime in native fields.
|
||||
// The field value is something like "2016-01-01" or "2016-01-01 10:11:12.
|
||||
default => new \DateTimeImmutable($value),
|
||||
};
|
||||
} catch (\DateMalformedStringException $e) {
|
||||
throw new \InvalidArgumentException('Invalid date provided', 1743159490, $e);
|
||||
}
|
||||
|
||||
return match ($format) {
|
||||
// time(sec) is stored as elapsed seconds in DB, hence we normalize it as time on 1970-01-01 for consistency
|
||||
'time' => $datetime->setDate(1970, 1, 1)->setTime((int)$datetime->format('H'), (int)$datetime->format('i'), 0),
|
||||
'timesec' => $datetime->setDate(1970, 1, 1),
|
||||
'date' => $datetime->setTime(0, 0, 0),
|
||||
// default case also for 'datetimesec'
|
||||
default => $datetime,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user