Skip to content

Commit 078dee8

Browse files
committed
feat: add str_to_datetime function
1 parent 8a76dd6 commit 078dee8

File tree

4 files changed

+120
-1
lines changed

4 files changed

+120
-1
lines changed

src/Processor/Expression/FunctionEvaluator.php

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ public static function evaluate(
114114
return self::sqlInetAton($conn, $scope, $expr, $row, $result);
115115
case 'INET_NTOA':
116116
return self::sqlInetNtoa($conn, $scope, $expr, $row, $result);
117+
case 'STR_TO_DATE':
118+
return self::sqlStrToDate($conn, $scope, $expr, $row, $result);
117119
}
118120

119121
throw new ProcessorException("Function " . $expr->functionName . " not implemented yet");
@@ -1533,4 +1535,76 @@ private static function getPhpIntervalFromExpression(
15331535
throw new ProcessorException('MySQL INTERVAL unit ' . $expr->unit . ' not supported yet');
15341536
}
15351537
}
1538+
1539+
/**
1540+
* @param array<string, mixed> $row
1541+
* @return string|null
1542+
*/
1543+
private static function sqlStrToDate(
1544+
FakePdoInterface $conn,
1545+
Scope $scope,
1546+
FunctionExpression $expr,
1547+
array $row,
1548+
QueryResult $result
1549+
) : ?string {
1550+
$args = $expr->args;
1551+
1552+
if (\count($args) !== 2) {
1553+
throw new ProcessorException("MySQL DATE_FORMAT() function must be called with one argument");
1554+
}
1555+
1556+
$subject = (string) Evaluator::evaluate($conn, $scope, $args[0], $row, $result);
1557+
$format = (string) Evaluator::evaluate($conn, $scope, $args[1], $row, $result);
1558+
1559+
if (strpos($format, '%') === false) {
1560+
return null;
1561+
}
1562+
1563+
$date_format_list = [
1564+
"%b" => "M", "%c" => "n", "%d" => "d", "%D" => "jS", "%e" => "j",
1565+
"%m" => "m", "%M" => "F", "%y" => "y", "%Y" => "Y"
1566+
];
1567+
1568+
$time_format_list = [
1569+
"%h" => "h", "%H" => "H", "%i" => "i", "%I" => "h", "%k" => "G",
1570+
"%l" => "g", "%r" => "h:i:s A", "%s" => "s", "%S" => "s", "%T" => "H:i:s"
1571+
];
1572+
1573+
$has_date_format = false;
1574+
$has_time_format = false;
1575+
preg_match_all("/(?:%[a-zA-Z])/u", $format, $matches);
1576+
foreach ($matches[0] as $match) {
1577+
$has_date_format = $has_date_format || in_array($match, array_keys($date_format_list));
1578+
$has_time_format = $has_time_format || in_array($match, array_keys($time_format_list));
1579+
}
1580+
1581+
1582+
$format = \str_replace(
1583+
array_keys($date_format_list + $time_format_list),
1584+
array_values($date_format_list + $time_format_list),
1585+
$format
1586+
);
1587+
1588+
if ($has_date_format && $has_time_format) {
1589+
$time = \DateTimeImmutable::createFromFormat($format, $subject);
1590+
if($time !== false) {
1591+
return $time->format('Y-m-d G:i:s');
1592+
}
1593+
}
1594+
1595+
if ($has_date_format) {
1596+
$time = \DateTimeImmutable::createFromFormat($format, $subject);
1597+
if($time !== false) {
1598+
return $time->format('Y-m-d');
1599+
}
1600+
}
1601+
1602+
if ($has_time_format) {
1603+
$time = \DateTimeImmutable::createFromFormat($format, $subject);
1604+
if($time !== false) {
1605+
return $time->format('G:i:s');
1606+
}
1607+
}
1608+
return null;
1609+
}
15361610
}

tests/EndToEndTest.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,4 +1242,36 @@ private static function getConnectionToFullDB(bool $emulate_prepares = true, boo
12421242

12431243
return $pdo;
12441244
}
1245+
1246+
public function testStrToDateInSelectFunction()
1247+
{
1248+
$pdo = self::getConnectionToFullDB(false);
1249+
$query = $pdo->prepare("SELECT STR_TO_DATE('01,5,2013', '%d,%m,%Y') AS date");
1250+
1251+
$query->execute();
1252+
1253+
$d = mktime(0, 0, 0, 5, 1, 2013);
1254+
1255+
$current_date = date('Y-m-d', $d);
1256+
1257+
$this->assertSame(
1258+
[[
1259+
'date' => $current_date,
1260+
]],
1261+
$query->fetchAll(\PDO::FETCH_ASSOC)
1262+
);
1263+
}
1264+
1265+
public function testStrToDateInWhereFunction()
1266+
{
1267+
$pdo = self::getConnectionToFullDB(false);
1268+
$query = $pdo->prepare("SELECT id FROM `video_game_characters` WHERE `created_on` = (STR_TO_DATE('26/3/2022', '%d/%m/%Y') - INTERVAL 2 MONTH)");
1269+
1270+
$query->execute();
1271+
1272+
$this->assertSame(
1273+
[['id' => 16,]],
1274+
$query->fetchAll(\PDO::FETCH_ASSOC)
1275+
);
1276+
}
12451277
}

tests/SelectParseTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,4 +316,17 @@ public function testBracketedFirstSelect()
316316

317317
$select_query = \Vimeo\MysqlEngine\Parser\SQLParser::parse($sql);
318318
}
319+
320+
public function testStrToDateFunction()
321+
{
322+
$sql = "SELECT STR_TO_DATE('01,5,2013', '%d,%m,%Y')";
323+
$select_query = \Vimeo\MysqlEngine\Parser\SQLParser::parse($sql);
324+
$this->assertInstanceOf(SelectQuery::class, $select_query);
325+
326+
$strToDateFunction = $select_query->selectExpressions[0];
327+
$this->assertTrue(isset($strToDateFunction->args[0]));
328+
$this->assertTrue(isset($strToDateFunction->args[1]));
329+
$this->assertEquals('01,5,2013', $strToDateFunction->args[0]->value);
330+
$this->assertEquals('%d,%m,%Y', $strToDateFunction->args[1]->value);
331+
}
319332
}

tests/fixtures/bulk_character_insert.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ VALUES
1616
('13','pac man', 'Ms Pac Man’s worse three-quarters', 'hero','yellow circle','atari','1','0','{"magic":0, "speed":0, "strength":0, "weapons":0}', NOW()),
1717
('14','yoshi', 'Green machine', 'hero','dinosaur','super nintendo','1','0','{"magic":0, "speed":1, "strength":0, "weapons":0}', NOW()),
1818
('15','link', 'Zelda? I hardly knew her!', 'hero','not sure','nes','1','0','{"magic":1, "speed":0, "strength":0, "weapons":1}', NOW()),
19-
('16','dude', 'Duuuuuude', 'hero','sure','sega genesis','1','0','{"magic":1, "speed":0, "strength":0, "weapons":1}', NOW())
19+
('16','dude', 'Duuuuuude', 'hero','sure','sega genesis','1','0','{"magic":1, "speed":0, "strength":0, "weapons":1}', '2022-01-26 00:00:00')

0 commit comments

Comments
 (0)