2009-08-11 268 views
3

我正在使用UTC將數據和時間值存儲在數據庫中。這些值在客戶端或每個客戶端時區轉換爲本地時間。我在MSDN article上加入了這些場景,其中顯示UTC時間似乎在夏令時期間存在問題。UTC和夏令時方案

有人在某值 流落的 美國類型的東海岸,如「2003年10月26日上午一時10分00秒」。

1)在這個特別的早由於 夏令,在2:00 AM,所述 本地時鐘被複位到1:00 AM, 創建25個小時的一天。自1:00 AM 和2:00 AM之間的時鐘時間都 值上 特別的早晨 - 至少在大多數 美國和加拿大, 電腦真的沒有辦法出現兩次知道 其中1 :上午10點是指 發生在交換機之前,或者 發生在夏令時切換後的10分鐘後。

2)類似地,問題發生在 春天,在某個特定的早上,不存在上午2點10分0135秒。原因是在凌晨2點, 特定的早上,當地的 時鐘突然變爲凌晨3點。 在這23小時的日子裏,整個2:00小時從未發生過 。

你是怎麼處理情況#1的,當你可能有4筆交易,2個交換機之前,2個交換機在夏令時之後?如何爲交易顯示用戶的時間,因爲最後兩筆交易可能比前兩筆交易顯示更早的時間。有時,例如:在郵件鏈中可能證明不合邏輯。

新增:

要添加關於上下文的詳細信息,在RIA應用程序,如客戶端的Silverlight /閃存運行(或任何客戶端應用程序交談,通過Webservice的服務器),允許用戶選擇交付或安排的時間當地電腦的時間。

如果我可以檢查給定的輸入時間爲無效時間,我可能會提醒用戶。此外,對於旅行者而言,需要在時間點找到時區,而不是基於用戶選擇,因爲它們可能在區域之間移動,並將時區保存在用戶配置文件中將無濟於事。

用於評估輸入時間的一些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); 

回答

4

這些場景都提倡使用DST的情況。無論你在顯示只要你商店排序 UTC的值。也就是說,如果您正確使用UTC,則會解決這些情況中出現的問題。

是的,看到像這樣的記錄會很混亂:12:30,1:20,1:10,3:30但如果這是按照UTC(真正發生的事情)排序的,我認爲這就是正確的方式來做到這一點。

SO通過記錄UTC一切,然後顯示它在所有UTC或相對時間(如「17分鐘前...」),完全避免了這個問題。


如果你指的是用戶提供的日期/時間作爲評論的建議,我對你有一些壞消息:它吮吸。我認爲最好的,最明顯的解決方案是選擇一條規則並運行。如果你真的需要完美地處理它,你的用戶界面將需要擴展到迂迴處理每年僅發生1小時的邊緣案例,然後僅適用於未實時創建的交易(因爲如果它們是真實的時間,你會知道DST等價物)。

+0

問題是問如何做,如果你沒有使用SqlServer的2008年,您可以使用往返格式存儲日期爲字符串您處理用戶在DST期間輸入的日期。將它們存儲爲UTC很容易,但是當你甚至不確定它應該是什麼時間時,如何轉換爲UTC? – 2009-08-11 17:06:51

+0

啊我明白了。據推測,這是針對一個斷開連接的應用程序,那麼,服務器時間無益? – 2009-08-11 17:07:48

+0

獲取服務器時間並將其存儲爲UTC很容易。所呈現的場景詢問用戶輸入的日期,這是一個完全不同的蠕蟲罐。 – 2009-08-11 17:12:34

1

如何顯示交易的用戶時間,因爲最後兩筆交易可能比前兩筆交易由於輪班而顯示更早的時間。有時,例如:在郵件鏈中可能證明不合邏輯。

您可以按照UTC和本地時間顯示。

因此,用戶可能會看到這樣的名單:

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

要麼,或顯示他們通過UTC排序。

1

如果你有別人手工輸入數據,運氣好的話,除非他們的UTC時間進入它。沒有真正的「正確」方式來處理這個問題。如果您處理的是非用戶輸入的日期,例如交易發生的時間,那麼將這些全部存儲爲UTC並且生活狀態良好。 :)

0

SQL Server有一個新的類型,「DATETIMEOFFSET」同時處理這口井,因爲你可以同時與不同的偏移。對於我的SQL Server 2005數據庫,我使用這種類型的字符串字面值,形式爲「YYYY-MM-DD hh:mm:ss-hh:mm」的nvarchar(25)。

爲此,我已經創建程序將這些字符串轉換爲正確的時間。

1

有兩個部分你的問題一般:

  1. 接收用戶輸入
  2. 顯示數據,用戶

兩個同樣應該處理,但是在某些方面有幾分單獨的解決方法。至於1,最簡單的選擇是確定您的企業是否真的需要一個明確的方式來指定特定時間的時間。很多應用程序只是簡單地忽略它(最後一次使用的日期選擇器是否包含它)?並且簡單地假設任何一致的猜測算法就足夠了。如果輸入不存在的小時數,則應該提供安全措施(即發出錯誤)。

至於2,跳過的時間沒關係,因爲你的數據庫是UTC。重複的時間可能會讓人困惑,特別是在時間戳的蹤跡中。如果值得,請考慮使用包含對夏令時偏移量的引用的時區標識符格式化日期字符串。大多數舊式,即非Olson,時區名稱包括這個(EST與EDT,GMT與BST等)。這將足以用於消除中的瑕疵。這可能只是你所需要的,因爲這種邊界情況可能不值得把顯示器弄得太多。如果您需要更多輸出,您還可以使用UTC偏移量來格式化時間戳,這將使時間戳跟蹤中的轉換非常明確。

2

您需要存儲時間偏移量。

目前在東海岸的時間(往返格式)

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 

爲了處理偏移量,從2.0sp1開始的.NET提供了DateTimeOffset類型。 SqlServer 2008還提供數據類型DateTimeOffset,它將幫助您存儲該值。

DateTime.Now.ToString("o") 
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, [email protected], @LocalTime) 
    END 
    ELSE 
    BEGIN 
     SELECT @UTCTime = DATEADD(HOUR, [email protected], @LocalTime) 
    END 

    RETURN @UTCTime 

END 
go