Share via


在 DateTime、DateTimeOffset 和 TimeZoneInfo 之間選擇

更新:2007 年 11 月

使用日期和時間資訊的 .NET Framework 應用程式有很多種,而且可以利用幾種不同方式使用該資訊。日期和時間資訊的常見使用方式包括下列一項或多項:

  • 只反映日期,因此時間資訊不重要。

  • 只反映時間,因此日期資訊不重要。

  • 反映絕對日期和時間,該日期和時間不屬於特定時間和地點 (例如,跨國連鎖的大多數商店都在星期一至五早上 9:00 開門營業)。

  • 從 .NET Framework 以外的來源擷取日期和時間資訊,在這些來源中,日期和時間資訊通常是以簡單的資料型別儲存。

  • 以唯一且明確的方式識別單一時間點。有些應用程式只需要主機系統上有明確的日期和時間,有些則需要所有系統都有明確的日期和時間 (也就是說,在某一系統上序列化的日期,可在全球任何一處的另一個系統上有意義地還原序列化和使用)。

  • 保留多個相關時間 (例如要求者的本地時間以及伺服器收到 Web 要求的時間)。

  • 執行日期和時間運算,可能是對唯一且明確識別單一時間點的結果執行。

.NET Framework 包含 DateTimeDateTimeOffsetTimeZoneInfo 型別,這些型別都可用來建置使用日期和時間的應用程式。

注意事項:

本主題不討論第四個型別 TimeZone,因為它的功能幾乎已完全整合到 TimeZoneInfo 類別。開發人員應盡可能使用 TimeZoneInfo 類別,而不要使用 TimeZone 類別。

DateTime 結構

定義特定日期和時間的 DateTime 值。從 .NET Framework 2.0 版開始,此結構加入了 Kind 屬性,此屬性可對日期和時間所屬的時區提供有限的資訊。Kind 屬性傳回的 DateTimeKind 值,會指出 DateTime 值表示的是本地時間 (DateTimeKind.Local)、Coordinated Universal Time (UTC) (DateTimeKind.Utc) 或未指定的時間 (DateTimeKind.Unspecified)。

DateTime 結構適用於執行下列作業的應用程式:

  • 只使用日期。

  • 只使用時間。

  • 使用絕對日期和時間。

  • 從 .NET Framework 以外的來源 (例如 SQL 資料庫) 擷取日期和時間資訊。一般來說,這些來源會使用與 DateTime 結構相容的簡單格式儲存日期和時間資訊。

  • 執行日期和時間運算,但通常與運算結果相關。例如,在將六個月加到特定日期和時間的加法運算中,結果是否調整為日光節約時間通常並不重要。

除非特定 DateTime 值表示的是 UTC,否則該日期和時間值通常是模稜兩可或可攜性有限。例如,如果 DateTime 值表示本地時間,則在該本地時區內具可攜性 (也就是說,如果此值在同時區的另一個系統上還原序列化,則仍可明確識別某個單一時間點)。在本地時區以外,該 DateTime 值就可能有多種解讀。如果值的 Kind 屬性為 DateTimeKind.Unspecified,那麼可攜性就更低:現在它在同時區內會是模稜兩可的,甚至可能在最初序列化的同一個系統上也是如此。只有在 DateTime 值表示 UTC 的情況下,該值才能在明確識別單一時間點,不論使用它的系統或時區為何。

重要事項:

儲存或共用 DateTime 資料時,應該使用 UTC,並將 DateTime 值的 Kind 屬性設為 DateTimeKindUTC()。

DateTimeOffset 結構

DateTimeOffset 結構表示日期和時間值,以及指出該值與 UTC 時差的位移。因此,此值永遠會以明確方式識別單一時間點。

