Skip to content

Commit d43f30a

Browse files
authored
Use DateTimeColumn (#358)
1 parent 4241557 commit d43f30a

14 files changed

+188
-70
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
- New #353: Realize `Schema::loadResultColumn()` method (@Tigrov)
4141
- Enh #300: Remove realization of `Connection::createBatchQueryResult()` method (@Tigrov)
4242
- Bug #360: Fix columns order in composite primary key (@Tigrov)
43+
- New #358: Use `DateTimeColumn` class for datetime column types (@Tigrov)
4344

4445
## 1.2.0 March 21, 2024
4546

src/Column/ColumnBuilder.php

+30
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,34 @@ public static function binary(int|null $size = null): ColumnInterface
1313
{
1414
return new BinaryColumn(ColumnType::BINARY, size: $size);
1515
}
16+
17+
public static function timestamp(int|null $size = 0): DateTimeColumn
18+
{
19+
return new DateTimeColumn(ColumnType::TIMESTAMP, size: $size);
20+
}
21+
22+
public static function datetime(int|null $size = 0): DateTimeColumn
23+
{
24+
return new DateTimeColumn(ColumnType::DATETIME, size: $size);
25+
}
26+
27+
public static function datetimeWithTimezone(int|null $size = 0): DateTimeColumn
28+
{
29+
return new DateTimeColumn(ColumnType::DATETIMETZ, size: $size);
30+
}
31+
32+
public static function time(int|null $size = 0): DateTimeColumn
33+
{
34+
return new DateTimeColumn(ColumnType::TIME, size: $size);
35+
}
36+
37+
public static function timeWithTimezone(int|null $size = 0): DateTimeColumn
38+
{
39+
return new DateTimeColumn(ColumnType::TIMETZ, size: $size);
40+
}
41+
42+
public static function date(): DateTimeColumn
43+
{
44+
return new DateTimeColumn(ColumnType::DATE);
45+
}
1646
}

src/Column/ColumnDefinitionBuilder.php

+5-3
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ final class ColumnDefinitionBuilder extends AbstractColumnDefinitionBuilder
2020
'decimal',
2121
'numeric',
2222
'float',
23-
'time',
2423
'datetime2',
2524
'datetimeoffset',
25+
'time',
2626
'char',
2727
'varchar',
2828
'nchar',
@@ -124,10 +124,12 @@ protected function getDbType(ColumnInterface $column): string
124124
ColumnType::TEXT => 'nvarchar(max)',
125125
ColumnType::BINARY => 'varbinary(max)',
126126
ColumnType::UUID => 'uniqueidentifier',
127-
ColumnType::DATETIME => 'datetime2',
128127
ColumnType::TIMESTAMP => 'datetime2',
129-
ColumnType::DATE => 'date',
128+
ColumnType::DATETIME => 'datetime2',
129+
ColumnType::DATETIMETZ => 'datetimeoffset',
130130
ColumnType::TIME => 'time',
131+
ColumnType::TIMETZ => 'time',
132+
ColumnType::DATE => 'date',
131133
ColumnType::ARRAY => 'nvarchar(max)',
132134
ColumnType::STRUCTURED => 'nvarchar(max)',
133135
ColumnType::JSON => 'nvarchar(max)',

src/Column/ColumnFactory.php

+13-8
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,12 @@ final class ColumnFactory extends AbstractColumnFactory
4040
'double' => ColumnType::DOUBLE,
4141

4242
/** Date and time */
43-
'date' => ColumnType::DATE,
44-
'time' => ColumnType::TIME,
4543
'smalldatetime' => ColumnType::DATETIME,
4644
'datetime' => ColumnType::DATETIME,
4745
'datetime2' => ColumnType::DATETIME,
48-
'datetimeoffset' => ColumnType::DATETIME,
46+
'datetimeoffset' => ColumnType::DATETIMETZ,
47+
'time' => ColumnType::TIME,
48+
'date' => ColumnType::DATE,
4949

5050
/** Character strings */
5151
'char' => ColumnType::CHAR,
@@ -84,11 +84,16 @@ public function fromPseudoType(string $pseudoType, array $info = []): ColumnInte
8484

