【问题标题】:Detect if a date is in Daylight saving time in MySql在MySql中检测日期是否处于夏令时
【发布时间】:2011-04-30 03:25:21
【问题描述】:

我继承了一个遗留应用程序,其中所有日期和时间都存储在本地时区(英国)中。我无法更改它们的存储方式。

但是,要求是在应用程序中以 GMT 显示所有日期。因此,当我从数据库中检索事件列表时,我需要它以这种时间格式显示它们,同时观察每个特定事件日期是否正在运行夏令时。使用以下逻辑,我可以确定查询中是否启用了夏令时:

IF(CAST(TIMEDIFF(NOW(), UTC_TIMESTAMP()) AS SIGNED) >0, 
 DATE_FORMAT(CONVERT_TZ(ADDTIME(`event_date`, IFNULL(`event_time`, '00:00')), '+01:00', '+00:00'), '%H:%i'), `event_time`) AS event_time

然而,这显然只是检查它正在运行的日期。因此,未来任何跨越夏令时界限的事件都不起作用。

有没有一种方法可以检测给定日期是否在 mysql 查询本身的 DST 中?

任何帮助,非常感谢。

【问题讨论】:

  • 不能在mysql中使用CONVERT_TZ函数吗?或者它没有给你正确的结果? dev.mysql.com/doc/refman/5.1/en/…
  • 我在做。关键是我需要知道是否应用它。由于它存储在本地时间,其中一些是 GMT+1,另一些是 GMT+0。我不能不分青红皂白地将其应用于所有人。因此,我需要知道日期是否为 +1
  • 我的印象是 CONVERT_TZ 会根据 time_zone_blah 表的内容自动神奇地考虑夏令时,因此如果设置正确,您应该能够简单地执行 CONVERT_TZ(event_time, "US/Eastern", "GMT") 并拥有它给出了格林威治标准时间的正确时间
  • 好吧,我没有使用命名时区,因为它们似乎没有在系统上设置。然而,问题肯定是它们一开始并没有存储在特定的时区。这是插入它们时的当地时间。这可能是 GMT +1 或 GMT + 0

标签: mysql time timezone dst convert-tz


【解决方案1】:

时区不存储在 DATETIME 值中。有趣的是,它们用于 TIMESTAMP。

给定一个存储的日期,您可以在死后根据当地规则确定当时 DST 是否“开启”。由于您无法更改日期,我猜您无法添加列来存储时区...

创建一个包含规则并将给定日期转换为 GMT 的存储过程。

请注意,只有居住在格林威治附近的人希望他们的时间以 GMT 显示。 :)

祝你好运。

【讨论】:

  • 不正确。TIMESTAMP 类型存储时区信息。恰恰相反:TIMESTAMP 类型将输入值调整为 UTC,然后丢弃原始时区信息。来自the doc 的引用:MySQL 将 TIMESTAMP 值从当前时区转换为 UTC 进行存储,然后从 UTC 转换回当前时区进行检索。
  • 对于凡人来说,UTC 在功能上是一个时区。
【解决方案2】:

你在英国。因此,您的 TZ 应该是 CET 或 CEST(中欧夏令时)。您可以通过以下方式获取信息:

mysql> SELECT @@system_time_zone;
+--------------------+
| @@system_time_zone |
+--------------------+
| CEST               |
+--------------------+

我在许多存储过程中都使用了它。注意:我需要两种形式的 TZ 信息,无论我是否需要在应用 DST 的情况下计算偏移量。

CASE @@system_time_zone
  WHEN 'CET'  THEN SET l_tz = 'Europe/Paris';   SET l_tzOff = '+1:00';
  WHEN 'CEST' THEN SET l_tz = 'Europe/Paris';   SET l_tzOff = '+1:00';
  WHEN 'SGT'  THEN SET l_tz = 'Asia/Singapore'; SET l_tzOff = '+8:00';
  ELSE             SET l_tz = 'Europe/Paris';   SET l_tzOff = '+1:00';
END CASE;

你可以从中得到一些启发。

