.net UTC和夏令时场景
我使用UTC在数据库中存储数据和时间值。这些值在客户端或每个客户端时区上转换为localtime。我从中了解了这些场景,在夏令时显示UTC的时间似乎会带来问题 住在美国东海岸的人 美国输入一个值 比如“2003年10月26日凌晨01:10:00” 1) 由于 夏令时,凌晨2:00 本地时钟重置为凌晨1:00, 创造一个25小时的工作日。既然 凌晨1:00之间的时钟时间值 凌晨2点在那一天发生两次 至少在大部分时间里是这样的早晨 美国和加拿大 计算机真的没有办法知道 上午1点10分是指 在开关之前发生,或 这发生在事故发生10分钟后 夏令时开关 2) 类似地,问题发生在 春天的时候,在某个特定的季节 早上好,没有2:10这样的时间 是原因是那天下午2点 特别是早上,时间就在当地 时钟突然变为凌晨3点。 整个2:00小时都不会发生在 这是一天23小时 您是如何处理这种情况的#1,当您在夏令时可能有4笔交易,两笔在切换前,两笔在切换后?如何向用户显示事务的时间,因为由于班次的原因,最后两个事务显示的时间可能早于前两个事务。?有时,它可能被证明是不合逻辑的,例如:在邮件链中 增加: 要添加有关上下文的更多信息,客户端上运行的Silverlight/Flash等RIA应用程序(或通过Web服务与服务器对话的任何客户端应用程序)允许用户选择交付时间或使用pc本地时间安排 如果我可以检查给定的输入时间是否为无效时间,我可能会提醒用户。此外,对于旅行者来说,时区需要在时间点找到,而不是基于用户选择,因为他们可能在区域之间移动,而在用户配置文件中保存他们的时区也没有帮助 一些用于评估输入时间的C#测试样本:.net UTC和夏令时场景,.net,datetime,time,timezone,dst,.net,Datetime,Time,Timezone,Dst,我使用UTC在数据库中存储数据和时间值。这些值在客户端或每个客户端时区上转换为localtime。我从中了解了这些场景,在夏令时显示UTC的时间似乎会带来问题 住在美国东海岸的人 美国输入一个值 比如“2003年10月26日凌晨01:10:00” 1) 由于 夏令时,凌晨2:00 本地时钟重置为凌晨1:00, 创造一个25小时的工作日。既然 凌晨1:00之间的时钟时间值 凌晨2点在那一天发生两次 至少在大部分时间里是这样的早晨 美国和加拿大 计算机真的没有办法知道 上午1点10分是指 在开关之前
//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(实际发生的情况)进行排序的方式,我认为这是正确的方式 因此,通过在UTC中记录所有内容,然后在UTC或相对时间(如“17分钟前…”)中显示所有内容,可以完全避免此问题
如果你指的是评论中建议的用户提供的日期/时间,我有一些坏消息要告诉你:它糟透了。我认为最好、最明显的解决办法是选择一条规则并执行它。如果您真的需要完美地处理它,您的UI将需要扩展到学究式地处理这种每年仅发生1小时的边缘情况,然后只处理非实时创建的事务(因为如果它们是实时的,您将知道DST等价物) 如何向用户显示事务的时间,因为由于班次的原因,最后两个事务显示的时间可能早于前两个事务。?有时,它可能被证明是不合逻辑的,例如:在邮件链中 按UTC排序,并在本地时间显示 因此,用户可能会看到如下列表:
01:10:00
01:50:00
01:05:00
01:20:00
或者按UTC显示和排序数据。如果您有人手动输入数据,祝您好运,除非他们在UTC时间输入数据。没有一个“正确”的方法来处理这个问题。如果您处理的日期不是用户输入的,比如交易发生的时间,只需将这些日期全部存储为UTC,生活就好了。:) SQL Server有一个新类型“datetimeoffset”,它可以很好地处理这个问题,因为您可以使用不同的偏移量来处理相同的时间。对于我的SQLServer2005DB,我使用该类型的字符串文字,形式为“YYYY-MM-DD hh:MM:ss hh:MM”的nvarchar(25)
为此,我创建了一些例程,将这些字符串转换为正确的时间。一般来说,问题分为两部分:
至于2,跳过的小时并不重要,因为您的数据库是UTC。重复一小时可能会让人困惑,特别是在一系列时间戳中。如果值得的话,考虑用时区标识符格式化日期字符串,其中包含对夏时制偏移量的引用。大多数老式的时区名称(即非奥尔森时区)都包含此名称(EST与EDT、GMT与BST等)。这将足以在必要时消除歧义。这可能就是您所需要的,因为这种边界情况可能不值得在显示上做太多的麻烦。如果您需要更多的输出,还可以使用UTC偏移量格式化时间戳,这将使转换在时间戳序列中非常明确。您需要存储时间偏移量 目前东海岸的时间为(往返格式) 即使东海岸被认为是-5,在夏时制期间,时间也将是-4 10月26日凌晨1:10,时间为
2009-10-26T1:10:00.0000000-04:00
但是,当时钟超过02:00,我们切换回正常时间时,您的时间将是
2009-10-26T1:10:00.0000000-05:00
为了
2009-10-26T1:10:00.0000000-05:00
DateTime.Now.ToString("o")
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