【问题标题】:STR_TO_DATE and ISO8601 Atomtime formatSTR_TO_DATE 和 ISO 8601 到时间格式
【发布时间】:2014-08-04 06:55:47
【问题描述】:

我有一个无法更改的 MySQL 数据库,我从中读取日期。问题是我有一个存储日期的 varchar 列。日期以 atomtime 格式存储,例如。 2014-06-01T00:00:00+02:00

我不知道如何在 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')

提前致谢。

【问题讨论】:

    标签: mysql datetime atom-feed timestamp-with-timezone str-to-date


    【解决方案1】:

    这可能是最好的解决方法,使用存储函数将时间戳从存储格式解析并转换为 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');
    
    -- 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 注入漏洞。

    代码可以进一步优化,但正如所写,这个函数足够高效,可以在中等功率的机器上每秒评估数千个表达式。

    【讨论】:

      【解决方案2】:

      改用 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');
      

      【讨论】:

      • 成功了。谢谢!您的查询中缺少括号,此查询有效:SELECT *, 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')
      • 有趣的答案,但非常错误。 unix_timestamp 函数充其量是忽略其参数中的时区信息,并假设会话的时区。运行该查询,然后运行SHOW WARNINGS;
      • @Michael:我总是很高兴能学到新东西。如果他们的所有条目都在同一时区,也许我的解决方案仍然适用于 OP。但我不得不承认,这不是一个值得推荐的解决方案。
      • 有趣的点@Michael-sqlbot。在这种特殊情况下,所有条目的时区都是相同的。关于正确解决方案的任何线索?
      • 如果你只使用日期而不是确切的时间,那么你也可以使用 date() 函数而不是 unix_timestamp() 函数。但它似乎也有不尊重时区的缺陷。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-01
      • 2013-11-11
      相关资源
      最近更新 更多