Skip to content

Instantly share code, notes, and snippets.

@Schrank
Created January 29, 2022 07:54
Show Gist options
  • Save Schrank/d1f3243e47e52bc20809a3c20cd0441a to your computer and use it in GitHub Desktop.
Save Schrank/d1f3243e47e52bc20809a3c20cd0441a to your computer and use it in GitHub Desktop.
Doctrine 2.11, Datetime and Microseconds
<?php
declare(strict_types=1);
namespace Winkelwagen\Dbal;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\DBAL\Types\ConversionException;
use Doctrine\DBAL\Types\Type;
class DateTimeWithMicroseconds extends Type
{
private const TYPENAME = 'datetime';
public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
{
if (isset($column['version']) && $column['version'] === true) {
return 'TIMESTAMP';
}
if ($platform instanceof PostgreSqlPlatform) {
return 'TIMESTAMP(6) WITHOUT TIME ZONE';
}
return 'DATETIME(6)';
}
public function convertToPHPValue($value, AbstractPlatform $platform): mixed
{
if ($value === null || $value instanceof \DateTimeInterface) {
return $value;
}
if (str_contains($value, '.')) {
return \DateTimeImmutable::createFromFormat('Y-m-d H:i:s.u', $value);
}
return \DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $value);
}
public function convertToDatabaseValue($value, AbstractPlatform $platform): mixed
{
if (null === $value) {
return null;
}
if ($value instanceof \DateTimeInterface) {
return $value->format('Y-m-d H:i:s.u');
}
throw ConversionException::conversionFailedInvalidType(
$value,
$this->getName(),
['null', 'DateTime']
);
}
public function getName(): string
{
return self::TYPENAME;
}
public function requiresSQLCommentHint(AbstractPlatform $platform): bool
{
return true;
}
}
<?php
declare(strict_types=1);
namespace Winkelwagen\Dbal;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\DBAL\Types\ConversionException;
use JetBrains\PhpStorm\ArrayShape;
use PHPUnit\Framework\TestCase;
class DateTimeWithMicrosecondsTest extends TestCase
{
private DateTimeWithMicroseconds $type;
protected function setUp(): void
{
parent::setUp();
$this->type = new DateTimeWithMicroseconds();
}
public function testSqlDeclarationIsTimestampForVersionTrue(): void
{
$column = ['version' => true];
$platform = $this->createMock(AbstractPlatform::class);
$this->assertSame('TIMESTAMP', $this->type->getSQLDeclaration($column, $platform));
}
public function testSqlDeclarationForPostgre(): void
{
$column = [];
$platform = $this->createMock(PostgreSqlPlatform::class);
$this->assertSame('TIMESTAMP(6) WITHOUT TIME ZONE', $this->type->getSQLDeclaration($column, $platform));
}
public function testSqlDeclarationDefaultIsTimestamp6(): void
{
$column = [];
$platform = $this->createMock(AbstractPlatform::class);
$this->assertSame('DATETIME(6)', $this->type->getSQLDeclaration($column, $platform));
}
/**
* @dataProvider provideTimestamps
*/
public function testConvertToPhpValue($value, $expected): void
{
$platform = $this->createMock(AbstractPlatform::class);
$this->assertEquals($expected, $this->type->convertToPHPValue($value, $platform));
}
#[ArrayShape([
'null' => "null[]",
'with zero microseconds' => "array",
'without microseconds' => "array",
'with 200 microseconds' => "array"
])] public function provideTimestamps(): array
{
return [
'null' => [null, null],
'with zero microseconds' => ['2001-01-03 12:46:18.000', new \DateTimeImmutable('2001-01-03 12:46:18.000')],
'without microseconds' => ['2001-01-03 12:46:18', new \DateTimeImmutable('2001-01-03 12:46:18.000')],
'with 200 microseconds' => ['2001-01-03 12:46:18.200', new \DateTimeImmutable('2001-01-03 12:46:18.200')],
];
}
/**
* @dataProvider provideDateTimeObject
*/
public function testConvertToDatabaseValue($value, $expected): void
{
$platform = $this->createMock(AbstractPlatform::class);
$this->assertSame($expected, $this->type->convertToDatabaseValue($value, $platform));
}
#[ArrayShape([
'null' => "null[]",
'with zero microseconds' => "array",
'without microseconds' => "array",
'with 200 microseconds' => "array"
])] public function provideDateTimeObject(): array
{
return [
'null' => [null, null],
'with zero microseconds' => [
new \DateTimeImmutable('2001-01-03 12:46:18.000'),
'2001-01-03 12:46:18.000000'
],
'without microseconds' => [new \DateTimeImmutable('2001-01-03 12:46:18.000'), '2001-01-03 12:46:18.000000'],
'with 200 microseconds' => [
new \DateTimeImmutable('2001-01-03 12:46:18.200'),
'2001-01-03 12:46:18.200000'
],
];
}
public function testThrowsExceptionIfNotDateTimeOrNullOnConvertToDatabase(): void
{
$this->expectException(ConversionException::class);
$platform = $this->createMock(AbstractPlatform::class);
$this->type->convertToDatabaseValue('not an object', $platform);
}
public function testGetName(): void
{
$this->assertSame('datetime', $this->type->getName());
}
public function testRequiresSqlCommentHint(): void
{
$platform = $this->createMock(AbstractPlatform::class);
$this->assertTrue($this->type->requiresSQLCommentHint($platform));
}
}
doctrine:
dbal:
types:
datetime: \Paddox\Dbal\DateTimeWithMicroseconds
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment