Skip to content

Commit 9c21b41

Browse files
tarasomTaras Omelianchuk
authored andcommitted
feat: Add support for CONVERT_TZ() function
1 parent 226e0fe commit 9c21b41

File tree

2 files changed

+86
-0
lines changed

2 files changed

+86
-0
lines changed

src/Processor/Expression/FunctionEvaluator.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ public static function evaluate(
9999
return self::sqlCeiling($conn, $scope, $expr, $row, $result);
100100
case 'FLOOR':
101101
return self::sqlFloor($conn, $scope, $expr, $row, $result);
102+
case 'CONVERT_TZ':
103+
return self::sqlConvertTz($conn, $scope, $expr, $row, $result);
102104
case 'TIMESTAMPDIFF':
103105
return self::sqlTimestampdiff($conn, $scope, $expr, $row, $result);
104106
case 'DATEDIFF':
@@ -1548,6 +1550,49 @@ private static function getPhpIntervalFromExpression(
15481550
}
15491551
}
15501552

1553+
/**
1554+
* @param FakePdoInterface $conn
1555+
* @param Scope $scope
1556+
* @param FunctionExpression $expr
1557+
* @param array<string, mixed> $row
1558+
* @param QueryResult $result
1559+
*
1560+
* @return string|null
1561+
* @throws ProcessorException
1562+
*/
1563+
private static function sqlConvertTz(
1564+
FakePdoInterface $conn,
1565+
Scope $scope,
1566+
FunctionExpression $expr,
1567+
array $row,
1568+
QueryResult $result)
1569+
{
1570+
$args = $expr->args;
1571+
1572+
if (count($args) !== 3) {
1573+
throw new \InvalidArgumentException("CONVERT_TZ() requires exactly 3 arguments");
1574+
}
1575+
1576+
/** @var string|null $dtValue */
1577+
$dtValue = Evaluator::evaluate($conn, $scope, $args[0], $row, $result);
1578+
/** @var string|null $fromTzValue */
1579+
$fromTzValue = Evaluator::evaluate($conn, $scope, $args[1], $row, $result);
1580+
/** @var string|null $toTzValue */
1581+
$toTzValue = Evaluator::evaluate($conn, $scope, $args[2], $row, $result);
1582+
1583+
if ($dtValue === null || $fromTzValue === null || $toTzValue === null) {
1584+
return null;
1585+
}
1586+
1587+
try {
1588+
$dt = new \DateTime($dtValue, new \DateTimeZone($fromTzValue));
1589+
$dt->setTimezone(new \DateTimeZone($toTzValue));
1590+
return $dt->format('Y-m-d H:i:s');
1591+
} catch (\Exception $e) {
1592+
return null;
1593+
}
1594+
}
1595+
15511596
/**
15521597
* @param FakePdoInterface $conn
15531598
* @param Scope $scope

tests/FunctionEvaluatorTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,47 @@ private static function getPdo(string $connection_string, bool $strict_mode = fa
103103
return new \Vimeo\MysqlEngine\Php7\FakePdo($connection_string, '', '', $options);
104104
}
105105

106+
/**
107+
* @dataProvider convertTzProvider
108+
*/
109+
public function testConvertTz(string $sql, ?string $expected)
110+
{
111+
$query = self::getConnectionToFullDB()->prepare($sql);
112+
$query->execute();
113+
114+
$this->assertSame($expected, $query->fetch(\PDO::FETCH_COLUMN));
115+
}
116+
117+
private static function convertTzProvider(): array
118+
{
119+
return [
120+
'normal conversion' => [
121+
'sql' => "SELECT CONVERT_TZ('2025-09-23 02:30:00', 'UTC', 'Europe/Kyiv');",
122+
'expected' => "2025-09-23 05:30:00",
123+
],
124+
'same tz' => [
125+
'sql' => "SELECT CONVERT_TZ('2025-12-31 23:59:59', 'Europe/Kyiv', 'Europe/Kyiv');",
126+
'expected' => "2025-12-31 23:59:59",
127+
],
128+
'crossing DST' => [
129+
'sql' => "SELECT CONVERT_TZ('2025-07-01 12:00:00', 'America/New_York', 'UTC');",
130+
'expected' => "2025-07-01 16:00:00",
131+
],
132+
'null date' => [
133+
'sql' => "SELECT CONVERT_TZ(NULL, 'UTC', 'Europe/Kyiv');",
134+
'expected' => null,
135+
],
136+
'invalid timezone' => [
137+
'sql' => "SELECT CONVERT_TZ('2025-09-23 02:30:00', 'Invalid/Zone', 'UTC');",
138+
'expected' => null,
139+
],
140+
'invalid date' => [
141+
'sql' => "SELECT CONVERT_TZ('not-a-date', 'UTC', 'UTC');",
142+
'expected' => null,
143+
]
144+
];
145+
}
146+
106147
private static function getConnectionToFullDB(bool $emulate_prepares = true, bool $strict_mode = false) : \PDO
107148
{
108149
$pdo = self::getPdo('mysql:foo;dbname=test;', $strict_mode);

0 commit comments

Comments
 (0)