8585
protected function getColumnClass(string $type, array $info = []): string
8686
{
87-
if ($type === ColumnType::BINARY) {
88-
return BinaryColumn::class;
89-
}
90-
91-
return parent::getColumnClass($type, $info);
87+
return match ($type) {
88+
ColumnType::BINARY => BinaryColumn::class,
89+
ColumnType::TIMESTAMP => DateTimeColumn::class,
90+
ColumnType::DATETIME => DateTimeColumn::class,
91+
ColumnType::DATETIMETZ => DateTimeColumn::class,
92+
ColumnType::TIME => DateTimeColumn::class,
93+
ColumnType::TIMETZ => DateTimeColumn::class,
94+
ColumnType::DATE => DateTimeColumn::class,
95+
default => parent::getColumnClass($type, $info),
96+
};
9297
}
9398

9499
protected function getType(string $dbType, array $info = []): string

src/Column/DateTimeColumn.php

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\Db\Mssql\Column;
6+
7+
final class DateTimeColumn extends \Yiisoft\Db\Schema\Column\DateTimeColumn
8+
{
9+
protected function getMillisecondsFormat(): string
10+
{
11+
return match ($this->getDbType()) {
12+
'smalldatetime' => '',
13+
'datetime' => '.v',
14+
default => parent::getMillisecondsFormat(),
15+
};
16+
}
17+
}

tests/ColumnBuilderTest.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44

55
namespace Yiisoft\Db\Mssql\Tests;
66

7+
use PHPUnit\Framework\Attributes\DataProviderExternal;
78
use Yiisoft\Db\Mssql\Column\ColumnBuilder;
9+
use Yiisoft\Db\Mssql\Tests\Provider\ColumnBuilderProvider;
810
use Yiisoft\Db\Mssql\Tests\Support\TestTrait;
911
use Yiisoft\Db\Tests\AbstractColumnBuilderTest;
1012

@@ -20,9 +22,7 @@ public function getColumnBuilderClass(): string
2022
return ColumnBuilder::class;
2123
}
2224

23-
/**
24-
* @dataProvider \Yiisoft\Db\Mssql\Tests\Provider\ColumnBuilderProvider::buildingMethods
25-
*/
25+
#[DataProviderExternal(ColumnBuilderProvider::class, 'buildingMethods')]
2626
public function testBuildingMethods(
2727
string $buildingMethod,
2828
array $args,

tests/ColumnTest.php

+37-29
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,36 @@
44

55
namespace Yiisoft\Db\Mssql\Tests;
66

7+
use DateTimeImmutable;
8+
use DateTimeZone;
79
use PDO;
10+
use PHPUnit\Framework\Attributes\DataProviderExternal;
811
use Yiisoft\Db\Command\Param;
912
use Yiisoft\Db\Expression\Expression;
1013
use Yiisoft\Db\Mssql\Column\BinaryColumn;
14+
use Yiisoft\Db\Mssql\Column\ColumnBuilder;
1115
use Yiisoft\Db\Mssql\Connection;
16+
use Yiisoft\Db\Mssql\Tests\Provider\ColumnProvider;
1217
use Yiisoft\Db\Mssql\Tests\Support\TestTrait;
1318
use Yiisoft\Db\Query\Query;
1419
use Yiisoft\Db\Schema\Column\BooleanColumn;
1520
use Yiisoft\Db\Schema\Column\ColumnInterface;
1621
use Yiisoft\Db\Schema\Column\DoubleColumn;
1722
use Yiisoft\Db\Schema\Column\IntegerColumn;
1823
use Yiisoft\Db\Schema\Column\StringColumn;
19-
use Yiisoft\Db\Tests\AbstractColumnTest;
24+
use Yiisoft\Db\Tests\Common\CommonColumnTest;
2025

2126
use function str_repeat;
2227

2328
/**
2429
* @group mssql
2530
*/
26-
final class ColumnTest extends AbstractColumnTest
31+
final class ColumnTest extends CommonColumnTest
2732
{
2833
use TestTrait;
2934

35+
protected const COLUMN_BUILDER = ColumnBuilder::class;
36+
3037
private function insertTypeValues(Connection $db): void
3138
{
3239
$db->createCommand()->insert(
@@ -44,16 +51,21 @@ private function insertTypeValues(Connection $db): void
4451
)->execute();
4552
}
4653

47-
private function assertResultValues(array $result): void
54+
private function assertTypecastedValues(array $result, bool $allTypecasted = false): void
4855
{
4956
$this->assertSame(1, $result['int_col']);
5057
$this->assertSame(str_repeat('x', 100), $result['char_col']);
5158
$this->assertNull($result['char_col3']);
5259
$this->assertSame(1.234, $result['float_col']);
5360
$this->assertSame("\x10\x11\x12", $result['blob_col']);
54-
$this->assertSame('2023-07-11 14:50:00.123', $result['datetime_col']);
61+
$this->assertEquals(new DateTimeImmutable('2023-07-11 14:50:00.123', new DateTimeZone('UTC')), $result['datetime_col']);
5562
$this->assertFalse($result['bool_col']);
56-
$this->assertSame('[{"a":1,"b":null,"c":[1,3,5]}]', $result['json_col']);
63+
64+
if ($allTypecasted) {
65+
$this->assertSame([['a' => 1, 'b' => null, 'c' => [1, 3, 5]]], $result['json_col']);
66+
} else {
67+
$this->assertSame('[{"a":1,"b":null,"c":[1,3,5]}]', $result['json_col']);
68+
}
5769
}
5870

5971
public function testQueryWithTypecasting(): void
@@ -66,11 +78,11 @@ public function testQueryWithTypecasting(): void
6678

6779
$result = $query->one();
6880

69-
$this->assertResultValues($result);
81+
$this->assertTypecastedValues($result);
7082

7183
$result = $query->all();
7284

73-
$this->assertResultValues($result[0]);
85+
$this->assertTypecastedValues($result[0]);
7486

7587
$db->close();
7688
}
@@ -85,11 +97,11 @@ public function testCommandWithPhpTypecasting(): void
8597

8698
$result = $command->queryOne();
8799

88-
$this->assertResultValues($result);
100+
$this->assertTypecastedValues($result);
89101

90102
$result = $command->queryAll();
91103

92-
$this->assertResultValues($result[0]);
104+
$this->assertTypecastedValues($result[0]);
93105

94106
$db->close();
95107
}
@@ -138,29 +150,19 @@ public function testPhpTypeCast(): void
138150
{
139151
$db = $this->getConnection(true);
140152
$schema = $db->getSchema();
141-
$tableSchema = $schema->getTableSchema('type');
153+
$columns = $schema->getTableSchema('type')->getColumns();
142154

143155
$this->insertTypeValues($db);
144156

145157
$query = (new Query($db))->from('type')->one();
146158

147-
$intColPhpType = $tableSchema->getColumn('int_col')?->phpTypecast($query['int_col']);
148-
$charColPhpType = $tableSchema->getColumn('char_col')?->phpTypecast($query['char_col']);
149-
$charCol3PhpType = $tableSchema->getColumn('char_col3')?->phpTypecast($query['char_col3']);
150-
$floatColPhpType = $tableSchema->getColumn('float_col')?->phpTypecast($query['float_col']);
151-
$blobColPhpType = $tableSchema->getColumn('blob_col')?->phpTypecast($query['blob_col']);
152-
$datetimeColPhpType = $tableSchema->getColumn('datetime_col')?->phpTypecast($query['datetime_col']);
153-
$boolColPhpType = $tableSchema->getColumn('bool_col')?->phpTypecast($query['bool_col']);
154-
$jsonColPhpType = $tableSchema->getColumn('json_col')?->phpTypecast($query['json_col']);
155-
156-
$this->assertSame(1, $intColPhpType);
157-
$this->assertSame(str_repeat('x', 100), $charColPhpType);
158-
$this->assertNull($charCol3PhpType);
159-
$this->assertSame(1.234, $floatColPhpType);
160-
$this->assertSame("\x10\x11\x12", $blobColPhpType);
161-
$this->assertSame('2023-07-11 14:50:00.123', $datetimeColPhpType);
162-
$this->assertFalse($boolColPhpType);
163-
$this->assertSame([['a' => 1, 'b' => null, 'c' => [1, 3, 5]]], $jsonColPhpType);
159+
$result = [];
160+
161+
foreach ($columns as $columnName => $column) {
162+
$result[$columnName] = $column->phpTypecast($query[$columnName]);
163+
}
164+
165+
$this->assertTypecastedValues($result, true);
164166

165167
$db->close();
166168
}
@@ -178,18 +180,24 @@ public function testColumnInstance()
178180
$this->assertInstanceOf(BooleanColumn::class, $tableSchema->getColumn('bool_col'));
179181
}
180182

181-
/** @dataProvider \Yiisoft\Db\Mssql\Tests\Provider\ColumnProvider::predefinedTypes */
183+
#[DataProviderExternal(ColumnProvider::class, 'predefinedTypes')]
182184
public function testPredefinedType(string $className, string $type, string $phpType)
183185
{
184186
parent::testPredefinedType($className, $type, $phpType);
185187
}
186188

