1

我有一个MySQL数据库,我不能改变,我从哪里读取日期。问题是我有一个存储日期的varchar列。日期以atomtime格式存储,例如。 2014-06-01T00:00:00+02:00STR_TO_DATE和ISO8601 Atomtime格式

我无法确定如何在STR_TO_DATE函数中指定格式。我试过STR_TO_DATE(Endtime, '%Y-%m-%dT%H:%i:%s+02:00'),但那不起作用。

有没有人有这方面的解决方案?

我试图运行下面的查询(这是不正常):

SELECT *, COUNT(*) as antal 
FROM ivocall_calls 
WHERE Agentname LIKE 'Vinh Nguyen' 
    AND Status1 = 'SALG' 
    AND STR_TO_DATE(Endtime, '%Y-%m-%dT%H:%i:%s+02:00') 
     BETWEEN STR_TO_DATE('2014-06-01T00:00:00+02:00', '%Y-%m-%dT%H:%i:%s+02:00') 
      AND STR_TO_DATE('2014-06-30T00:00:00+02:00', '%Y-%m-%dT%H:%i:%s+02:00') 

在此先感谢。

回答

2

这可能是最好的解决使用存储功能来分析和存储格式的时间戳转换为MySQL的原生格式,使用内置的日期时间的数学用于执行时区转换。

以下功能将正确处理两种格式,即YYYY-MM-DDTHH:MM:SSZYYYY-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'); 

-- unexpected values will be cast as a datetime. 

DECLARE offset_sign TINYINT DEFAULT NULL; 
DECLARE offset_hours TINYINT DEFAULT NULL; 
DECLARE offset_minutes TINYINT DEFAULT NULL; 

-- 2014-02-01T23:59:59Z -- 

IF (in_ts REGEXP '^[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2}T[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}Z$') THEN 

    RETURN CONVERT_TZ(REPLACE(REPLACE(in_ts,'T',' '),'Z',''),'UTC',@@time_zone); 

-- 2014-09-18T07:00-06:00 -- or +05:00 

ELSEIF (in_ts REGEXP '^[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2}T[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}[+-][[:digit:]]{2}:[[:digit:]]{2}$') THEN 

    SET offset_sign = IF(SUBSTRING(in_ts FROM 20 FOR 1) = '+', -1, +1); # we need to flip the sign, to "back out" the offset to get a time in UTC 
    SET offset_hours = CAST(SUBSTRING(in_ts FROM 21 FOR 2) AS SIGNED) * offset_sign; 
    SET offset_minutes = CAST(SUBSTRING(in_ts FROM 24 FOR 2) AS SIGNED) * offset_sign; 
    RETURN CONVERT_TZ(DATE_ADD(DATE_ADD(REPLACE(SUBSTRING(in_ts FROM 1 FOR 19),'T',' '), INTERVAL offset_hours HOUR), INTERVAL offset_minutes MINUTE),'UTC',@@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注入漏洞。

代码可以进一步优化,但是按照书面的说法,这个函数足够高效,它可以在中等功率的机器上每秒评估几千个表达式。

0

使用UNIX_TIMESTAMP()来代替:

SELECT something, COUNT() as antal FROM ivocall_calls 
WHERE Agentname LIKE 'Vinh Nguyen' 
AND Status1 = 'SALG' 
AND unix_timestamp(Endtime) BETWEEN 
    unix_timestamp('2014-06-01T00:00:00+02:00' and unix_timestamp('2014-06-30T00:00:00+02:00'); 
+0

这样做。谢谢! 您的查询中缺少括号,该查询的工作原理如下: 'SELECT *,COUNT(*)as antal FROM ivocall_calls WHERE Agentname LIKE'Vinh Nguyen'AND St​​atus1 ='SALG'AND unix_timestamp(Endtime)BETWEEN unix_timestamp 2014-06-01T00:00:00 + 02:00')AND unix_timestamp('2014-06-30T00:00:00 + 02:00')' – denn

+0

有趣的答案,但非常错误。最好的情况是,unix_timestamp函数忽略其参数中的时区信息,并假定会话的时区。运行该查询,然后运行'SHOW WARNINGS;'。 –

+1

@迈克尔:我总是乐于学习新东西。也许我的解决方案仍然适用于OP,如果他们的所有条目都在同一时区。但我必须承认,那么这不是一个值得推荐的解决方案。 –