这可能最好使用存储函数来解析时间戳,并将时间戳从存储格式转换为 MySQL 的本机格式,使用内置的日期时间数学函数进行时区转换。
下面的函数将正确处理两种格式,YYYY-MM-DDTHH:MM:SSZ
以及YYYY-MM-DDTHH:MM:SS+/-HH:MM
正确形成的 MySQL 日期时间文字,它们将不加修改地传递。
DELIMITER $$
DROP FUNCTION IF EXISTS `from_iso8601_subset` $$
CREATE FUNCTION `from_iso8601_subset`(in_ts TINYTEXT) RETURNS DATETIME
DETERMINISTIC
NO SQL
BEGIN
-- this function takes an input timestamp value in a suppported subset of iso8601 values, and
-- and converts it to the equivalent MySQL datetime value, expressed in the current session's
-- time zone. Since this is also the timezone that columns in the TIMESTAMP data type expect,
-- this causes the input value to be stored correctly in the native TIMESTAMP format, which is.
-- UTC under the hood.
-- if you are taking the value here and stuffing it into a DATETIME column, you need to have your
-- session @@time_zone set to the same zone in which that column should be stored, or use
-- CONVERT(from_iso('input value'),'UTC','Your Desired Time Zone');
-- 2014-02-01T23:59:59Z --
IF (in_ts REGEXP '^[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2}[T ][[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}(Z|[+-][[:digit:]]{2}:[[:digit:]]{2})$') THEN
SET in_ts = REPLACE(REPLACE(in_ts, 'T', ' '), 'Z', '+00:00');
RETURN CONVERT_TZ(SUBSTRING(in_ts FROM 1 FOR 19), SUBSTRING(in_ts FROM 20 FOR 24), @@time_zone);
-- unexpected format -- let MySQL's built-in functions do the best they can; this will throw warnings
-- if the input is not a yyyy-mm-dd hh:mm:ss datetime literal; alternately this could return NULL.
ELSE
RETURN CAST(in_ts AS DATETIME);
END IF;
END $$
DELIMITER ;
示例输出:
mysql> SET @@time_zone = 'America/New_York';
Query OK, 0 rows affected (0.08 sec)
mysql> SELECT from_iso8601_subset('2014-06-01T00:00:00+02:00');
+--------------------------------------------------+
| from_iso8601_subset('2014-06-01T00:00:00+02:00') |
+--------------------------------------------------+
| 2014-05-31 18:00:00 |
+--------------------------------------------------+
1 row in set (0.08 sec)
mysql> set @@time_zone = 'UTC';
Query OK, 0 rows affected (0.08 sec)
mysql> SELECT from_iso8601_subset('2014-06-01T00:00:00+02:00');
+--------------------------------------------------+
| from_iso8601_subset('2014-06-01T00:00:00+02:00') |
+--------------------------------------------------+
| 2014-05-31 22:00:00 |
+--------------------------------------------------+
1 row in set (0.08 sec)
我们假设如果输入数据与其中一种模式匹配,那么传入的值的内容也将是正常的;如果您提供无意义的输入值,您将得到一些无意义的输出,例如如果您使用的时区为 '+99:00' 但它不会失败。该函数没有任何 SQL 注入漏洞。
代码可以进一步优化,但正如所写,这个函数足够高效,它可以在中等功率的机器上每秒评估数千个表达式。