187-
/** @dataProvider \Yiisoft\Db\Mssql\Tests\Provider\ColumnProvider::dbTypecastColumns */
189+
#[DataProviderExternal(ColumnProvider::class, 'dbTypecastColumns')]
188190
public function testDbTypecastColumns(ColumnInterface $column, array $values)
189191
{
190192
parent::testDbTypecastColumns($column, $values);
191193
}
192194

195+
#[DataProviderExternal(ColumnProvider::class, 'phpTypecastColumns')]
196+
public function testPhpTypecastColumns(ColumnInterface $column, array $values)
197+
{
198+
parent::testPhpTypecastColumns($column, $values);
199+
}
200+
193201
public function testBinaryColumn()
194202
{
195203
$binaryCol = new BinaryColumn();

tests/Provider/ColumnBuilderProvider.php

+12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Yiisoft\Db\Mssql\Tests\Provider;
66

77
use Yiisoft\Db\Mssql\Column\BinaryColumn;
8+
use Yiisoft\Db\Mssql\Column\DateTimeColumn;
89

910
class ColumnBuilderProvider extends \Yiisoft\Db\Tests\Provider\ColumnBuilderProvider
1011
{
@@ -14,6 +15,17 @@ public static function buildingMethods(): array
1415

1516
$values['binary()'][2] = BinaryColumn::class;
1617
$values['binary(8)'][2] = BinaryColumn::class;
18+
$values['timestamp()'][2] = DateTimeColumn::class;
19+
$values['timestamp(3)'][2] = DateTimeColumn::class;
20+
$values['datetime()'][2] = DateTimeColumn::class;
21+
$values['datetime(3)'][2] = DateTimeColumn::class;
22+
$values['datetimeWithTimezone()'][2] = DateTimeColumn::class;
23+
$values['datetimeWithTimezone(3)'][2] = DateTimeColumn::class;
24+
$values['time()'][2] = DateTimeColumn::class;
25+
$values['time(3)'][2] = DateTimeColumn::class;
26+
$values['timeWithTimezone()'][2] = DateTimeColumn::class;
27+
$values['timeWithTimezone(3)'][2] = DateTimeColumn::class;
28+
$values['date()'][2] = DateTimeColumn::class;
1729

1830
return $values;
1931
}

tests/Provider/ColumnFactoryProvider.php

+7-6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Yiisoft\Db\Constant\ColumnType;
88
use Yiisoft\Db\Expression\Expression;
99
use Yiisoft\Db\Mssql\Column\BinaryColumn;
10+
use Yiisoft\Db\Mssql\Column\DateTimeColumn;
1011
use Yiisoft\Db\Schema\Column\ArrayColumn;
1112
use Yiisoft\Db\Schema\Column\BooleanColumn;
1213
use Yiisoft\Db\Schema\Column\DoubleColumn;
@@ -40,12 +41,12 @@ public static function dbTypes(): array
4041
['double', ColumnType::DOUBLE, DoubleColumn::class],
4142
['smallmoney', ColumnType::MONEY, StringColumn::class],
4243
['money', ColumnType::MONEY, StringColumn::class],
43-
['date', ColumnType::DATE, StringColumn::class],
44-
['time', ColumnType::TIME, StringColumn::class],
45-
['smalldatetime', ColumnType::DATETIME, StringColumn::class],
46-
['datetime', ColumnType::DATETIME, StringColumn::class],
47-
['datetime2', ColumnType::DATETIME, StringColumn::class],
48-
['datetimeoffset', ColumnType::DATETIME, StringColumn::class],
44+
['smalldatetime', ColumnType::DATETIME, DateTimeColumn::class],
45+
['datetime', ColumnType::DATETIME, DateTimeColumn::class],
46+
['datetime2', ColumnType::DATETIME, DateTimeColumn::class],
47+
['datetimeoffset', ColumnType::DATETIMETZ, DateTimeColumn::class],
48+
['time', ColumnType::TIME, DateTimeColumn::class],
49+
['date', ColumnType::DATE, DateTimeColumn::class],
4950
['char', ColumnType::CHAR, StringColumn::class],
5051
['varchar', ColumnType::STRING, StringColumn::class],
5152
['text', ColumnType::TEXT, StringColumn::class],

0 commit comments

Comments
 (0)