UTC和夏令时方案

时间:2009-08-11 16:59:07

标签: .net datetime time timezone dst

我使用UTC在数据库中存储数据和时间值。这些值在客户端或每个客户端时区转换为本地时间。我从MSDN article开始采用这些方案,显示UTC时间似乎在夏令时造成问题。

  生活在东海岸的人   美国输入一个值   比如“Oct 26,2003 01:10:00 AM”。

     

1)在这个特殊的早晨,由于   夏令时,凌晨2点,   本地时钟重置为凌晨1点,   创造一个25小时的一天。一切都好   时钟时间值在凌晨1点之间   上午2点发生两次   特别是早上 - 至少在大部分时间   美国和加拿大   电脑真的没办法知道   上午1:10的意思是 - 那个   在切换之前或者切换之前发生   发生在10分钟后   夏令时开关。

     

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

你是如何处理情况#1的,当你可能有4个交易,两个交换前和两个交换后的夏令时?如何向用户显示交易的时间,因为最后两个交易可能由于转移而显示比前两个交易更早的时间。有时,它可能证明是不合逻辑的,例如:在邮件链中。

增加:

要添加有关上下文的更多信息,在客户端(或通过Webservice与服务器通信的任何客户端应用程序)上运行的RIA应用程序(如Silverlight / Flash)允许用户选择交付时间或使用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 个答案:

答案 0 :(得分:3)

这些情景是倡导使用DST的案例。只要您以UTC 存储排序值,您显示的内容无关紧要。也就是说,如果您正确使用UTC,那么这些场景中出现的问题就会得到解决。

是的,看到这样的记录会很困惑:12:30,1:20,1:10,3:30但如果这是按照UTC(真正发生的事情)订购的方式,我认为这是正确的方法。

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


如果您在评论中提到用户提供的日期/时间,我会有一些坏消息:它很糟糕。我认为最好,最明显的解决方案是选择规则并运行它。如果你确实需要完美地处理它,你的UI需要扩展到迂腐地处理这种边缘情况,每年只发生1小时,然后只对非实时创建的交易(因为如果它们是真实的) - 时间,你知道DST等价物。)

答案 1 :(得分:3)

您需要存储时间偏移。

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

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。 Microsoft SQL Server 2008还提供了可帮助您存储该值的数据类型datetimeoffset。如果您不使用Microsoft SQL Server 2008,则可以使用往返格式将日期存储为字符串:

DateTime.Now.ToString("o")

答案 2 :(得分:1)

  

如何向用户显示交易的时间,因为最后两个交易可能会显示比前两个交易更早的时间。有时,它可能证明是不合逻辑的,例如:在邮件链中。

按UTC排序,并以当地时间显示。

因此用户可能会看到如下列表:

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

或者显示按UTC排序。

答案 3 :(得分:1)

如果你有人手动输入数据,祝你好运,除非他们在UTC时间输入数据。没有一种“正确”的方法可以解决这个问题。如果您正在处理非用户输入的日期,例如交易发生的时间,那么只需将这些日期存储为UTC并且生活良好。 :)

答案 4 :(得分:1)

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

  1. 接收用户输入
  2. 向用户显示数据
  3. 这两者应该以类似的方式处理,但在某些方面有一些单独的解决方法。对于1,最简单的选择是找出您的企业是否真的需要一种明确的方式来指定该特定小时的时间。许多应用程序只是忽略它(你最后一次使用日期选择器的时候是什么时候?)并简单地假设任何一致的猜测算法就足够了。如果输入不存在的小时,您应该提供保护措施(即抛出错误)。

    对于2,跳过的小时无关紧要,因为您的数据库是UTC。重复小时可能会令人困惑,特别是在时间戳的痕迹中。如果值得,请考虑使用时区标识符格式化日期字符串,该标识符包含对夏令时偏移量的引用。大多数旧式,即非Olson,时区名称包括此(EST与EDT,GMT与BST等)。这足以消除捏合中的。这可能就是你所需要的,因为这个边界情况可能不值得过多地显示。如果您需要更多输出,您还可以使用UTC偏移量格式化时间戳,这将使得时间戳跟踪中的转换非常明确。

答案 5 :(得分:0)

SQL Server有一个新类型'datetimeoffset'可以很好地处理这个问题,因为你可以在不同的偏移量下使用相同的时间。对于我的SQL Server 2005 DB,我使用该类型的字符串文字,nvarchar(25)格式为“YYYY-MM-DD hh:mm:ss-hh:mm”。

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

答案 6 :(得分:0)

我已经解决了新西兰时间如下:

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