@@ -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 'TIMESTAMPDIFF ' :
103+ return self ::sqlTimestampdiff ($ conn , $ scope , $ expr , $ row , $ result );
102104 case 'DATEDIFF ' :
103105 return self ::sqlDateDiff ($ conn , $ scope , $ expr , $ row , $ result );
104106 case 'DAY ' :
@@ -1545,4 +1547,79 @@ private static function getPhpIntervalFromExpression(
15451547 throw new ProcessorException ('MySQL INTERVAL unit ' . $ expr ->unit . ' not supported yet ' );
15461548 }
15471549 }
1550+
1551+ /**
1552+ * @param FakePdoInterface $conn
1553+ * @param Scope $scope
1554+ * @param FunctionExpression $expr
1555+ * @param array<string, mixed> $row
1556+ * @param QueryResult $result
1557+ *
1558+ * @return int|null
1559+ * @throws ProcessorException
1560+ */
1561+ private static function sqlTimestampdiff (
1562+ FakePdoInterface $ conn ,
1563+ Scope $ scope ,
1564+ FunctionExpression $ expr ,
1565+ array $ row ,
1566+ QueryResult $ result
1567+ ) {
1568+ $ args = $ expr ->args ;
1569+
1570+ if (\count ($ args ) !== 3 ) {
1571+ throw new ProcessorException ("MySQL TIMESTAMPDIFF() function must be called with three arguments " );
1572+ }
1573+
1574+ if (!$ args [0 ] instanceof ColumnExpression) {
1575+ throw new ProcessorException ("MySQL TIMESTAMPDIFF() function should be called with a unit for interval " );
1576+ }
1577+
1578+ /** @var string|null $unit */
1579+ $ unit = $ args [0 ]->columnExpression ;
1580+ /** @var string|int|float|null $start */
1581+ $ start = Evaluator::evaluate ($ conn , $ scope , $ args [1 ], $ row , $ result );
1582+ /** @var string|int|float|null $end */
1583+ $ end = Evaluator::evaluate ($ conn , $ scope , $ args [2 ], $ row , $ result );
1584+
1585+ if ($ start === null || $ end === null || $ unit === null ) {
1586+ return null ;
1587+ }
1588+
1589+ try {
1590+ $ dtStart = new \DateTime ((string ) $ start );
1591+ $ dtEnd = new \DateTime ((string ) $ end );
1592+ } catch (\Exception $ e ) {
1593+ throw new ProcessorException ("Invalid datetime value passed to TIMESTAMPDIFF() " );
1594+ }
1595+
1596+ $ interval = $ dtStart ->diff ($ dtEnd );
1597+
1598+ // Calculate difference in seconds for fine-grained units
1599+ $ seconds = $ dtEnd ->getTimestamp () - $ dtStart ->getTimestamp ();
1600+
1601+ switch (strtoupper ($ unit )) {
1602+ case 'MICROSECOND ' :
1603+ return $ seconds * 1000000 ;
1604+ case 'SECOND ' :
1605+ return $ seconds ;
1606+ case 'MINUTE ' :
1607+ return (int ) floor ($ seconds / 60 );
1608+ case 'HOUR ' :
1609+ return (int ) floor ($ seconds / 3600 );
1610+ case 'DAY ' :
1611+ return (int ) $ interval ->days * ($ seconds < 0 ? -1 : 1 );
1612+ case 'WEEK ' :
1613+ return (int ) floor ($ interval ->days / 7 ) * ($ seconds < 0 ? -1 : 1 );
1614+ case 'MONTH ' :
1615+ return ($ interval ->y * 12 + $ interval ->m ) * ($ seconds < 0 ? -1 : 1 );
1616+ case 'QUARTER ' :
1617+ $ months = $ interval ->y * 12 + $ interval ->m ;
1618+ return (int ) floor ($ months / 3 ) * ($ seconds < 0 ? -1 : 1 );
1619+ case 'YEAR ' :
1620+ return $ interval ->y * ($ seconds < 0 ? -1 : 1 );
1621+ default :
1622+ throw new ProcessorException ("Unsupported unit ' $ unit' in TIMESTAMPDIFF() " );
1623+ }
1624+ }
15481625}
0 commit comments