【讨论】:

    【解决方案3】:

    作为替代方案,这里有一些函数可以确定北美夏令时规则是否生效。

    截至 2007 年*,北美的 rule 一直是夏令时 (DST):

    • 三月第二个星期日凌晨 2 点(当地时间)开始
    • 11 月的第一个星期日凌晨 2 点(当地时间)结束

    *(从 1987 年到 2006 年它was 4 月的第一个星期日到 10 月的最后一个星期日。)


    MySQL 函数:

    DELIMITER // CREATE FUNCTION DSTstart(dt DATETIME) RETURNS DATETIME DETERMINISTIC BEGIN 
    RETURN cast(concat(year(now()),'-3-',(14-WEEKDAY(concat(year(now()),'-3-1'))),' 2:00') as datetime);
    END// DELIMITER ;
    
    DELIMITER // CREATE FUNCTION DSTstop(dt DATETIME) RETURNS DATETIME DETERMINISTIC BEGIN 
    RETURN cast(concat(year(now()),'-11-',(7-WEEKDAY(concat(year(now()),'-11-1'))),' 2:00') as datetime);
    END// DELIMITER ;
    
    DELIMITER // CREATE FUNCTION isDST(dt DATETIME) RETURNS BIT DETERMINISTIC BEGIN 
    RETURN (dt>=cast(concat(year(now()),'-3-',(14-WEEKDAY(concat(year(now()),'-3-1'))),' 2:00') as datetime))
    AND (dt<cast(concat(year(now()),'-11-',(7-WEEKDAY(concat(year(now()),'-11-1'))),' 2:00') as datetime));
    END// DELIMITER ;
    

    仅供参考,我将包含函数的 VBA 和 Excel 工作表版本:

    Excel/Access/etc VBA 函数:

    Function DSTstart(dt As Date) As Date 
      DSTstart = CDate(Year(dt) & "-3-" & (15 - Weekday(CDate(Year(dt) & "-3-1"), 2)) & " 2:00")
    End Function
    
    Function DSTstop(dt As Date) As Date 
      DSTstop = CDate(Year(dt) & "-11-" & (8 - Weekday(CDate(Year(dt) & "-11-1"), 2)) & " 2:00")
    End Function
    
    Function isDST(dt As Date) As Boolean
      isDST = (dt >= CDate(Year(dt) & "-3-" & (15 - Weekday(CDate(Year(dt) & "-3-1"), 2)) & " 2:00") _
              And dt < CDate(Year(dt) & "-11-" & (8 - Weekday(CDate(Year(dt) & "-11-1"), 2)) & " 2:00"))
    End Function
    

    Excel 工作表函数:

    DST 开始: (返回[A1] 中日期所在年份的 DST 开始日期/时间)

    =DATE(YEAR(A1),3,15-WEEKDAY(DATE(YEAR(A1),3,1),2))+TIMEVALUE("2:00")
    

    DST 结束: (返回 [A1] 中日期的年份中 DST 开始的日期/时间)

    =DATE(YEAR(A1),11,8-WEEKDAY(DATE(YEAR(A1),11,1),2))+TIMEVALUE("2:00")
    

    是 DST 吗?: (如果 DST 在 [A1] 中的日期有效,则返回 TRUE

    =AND(A1>=DATE(YEAR(A1),3,15-WEEKDAY(DATE(YEAR(A1),3,1),2))+TIMEVALUE("2:00"),A1<DATE(YEAR(A1),11,8-WEEKDAY(DATE(YEAR(A1),11,1),2))+TIMEVALUE("2:00"))
    

    【讨论】:

      【解决方案4】:

      发现一个丑陋的复杂方式,@DT 是输入日期和时间

      Set @DT=20150329020304; # -> 0
      
      Set @DT=20150329030304; # -> 1(-0.0511)
      
      Set @DT=20150329040304; # -> 1(-1)
      
      Select 0!=(UNIX_TIMESTAMP(@DT)-   UNIX_TIMESTAMP(Concat(Year(@DT),'0101',Time_Format(@DT,'%H%i%s')))-DateDiff(@DT,Concat(Year(@DT),'0101',Time_Format(@DT,'%H%i%s')))*86400)/3600 as DlsIsOn
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-11-20
        • 2020-04-29
        • 1970-01-01
        • 2010-11-06
        • 2022-01-20
        • 2013-11-22
        • 2010-09-22
        相关资源
        最近更新 更多