Skip to content
This repository was archived by the owner on Jun 2, 2025. It is now read-only.

Commit aa9ad47

Browse files
committed
Add support for DATE_FORMAT()
1 parent 0b3ba08 commit aa9ad47

File tree

1 file changed

+100
-0
lines changed

1 file changed

+100
-0
lines changed

wp-includes/sqlite-ast/class-wp-sqlite-driver.php

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,51 @@ class WP_SQLite_Driver {
151151
'geometrycollection' => 'TEXT',
152152
);
153153

154+
/**
155+
* The MySQL to SQLite date formats translation.
156+
*
157+
* Maps MySQL formats to SQLite strftime() formats.
158+
*
159+
* For MySQL formats, see:
160+
* * https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-format
161+
*
162+
* For SQLite formats, see:
163+
* * https://www.sqlite.org/lang_datefunc.html
164+
* * https://strftime.org/
165+
*/
166+
const DATE_FORMAT_TO_STRFTIME_MAP = array(
167+
'%a' => '%D',
168+
'%b' => '%M',
169+
'%c' => '%n',
170+
'%D' => '%jS',
171+
'%d' => '%d',
172+
'%e' => '%j',
173+
'%H' => '%H',
174+
'%h' => '%h',
175+
'%I' => '%h',
176+
'%i' => '%M',
177+
'%j' => '%z',
178+
'%k' => '%G',
179+
'%l' => '%g',
180+
'%M' => '%F',
181+
'%m' => '%m',
182+
'%p' => '%A',
183+
'%r' => '%h:%i:%s %A',
184+
'%S' => '%s',
185+
'%s' => '%s',
186+
'%T' => '%H:%i:%s',
187+
'%U' => '%W',
188+
'%u' => '%W',
189+
'%V' => '%W',
190+
'%v' => '%W',
191+
'%W' => '%l',
192+
'%w' => '%w',
193+
'%X' => '%Y',
194+
'%x' => '%o',
195+
'%Y' => '%Y',
196+
'%y' => '%y',
197+
);
198+
154199
const DATA_TYPES_CACHE_TABLE = '_mysql_data_types_cache';
155200

156201
const CREATE_DATA_TYPES_CACHE_TABLE = 'CREATE TABLE IF NOT EXISTS _mysql_data_types_cache (
@@ -1323,6 +1368,8 @@ private function translate( $ast ) {
13231368
return $this->translate_sequence( $ast->get_children() );
13241369
case 'runtimeFunctionCall':
13251370
return $this->translate_runtime_function_call( $ast );
1371+
case 'functionCall':
1372+
return $this->translate_function_call( $ast );
13261373
case 'systemVariable':
13271374
// @TODO: Emulate some system variables, or use reasonable defaults.
13281375
// See: https://dev.mysql.com/doc/refman/8.4/en/server-system-variable-reference.html
@@ -1478,6 +1525,59 @@ private function translate_runtime_function_call( WP_Parser_Node $node ): string
14781525
}
14791526
}
14801527

1528+
private function translate_function_call( WP_Parser_Node $node ): string {
1529+
$nodes = $node->get_child_nodes();
1530+
$name = strtoupper(
1531+
$this->unquote_sqlite_identifier( $this->translate( $nodes[0] ) )
1532+
);
1533+
1534+
$args = array();
1535+
foreach ( $nodes[1]->get_child_nodes() as $child ) {
1536+
$args[] = $this->translate( $child );
1537+
}
1538+
1539+
switch ( $name ) {
1540+
case 'DATE_FORMAT':
1541+
list ( $date, $mysql_format ) = $args;
1542+
1543+
$format = strtr( $mysql_format, self::DATE_FORMAT_TO_STRFTIME_MAP );
1544+
if ( ! $format ) {
1545+
throw new Exception( "Could not translate a DATE_FORMAT() format to STRFTIME format ($mysql_format)" );
1546+
}
1547+
1548+
/*
1549+
* MySQL supports comparing strings and floats, e.g.
1550+
*
1551+
* > SELECT '00.42' = 0.4200
1552+
* 1
1553+
*
1554+
* SQLite does not support that. At the same time,
1555+
* WordPress likes to filter dates by comparing numeric
1556+
* outputs of DATE_FORMAT() to floats, e.g.:
1557+
*
1558+
* -- Filter by hour and minutes
1559+
* DATE_FORMAT(
1560+
* STR_TO_DATE('2014-10-21 00:42:29', '%Y-%m-%d %H:%i:%s'),
1561+
* '%H.%i'
1562+
* ) = 0.4200;
1563+
*
1564+
* Let's cast the STRFTIME() output to a float if
1565+
* the date format is typically used for string
1566+
* to float comparisons.
1567+
*
1568+
* In the future, let's update WordPress to avoid comparing
1569+
* strings and floats.
1570+
*/
1571+
$cast_to_float = "'%H.%i'" === $mysql_format;
1572+
if ( true === $cast_to_float ) {
1573+
return sprintf( 'CAST(STRFTIME(%s, %s) AS FLOAT)', $format, $date );
1574+
}
1575+
return sprintf( 'STRFTIME(%s, %s)', $format, $date );
1576+
default:
1577+
return $this->translate_sequence( $node->get_children() );
1578+
}
1579+
}
1580+
14811581
private function get_sqlite_create_table_statement( string $table_name, ?string $new_table_name = null ): array {
14821582
// 1. Get table info.
14831583
$table_info = $this->execute_sqlite_query(

0 commit comments

Comments
 (0)