雖然 DateTimeOffset 型別包含 DateTime 型別的大部分功能,但它並不是要用來在應用程式開發取代 DateTime 型別。反之,它適合用於執行下列作業的應用程式:

  • 以唯一且明確的方式識別單一時間點。DateTimeOffset 型別可用來明確定義「現在」的意義,記錄交易時間、記錄系統或應用程式事件的時間,以及記錄檔案建立和修改的時間。

  • 執行一般日期和時間運算。

  • 保留多個相關時間,但前提是這些時間儲存成兩個不同的值,或是結構的兩個成員。

注意事項:

以上使用 DateTimeOffset 值的情形要比使用 DateTime 值常見許多。因此,在進行應用程式開發時,請考慮使用 DateTimeOffset 做為預設日期和時間型別。

DateTimeOffset 值不屬於特定的時區,但可能來自各個不同的時區。為說明此點,下列範例列出了數個 DateTimeOffset 值 (包括本地太平洋標準時間) 可能屬於的時區。

Imports System.Collections.ObjectModel

Module TimeOffsets
   Public Sub Main()
      Dim thisTime As DateTimeOffset 

      thisTime = New DateTimeOffset(#06/10/2007#, New TimeSpan(-7, 0, 0))
      ShowPossibleTimeZones(thisTime) 

      thisTime = New DateTimeOffset(#03/10/2007#, New TimeSpan(-6, 0, 0))  
      ShowPossibleTimeZones(thisTime)

      thisTime = New DateTimeOffset(#03/10/2007#, New TimeSpan(+1, 0, 0))
      ShowPossibleTimeZones(thisTime)
   End Sub

   Private Sub ShowPossibleTimeZones(offsetTime As DateTimeOffset)
      Dim offset As TimeSpan = offsetTime.Offset
      Dim timeZones As ReadOnlyCollection(Of TimeZoneInfo)

      Console.WriteLine("{0} could belong to the following time zones:", _
                        offsetTime.ToString())
      ' Get all time zones defined on local system
      timeZones = TimeZoneInfo.GetSystemTimeZones()     
      ' Iterate time zones
      For Each timeZone As TimeZoneInfo In timeZones
         ' Compare offset with offset for that date in that time zone
         If timeZone.GetUtcOffset(offsetTime.DateTime).Equals(offset) Then
            Console.WriteLine("   {0}", timeZone.DisplayName)
         End If   
      Next
      Console.WriteLine()
   End Sub
End Module
' This example displays the following output to the console:
'       6/10/2007 12:00:00 AM -07:00 could belong to the following time zones:
'          (GMT-07:00) Arizona
'          (GMT-08:00) Pacific Time (US & Canada)
'          (GMT-08:00) Tijuana, Baja California
'       
'       3/10/2007 12:00:00 AM -06:00 could belong to the following time zones:
'          (GMT-06:00) Central America
'          (GMT-06:00) Central Time (US & Canada)
'          (GMT-06:00) Guadalajara, Mexico City, Monterrey - New
'          (GMT-06:00) Guadalajara, Mexico City, Monterrey - Old
'          (GMT-06:00) Saskatchewan
'       
'       3/10/2007 12:00:00 AM +01:00 could belong to the following time zones:
'          (GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna
'          (GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague
'          (GMT+01:00) Brussels, Copenhagen, Madrid, Paris
'          (GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb
'          (GMT+01:00) West Central Africa
using System;
using System.Collections.ObjectModel;

public class TimeOffsets
{
   public static void Main()
   {
      DateTime thisDate = new DateTime(2007, 3, 10, 0, 0, 0);
      DateTime dstDate = new DateTime(2007, 6, 10, 0, 0, 0);
      DateTimeOffset thisTime;

      thisTime = new DateTimeOffset(dstDate, new TimeSpan(-7, 0, 0));
      ShowPossibleTimeZones(thisTime);

      thisTime = new DateTimeOffset(thisDate, new TimeSpan(-6, 0, 0));  
      ShowPossibleTimeZones(thisTime);

      thisTime = new DateTimeOffset(thisDate, new TimeSpan(+1, 0, 0));
      ShowPossibleTimeZones(thisTime);
   }

   private static void ShowPossibleTimeZones(DateTimeOffset offsetTime)
   {
      TimeSpan offset = offsetTime.Offset;
      ReadOnlyCollection<TimeZoneInfo> timeZones;

      Console.WriteLine("{0} could belong to the following time zones:", 
                        offsetTime.ToString());
      // Get all time zones defined on local system
      timeZones = TimeZoneInfo.GetSystemTimeZones();     
      // Iterate time zones 
      foreach (TimeZoneInfo timeZone in timeZones)
      {
         // Compare offset with offset for that date in that time zone
         if (timeZone.GetUtcOffset(offsetTime.DateTime).Equals(offset))
            Console.WriteLine("   {0}", timeZone.DisplayName);
      }
      Console.WriteLine();
   } 
}
// This example displays the following output to the console:
//       6/10/2007 12:00:00 AM -07:00 could belong to the following time zones:
//          (GMT-07:00) Arizona
//          (GMT-08:00) Pacific Time (US & Canada)
//          (GMT-08:00) Tijuana, Baja California
//       
//       3/10/2007 12:00:00 AM -06:00 could belong to the following time zones:
//          (GMT-06:00) Central America
//          (GMT-06:00) Central Time (US & Canada)
//          (GMT-06:00) Guadalajara, Mexico City, Monterrey - New
//          (GMT-06:00) Guadalajara, Mexico City, Monterrey - Old
//          (GMT-06:00) Saskatchewan
//       
//       3/10/2007 12:00:00 AM +01:00 could belong to the following time zones:
//          (GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna
//          (GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague
//          (GMT+01:00) Brussels, Copenhagen, Madrid, Paris
//          (GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb
//          (GMT+01:00) West Central Africa

從輸出可以看到,這個範例中的每個日期和時間值都可能屬於至少三個不同的時區。DateTimeOffset 值 6/10/2007 顯示,如果日期和時間值表示的是日光節約時間,它與 UTC 之間的位移甚至不一定相當於原始時區的基礎 UTC 位移,或是與其顯示名稱找到的 UTC 之間的位移。這表示,由於單一 DateTimeOffset 值並未與其時區緊密連結,因此無法反映出時區與日光節約時間的來回轉換。當使用日期和時間計算 DateTimeOffset 值的時候,問題就更為明顯 (如需如何在執行日期和時間運算時,將時區的調整規則列入考量的討論,請參閱使用日期和時間執行算術運算)。

TimeZoneInfo 類別

TimeZoneInfo 類別可表示全球任何時區,並可將某一時區的任何日期和時間轉換成另一個時區的日期和時間。TimeZoneInfo 類別可在使用日期和時間時,讓任何日期和時間值都明確識別某個單一時間點。TimeZoneInfo 類別也是可以延伸的。雖然它依賴提供給 Windows 系統以及定義在登錄中的時間資訊,但也支援建立自訂時區。此外也支援時區資訊的序列化和還原序列化。

在某些情況下,要完整利用 TimeZoneInfo 類別,可能需要額外的開發工作。首先,日期和時間值並未與其所屬時區緊密結合。因此,除非應用程式提供機制來將日期和時間與其相關時區連結,否則特定日期和時間值就很容易切斷與其時區的關聯 (連結此資訊的其中一個方法是定義類別或結構,其中同時包含日期和時間值以及其相關的時區物件)。其次,Windows XP 和更早的 Windows 版本並沒有針對歷史時區資訊提供支援,而 Windows Vista 的支援則有限。設計處理歷史日期和時間的應用程式勢必要使用大量的自訂時區。

要利用 .NET Framework 中的時區支援,就必須在具現化日期和時間物件時,知道日期和時間值屬於哪一個時區。但通常情形並非如此,尤其是在 Web 或網路應用程式中。

請參閱

其他資源

時間和時區