Skip to content

Commit c493612

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

File tree

4 files changed

+107
-1
lines changed

4 files changed

+107
-1
lines changed

src/Processor/Expression/FunctionEvaluator.php

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

tests/EndToEndTest.php

+32
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(null, null, null, 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

+13
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

+1-1
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)