Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/Platforms/PostgreSQLPlatform.php
Original file line number Diff line number Diff line change
Expand Up @@ -594,15 +594,15 @@
*/
public function getDateTimeTypeDeclarationSQL(array $column): string
{
return 'TIMESTAMP(0) WITHOUT TIME ZONE';
return sprintf('TIMESTAMP(%d) WITHOUT TIME ZONE', $column['precision']);

Check warning on line 597 in src/Platforms/PostgreSQLPlatform.php

View check run for this annotation

Codecov / codecov/patch

src/Platforms/PostgreSQLPlatform.php#L597

Added line #L597 was not covered by tests
}

/**
* {@inheritDoc}
*/
public function getDateTimeTzTypeDeclarationSQL(array $column): string
{
return 'TIMESTAMP(0) WITH TIME ZONE';
return sprintf('TIMESTAMP(%d) WITH TIME ZONE', $column['precision']);

Check warning on line 605 in src/Platforms/PostgreSQLPlatform.php

View check run for this annotation

Codecov / codecov/patch

src/Platforms/PostgreSQLPlatform.php#L605

Added line #L605 was not covered by tests
}

/**
Expand Down Expand Up @@ -662,9 +662,14 @@
return 'TEXT';
}

public function getDateTimeFormatString(): string

Check warning on line 665 in src/Platforms/PostgreSQLPlatform.php

View check run for this annotation

Codecov / codecov/patch

src/Platforms/PostgreSQLPlatform.php#L665

Added line #L665 was not covered by tests
{
return 'Y-m-d H:i:s.u';

Check warning on line 667 in src/Platforms/PostgreSQLPlatform.php

View check run for this annotation

Codecov / codecov/patch

src/Platforms/PostgreSQLPlatform.php#L667

Added line #L667 was not covered by tests
}

public function getDateTimeTzFormatString(): string
{
return 'Y-m-d H:i:sO';
return 'Y-m-d H:i:s.uO';

Check warning on line 672 in src/Platforms/PostgreSQLPlatform.php

View check run for this annotation

Codecov / codecov/patch

src/Platforms/PostgreSQLPlatform.php#L672

Added line #L672 was not covered by tests
}

public function getEmptyIdentityInsertSQL(string $quotedTableName, string $quotedIdentifierColumnName): string
Expand Down
17 changes: 17 additions & 0 deletions src/Schema/PostgreSQLSchemaManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\DBAL\Result;
use Doctrine\DBAL\Types\JsonType;
use Doctrine\DBAL\Types\PhpDateTimeMappingType;

Check failure on line 11 in src/Schema/PostgreSQLSchemaManager.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (PHP: 8.4)

Type Doctrine\DBAL\Types\PhpDateTimeMappingType is not used in this file.
use Doctrine\DBAL\Types\Type;

use function array_change_key_case;
Expand Down Expand Up @@ -331,6 +332,22 @@

break;

case 'timestamp':
case 'timestamptz':

Check warning on line 336 in src/Schema/PostgreSQLSchemaManager.php

View check run for this annotation

Codecov / codecov/patch

src/Schema/PostgreSQLSchemaManager.php#L335-L336

Added lines #L335 - L336 were not covered by tests
if (
preg_match(
Copy link
Member

@morozov morozov May 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see if this could be replaced with $this->parseColumnTypeParameters($completeType) (introduced in #6933). You will need to rebase on 4.3.x and retarget the PR.

'([A-Za-z]+\(([0-9]+)\))',
$tableColumn['complete_type'],
$match,
) === 1

Check warning on line 342 in src/Schema/PostgreSQLSchemaManager.php

View check run for this annotation

Codecov / codecov/patch

src/Schema/PostgreSQLSchemaManager.php#L338-L342

Added lines #L338 - L342 were not covered by tests
) {
$precision = (int) $match[1];

Check warning on line 344 in src/Schema/PostgreSQLSchemaManager.php

View check run for this annotation

Codecov / codecov/patch

src/Schema/PostgreSQLSchemaManager.php#L344

Added line #L344 was not covered by tests
} else {
$precision = 6;

Check warning on line 346 in src/Schema/PostgreSQLSchemaManager.php

View check run for this annotation

Codecov / codecov/patch

src/Schema/PostgreSQLSchemaManager.php#L346

Added line #L346 was not covered by tests
}

break;

Check warning on line 349 in src/Schema/PostgreSQLSchemaManager.php

View check run for this annotation

Codecov / codecov/patch

src/Schema/PostgreSQLSchemaManager.php#L349

Added line #L349 was not covered by tests

case 'year':
$length = null;
break;
Expand Down
6 changes: 0 additions & 6 deletions src/Types/DateTimeImmutableType.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,6 @@ public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?Da
return $value;
}

$dateTime = DateTimeImmutable::createFromFormat($platform->getDateTimeFormatString(), $value);

if ($dateTime !== false) {
return $dateTime;
}

try {
return new DateTimeImmutable($value);
} catch (Exception $e) {
Expand Down
6 changes: 0 additions & 6 deletions src/Types/DateTimeType.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,6 @@ public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?Da
return $value;
}

$dateTime = DateTime::createFromFormat($platform->getDateTimeFormatString(), $value);

if ($dateTime !== false) {
return $dateTime;
}

try {
return new DateTime($value);
} catch (Exception $e) {
Expand Down
20 changes: 10 additions & 10 deletions src/Types/DateTimeTzImmutableType.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Exception\InvalidFormat;
use Doctrine\DBAL\Types\Exception\InvalidType;
use Exception;

/**
* Immutable type of {@see DateTimeTzType}.
Expand Down Expand Up @@ -59,16 +60,15 @@
return $value;
}

$dateTime = DateTimeImmutable::createFromFormat($platform->getDateTimeTzFormatString(), $value);

if ($dateTime !== false) {
return $dateTime;
try {
return new DateTimeImmutable($value);
} catch (Exception $e) {

Check failure on line 65 in src/Types/DateTimeTzImmutableType.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (PHP: 8.4)

Referencing general \Exception; use \Throwable instead.
throw InvalidFormat::new(
$value,
static::class,
$platform->getDateTimeTzFormatString(),
$e,
);
}

throw InvalidFormat::new(
$value,
static::class,
$platform->getDateTimeTzFormatString(),
);
}
}
19 changes: 10 additions & 9 deletions src/Types/DateTimeTzType.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Exception\InvalidFormat;
use Doctrine\DBAL\Types\Exception\InvalidType;
use Exception;

/**
* DateTime type accepting additional information about timezone offsets.
Expand Down Expand Up @@ -73,15 +74,15 @@
return $value;
}

$dateTime = DateTime::createFromFormat($platform->getDateTimeTzFormatString(), $value);
if ($dateTime !== false) {
return $dateTime;
try {
return new DateTime($value);
} catch (Exception $e) {

Check failure on line 79 in src/Types/DateTimeTzType.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (PHP: 8.4)

Referencing general \Exception; use \Throwable instead.
throw InvalidFormat::new(
$value,
static::class,
$platform->getDateTimeTzFormatString(),
$e,
);
}

throw InvalidFormat::new(
$value,
static::class,
$platform->getDateTimeTzFormatString(),
);
}
}
26 changes: 1 addition & 25 deletions src/Types/VarDateTimeImmutableType.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,39 +6,15 @@

use DateTimeImmutable;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Exception\InvalidType;

Check failure on line 9 in src/Types/VarDateTimeImmutableType.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (PHP: 8.4)

Type Doctrine\DBAL\Types\Exception\InvalidType is not used in this file.
use Doctrine\DBAL\Types\Exception\ValueNotConvertible;
use Exception;

/**

Check failure on line 13 in src/Types/VarDateTimeImmutableType.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (PHP: 8.4)

Found multi-line doc comment with single line content, use one-line doc comment instead.
* Immutable type of {@see VarDateTimeType}.
* @deprecated Use {@see DateTimeImmutableType} instead.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this relevant to introducing the support for precision?

*/
class VarDateTimeImmutableType extends DateTimeImmutableType
{
/**
* @param T $value
*
* @return (T is null ? null : string)
*
* @template T
*/
public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): ?string
{
if ($value === null) {
return $value;
}

if ($value instanceof DateTimeImmutable) {
return $value->format($platform->getDateTimeFormatString());
}

throw InvalidType::new(
$value,
static::class,
['null', DateTimeImmutable::class],
);
}

