我使用 UTC 在数据库中存储数据和时间值。这些值将转换为客户端上的本地时间或每个客户端时区。我从这些场景中踩过 MSDN 文章, ,在夏令时期间显示 UTC 时间似乎会带来问题。

居住在美国东海岸的人类型为“ 2003年10月26日01:10:00 AM”。

1)在这个特定的早晨,由于日光节省,在凌晨2:00,当地时钟被重置为1:00 AM,创建了25小时的一天。由于至少在美国和加拿大的大多数早晨,在1:00 am到2:00之间的所有时钟时间值都发生了两次,因此计算机确实无法知道哪个是1:10 AM的意思 - 在开关之前发生的一次,或在日光节省时间切换后10分钟发生的一次。

2)类似地,问题发生在春天,在特定的早晨,没有凌晨2:10的时间。原因是在那个特定早晨的2:00,当地时钟上的时间突然变为3:00 am。整个2:00小时在这23小时的一天从未发生。

您如何处理第 1 种情况,即您可能有 4 笔交易,其中两笔在转换之前,两笔在夏令时转换之后?如何向用户显示交易时间,因为由于轮班,最后两笔交易的显示时间可能早于前两笔交易。有时,它可能被证明是不合逻辑的,例如:在邮件链中。

添加:

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

解决方案

这些场景是提倡使用DST的箱子。这不要紧,你是什么的显示的,只要你的商店的和UTC的排序的值。也就是说,如果您使用UTC得当,在这些情况下出现的问题都解决了。

是的,这将会引起混乱看到这样的记录:12:30,1:20,1:10,3:30,但如果这就是他们如何根据UTC(到底发生了什么)下令,我认为这是正确的方式做到这一点。

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


如果你指的是用户提供的日期/时间作为评论的建议,我有一些坏消息要告诉你:它吮吸。我认为最好的,最明显的解决办法是选择一个规则,并运行它。如果你真的需要完全处理它,你的用户界面将需要扩大到迂腐地处理发生,每年仅1小时,然后才创建交易这种边缘情况未在实时(因为如果他们是真正的 - 时间,你会知道的DST当量)。

其他提示

您需要存储时间偏移量。

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

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

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

10月26日凌晨1点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")
  

如何显示的时间给用户的交易,最后两个交易可能出现较早的时间比通过换档前两个交易?有时,它可以为例如证明不合逻辑:在邮件链

通过UTC对它们进行排序,并在本地时间显示它们。

因此,用户可能会看到这样的名单:

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

要么,或显示通过UTC对它们进行排序。

如果你有别人手工输入数据,运气好的话,除非他们的UTC时间进入它。是不是真的有一个“正确”的方式来处理。如果你正在处理的不是用户输入,就像一个交易发生的时间日期,只存储那些所有为UTC和生活是美好的。 :)

您的问题一般分为两部分:

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

两者应该以类似的方式处理,但在某些方面有一些不同的解决方法。对于 1,最简单的选择是查明您的企业是否确实需要一种明确的方式来指定特定时间的时间。许多应用程序只是忽略它(您最后一次使用具有此功能的日期选择器是什么时候?)并简单地假设任何一致的猜测算法就足够了。您应该提供保障(即如果输入不存在的小时,则会抛出错误。

至于 2,跳过的小时并不重要,因为您的数据库采用 UTC 格式。重复时间可能会令人困惑,尤其是在时间戳的踪迹中。如果值得,请考虑使用包含夏令时偏移量引用的时区标识符来格式化日期字符串。最旧的风格,即非 Olson,时区名称包括此内容(EST 与 EDT、GMT 与 BST 等)。这足以消除歧义 紧要关头. 。这可能就是您所需要的,因为这种边界情况可能不值得过多地破坏显示。如果您需要更多输出,您还可以使用 UTC 偏移量格式化时间戳,这将使时间戳跟踪中的转换非常明确。

SQL Server有一个新的类型,“DATETIMEOFFSET”同时处理这口井,因为你可以同时与不同的偏移。对于我的SQL Server 2005 DB I使用一个字符串这种类型的,形式为nvarchar(25) “YYYY-MM-DD HH:MM:SS-HH:MM”。

有关这一点,我已经创建程序将这些字符串转换为正确的时间。

我这解决了NZ时间如下:

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
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top