代码之家  ›  专栏  ›  技术社区  ›  pencilslate

UTC和夏令时方案

  •  3
  • pencilslate  · 技术社区  · 15 年前

    我使用UTC在数据库中存储数据和时间值。这些值在客户机或每个客户机时区转换为localtime。我从 MSDN article ,在这里显示来自UTC的时间似乎会在夏令时造成问题。

    住在东海岸的人 美国输入一个值 比如“2003年10月26日01:10:00 AM”。

    1)由于 夏令时,凌晨2:00, 本地时钟重置为凌晨1:00, 创造25小时的一天。既然所有 凌晨1:00之间的时钟时间值 凌晨2点有两次 特定的早晨至少在大多数 美国和加拿大 计算机真的没有办法知道 上午1:10是指 在切换之前发生,或在切换之前发生 发生在 夏令时开关。

    2)同样,问题发生在 春天,当 早上好,没有2:10这样的时间 是。原因是2点的时候 特定的早晨,当地时间 时钟突然变为凌晨3点。 整个2:00小时从未发生过 今天是23小时。

    您如何处理这种情况1,当时您可能有4个事务,两个在切换之前,两个在切换之后,日光节约?如何向用户显示事务的时间,因为最后两个事务的显示时间可能比前两个事务的显示时间早,这是由于移位所致。有时,它可能被证明是不合逻辑的,例如:在邮件链中。

    补充:

    要添加有关上下文的更多信息,RIA应用程序(如在客户端上运行的Silverlight/Flash或通过WebService与服务器对话的任何客户端应用程序)允许用户使用PC本地时间选择交付时间或计划时间。

    如果我可以检查一个给定的输入时间是否无效,我可能会提醒用户。此外,对于旅行者来说,时区需要在时间点上找到,而不是基于用户选择,因为他们可能在不同的时区之间移动,并将时区保存在用户配置文件中没有帮助。

    一些用于评估输入时间的C测试样本:

    //2:30 am CT to UTC --> 8:30 am  
    DateTime dt = new DateTime(2009, 03, 08, 2, 30, 00, DateTimeKind.Local);  
    
    //8:30 am UTC to CT --> 3:30 am.. which is as expected  
    DateTime dt1 = new DateTime(2009, 03, 08, 8, 30, 00, DateTimeKind.Utc);  
    
    //check for daylight saving time returns false.. ??  
    TimeZoneInfo.Local.IsDaylightSavingTime(dt);  
    
    //check for daylight saving time returns true  
    TimeZoneInfo.Local.IsInvalidTime(dt);  
    
    7 回复  |  直到 8 年前
        1
  •  4
  •   shsteimer    15 年前

    这些场景是提倡使用DST的案例。你做什么没关系 显示 只要你 商店 分类 UTC中的值。也就是说,如果正确使用UTC,这些场景中出现的问题就会得到解决。

    是的,看到这样的记录会让人困惑:12:30、1:20、1:10、3:30,但如果这是根据UTC订购的记录(实际发生的情况),我认为这是正确的做法。

    因此,通过用UTC记录所有内容,然后用UTC或相对时间(如“17分钟前…”)显示所有内容,可以完全避免这个问题。


    如果你指的是用户提供的日期/时间,如评论中所建议的,我有一些坏消息告诉你:太糟糕了。我认为最好、最明显的解决方案是选择一个规则并用它运行。如果你真的需要完美地处理它,你的用户界面将需要扩展到以教育的方式处理这种每年只发生1小时的边缘情况,然后只处理不实时创建的事务(因为如果它们是实时的,你就知道DST等价物)。

        2
  •  2
  •   Pierre-Alain Vigeant    15 年前

    您需要存储时间偏移。

    目前东海岸的时间是(往返形式)

    2009-08-11T13:22:13.8493713-04:00
    

    即使东海岸被认为是-5,在夏令时,时间将是-4。

    10月26日上午01:10,时间是

    2009-10-26T1:10:00.0000000-04:00
    

    但是如果时钟超过02:00,我们又回到正常时间,你的时间将是

    2009-10-26T1:10:00.0000000-05:00
    

    为了处理偏移量,.net从2.0sp1开始提供类型datetimeoffset。SQLServer2008还提供了数据类型datetimeoffset,它将帮助您存储该值。如果不使用SQLServer2008,可以使用往返格式将日期存储为字符串:

    DateTime.Now.ToString("o")
    
        3
  •  1
  •   Stack Overflow is garbage    15 年前

    如何向用户显示事务的时间,因为最后两个事务的显示时间可能比前两个事务的显示时间早,这是由于移位所致。有时,它可能被证明是不合逻辑的,例如:在邮件链中。

    按UTC排序,并在本地时间显示。

    所以用户可能会看到这样的列表:

    01:10:00
    01:50:00
    01:05:00
    01:20:00
    

    要么,要么表演 按UTC排序。

        4
  •  1
  •   Apocalisp    15 年前

    如果您有人手工输入数据,那么祝您好运,除非他们以UTC时间输入数据。没有真正“正确”的方法来处理这个问题。如果您处理的日期不是用户输入的,比如事务发生的时间,只需将这些日期存储为UTC,这样生活就很好了。:)

        5
  •  1
  •   David Berger    15 年前

    一般来说,你的问题分为两部分:

    1. 接收用户输入
    2. 向用户显示数据

    这两个问题的处理应该类似,但在某些方面有一些单独的解决方法。至于1,最简单的选择是找出您的业务是否真的需要一种明确的方法来指定特定时间。很多应用程序都会忽略它(您上次使用的日期选择器是什么时候提供的?)简单假设任何一致的猜测算法都是足够的。如果输入了不存在的时间,您应该提供保护措施(即抛出错误)。

    至于2,跳过的时间并不重要,因为您的数据库是UTC格式的。重复的一小时可能会令人困惑,特别是在时间戳的痕迹中。如果值得的话,可以考虑用一个时区标识符来格式化日期字符串,该标识符包含对夏令时偏移量的引用。大多数旧样式,即非olson,时区名称包括(est与edt、gmt与bst等)。这足以消除歧义。 处于紧要关头 .这可能是您所需要的,因为这个边界案例可能不值得太多地破坏显示。如果您需要更多的输出,您还可以使用UTC偏移量格式化时间戳,这将使转换在时间戳跟踪中非常明确。

        6
  •  0
  •   Lance Roberts    15 年前

    SQL Server有一个新的类型“datetimeoffset”,它可以很好地处理这一问题,因为可以使用不同的偏移量来处理同一时间。对于我的SQL Server 2005数据库,我使用了一个该类型的字符串文本,nvarchar(25)格式为“YYYY-MM-DD HH:MM:SS HH:MM”。

    为此,我创建了将这些字符串转换为正确时间的例程。

        7
  •  0
  •   Vivi Rosa    8 年前

    我在新西兰时间内解决了以下问题:

    SET ANSI_NULLS ON
    GO
    
    SET QUOTED_IDENTIFIER ON
    GO
    
    -- =============================================
    -- Author:      Vivi Woolford
    -- Create date: 27-09-2016
    -- Description: This procedure only Works in New Zealand
    -- =============================================
    CREATE FUNCTION [dbo].[udf_GetLocalTimeFromUTC] 
    (   
        @UTCTime DATETIME
    )
    RETURNS DATETIME
    AS
    BEGIN
        --Daylight Saving commences on the last Sunday in September, when 2.00am becomes 3.00am. 
        --It ends on the first Sunday in April, when 3.00am becomes 2.00am.
        DECLARE @LocalTime DATETIME 
        DECLARE @OffSet INT = 12
    
        SELECT @LocalTime = DATEADD(HOUR, @OffSet, @UTCTime)
    
        IF @LocalTime BETWEEN 
        /*FINISH DAY LIGHT SAVINGS*/
        DATEADD(HOUR, 2, DATEADD(dd, (6-(DATEDIFF(dd,0,DATEADD(mm,(YEAR(@LocalTime)-1900) * 12 + 3,0))%7)),DATEADD(mm,(YEAR(@LocalTime)-1900) * 12 + 3,0)))
        AND     
        /*START DAY LIGHT SAVINGS*/
        DATEADD(HOUR, 2, DATEADD(dd, -1*(DATEPART(dw, DateAdd(day, -1, DateAdd(month, DateDiff(month, 0, @LocalTime)+1, 0)))-1),DateAdd(day, -1, DateAdd(month, DateDiff(month, 0, @LocalTime)+1, 0))))     
        BEGIN
            SELECT @LocalTime = @LocalTime 
        END 
        ELSE
        BEGIN
            SELECT @LocalTime = DATEADD(HOUR, 1, @LocalTime)
        END
    
        RETURN @LocalTime
    
    END
    
    GO
    
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    -- =============================================
    -- Author:      Vivi Woolford
    -- Create date: 27-09-2016
    -- Description: This procedure only Works in New Zealand
    -- =============================================
    ALTER FUNCTION [dbo].[udf_GetUTCFromLocalTime]
    (   
        @LocalTime DATETIME
    )
    RETURNS DATETIME
    AS
    BEGIN
        --Daylight Saving commences on the last Sunday in September, when 2.00am becomes 3.00am. 
        --It ends on the first Sunday in April, when 3.00am becomes 2.00am.
        DECLARE @UTCTime DATETIME   
        DECLARE @OffSet INT = 12
    
        IF @LocalTime BETWEEN 
        /*FINISH DAY LIGHT SAVINGS*/
        DATEADD(HOUR, 2, DATEADD(dd, (6-(DATEDIFF(dd,0,DATEADD(mm,(YEAR(@LocalTime)-1900) * 12 + 3,0))%7)),DATEADD(mm,(YEAR(@LocalTime)-1900) * 12 + 3,0)))
        AND     
        /*START DAY LIGHT SAVINGS*/
        DATEADD(HOUR, 2, DATEADD(dd, -1*(DATEPART(dw, DateAdd(day, -1, DateAdd(month, DateDiff(month, 0, @LocalTime)+1, 0)))-1),DateAdd(day, -1, DateAdd(month, DateDiff(month, 0, @LocalTime)+1, 0))))
        BEGIN
            SELECT @UTCTime = DATEADD(HOUR, -@OffSet, @LocalTime)
        END 
        ELSE 
        BEGIN
            SELECT @UTCTime = DATEADD(HOUR, -1-@OffSet, @LocalTime)
        END
    
        RETURN @UTCTime
    
    END
    go