/**
* @param T $value
*
Expand Down
6 changes: 1 addition & 5 deletions src/Types/VarDateTimeType.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,8 @@
use Doctrine\DBAL\Types\Exception\ValueNotConvertible;
use Exception;

/**

Check failure on line 12 in src/Types/VarDateTimeType.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (PHP: 8.4)

Found multi-line doc comment with single line content, use one-line doc comment instead.
* Variable DateTime Type using DateTime::__construct() instead of DateTime::createFromFormat().
*
* This type has performance implications as it runs twice as long as the regular
* {@see DateTimeType}, however in certain PostgreSQL configurations with
* TIMESTAMP(n) columns where n > 0 it is necessary to use this type.
* @deprecated Use {@see DateTimeType} instead.
*/
class VarDateTimeType extends DateTimeType
{
Expand Down
9 changes: 9 additions & 0 deletions tests/Functional/Schema/PostgreSQL/ComparatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,15 @@ public function testCompareBinariesOfDifferentLength(): void
});
}

public function testCompareDatesOfZeroPrecision(): void
{
$this->testColumnModification(static function (Table $table, string $name): Column {
return $table->addColumn($name, Types::DATETIME_IMMUTABLE)->setPrecision(null);
}, static function (Column $column): void {
$column->setPrecision(0);
});
}

public function testPlatformOptionsChangedColumnComparison(): void
{
$table = new Table('update_json_to_jsonb_table');
Expand Down
28 changes: 28 additions & 0 deletions tests/Functional/Schema/PostgreSQLSchemaManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,34 @@ public function testPartitionTable(): void
));
self::assertSame(1, $partitionsCount);
}

public function testTimestampPrecisionComparison(): void
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move this to SchemaManagerTest. It's fine if for now it will be skipped on all platforms except for Postgres, but we want to be able to reuse this test once the support for precision is added to more platforms.

{
$offlineTable = new Table('timestamp_columns_test');
$offlineTable->addColumn('t_0_wz', Types::DATETIME_MUTABLE, ['precision' => 0]);
$offlineTable->addColumn('t_6_wz', Types::DATETIME_MUTABLE, ['precision' => 6]);
$offlineTable->addColumn('t_u_wz', Types::DATETIME_MUTABLE, ['precision' => 6]);
$offlineTable->addColumn('t_0_tz', Types::DATETIMETZ_MUTABLE, ['precision' => 0]);
$offlineTable->addColumn('t_6_tz', Types::DATETIMETZ_MUTABLE, ['precision' => 6]);
$offlineTable->addColumn('t_u_tz', Types::DATETIMETZ_MUTABLE, ['precision' => 6]);

$createTableSQL = <<<'SQL'
CREATE TABLE timestamp_columns_test (
Copy link
Member

@morozov morozov May 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why create this table via SQL if the table is already defined above via the DBAL API?

t_0_wz TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
t_6_wz TIMESTAMP(6) WITHOUT TIME ZONE NOT NULL,
t_u_wz TIMESTAMP WITHOUT TIME ZONE NOT NULL,
t_0_tz TIMESTAMP(0) WITH TIME ZONE NOT NULL,
t_6_tz TIMESTAMP(6) WITH TIME ZONE NOT NULL,
t_u_tz TIMESTAMP WITH TIME ZONE NOT NULL
)
SQL;

$this->connection->executeStatement($createTableSQL);

$onlineTable = $this->connection->createSchemaManager()->introspectTable($offlineTable->getName());

self::assertEquals($offlineTable->getColumns(), $onlineTable->getColumns());
}
}

class MoneyType extends Type
Expand Down
12 changes: 0 additions & 12 deletions tests/Types/DateTimeImmutableTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,6 @@ public function testConvertsNullToPHPValue(): void

public function testConvertsDateTimeStringToPHPValue(): void
{
$this->platform->expects(self::once())
->method('getDateTimeFormatString')
->willReturn('Y-m-d H:i:s');

$date = $this->type->convertToPHPValue('2016-01-01 15:58:59', $this->platform);

self::assertInstanceOf(DateTimeImmutable::class, $date);
Expand All @@ -90,10 +86,6 @@ public function testConvertsDateTimeStringToPHPValue(): void

public function testConvertsDateTimeStringWithMicrosecondsToPHPValue(): void
{
$this->platform
->method('getDateTimeFormatString')
->willReturn('Y-m-d H:i:s');

$date = $this->type->convertToPHPValue('2016-01-01 15:58:59.123456', $this->platform);

self::assertNotNull($date);
Expand All @@ -102,10 +94,6 @@ public function testConvertsDateTimeStringWithMicrosecondsToPHPValue(): void

public function testThrowsExceptionDuringConversionToPHPValueWithInvalidDateTimeString(): void
{
$this->platform->expects(self::atLeastOnce())
->method('getDateTimeFormatString')
->willReturn('Y-m-d H:i:s');

$this->expectException(ConversionException::class);

$this->type->convertToPHPValue('invalid datetime string', $this->platform);
Expand Down
8 changes: 0 additions & 8 deletions tests/Types/DateTimeTzImmutableTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,6 @@ public function testConvertsNullToPHPValue(): void

public function testConvertsDateTimeWithTimezoneStringToPHPValue(): void
{
$this->platform->expects(self::once())
->method('getDateTimeTzFormatString')
->willReturn('Y-m-d H:i:s T');

$date = $this->type->convertToPHPValue('2016-01-01 15:58:59 UTC', $this->platform);

self::assertInstanceOf(DateTimeImmutable::class, $date);
Expand All @@ -90,10 +86,6 @@ public function testConvertsDateTimeWithTimezoneStringToPHPValue(): void

public function testThrowsExceptionDuringConversionToPHPValueWithInvalidDateTimeWithTimezoneString(): void
{
$this->platform->expects(self::atLeastOnce())
->method('getDateTimeTzFormatString')
->willReturn('Y-m-d H:i:s T');

$this->expectException(ConversionException::class);

$this->type->convertToPHPValue('invalid datetime with timezone string', $this->platform);
Expand Down
Loading