Wybieranie między wartościami DateTime, DateOnly, DateTimeOffset, TimeSpan, TimeOnly i TimeZoneInfo

Aplikacje .NET mogą używać informacji o dacie i godzinie na kilka sposobów. Bardziej typowe zastosowania informacji o dacie i godzinie to:

  • Aby odzwierciedlić tylko datę, aby informacje o godzinie nie są ważne.
  • Aby odzwierciedlić tylko godzinę, aby informacje o dacie nie są ważne.
  • Aby odzwierciedlić abstrakcyjną datę i godzinę, która nie jest powiązana z określonym czasem i miejscem (na przykład większość sklepów w sieci międzynarodowej jest otwarta w dni powszednie o godzinie 9:00).
  • Aby pobrać informacje o dacie i godzinie ze źródeł spoza platformy .NET, zwykle miejsce przechowywania informacji o dacie i godzinie w prostym typie danych.
  • Aby jednoznacznie i jednoznacznie zidentyfikować pojedynczy punkt w czasie. Niektóre aplikacje wymagają, aby data i godzina był jednoznaczny tylko w systemie hosta. Inne aplikacje wymagają, aby była jednoznaczna w różnych systemach (oznacza to, że data serializowana w jednym systemie może być znacząco deserializowana i używana w innym systemie w dowolnym miejscu na świecie).
  • Aby zachować wiele powiązanych czasów (na przykład czasu lokalnego obiektu żądającego i czasu otrzymania żądania internetowego przez serwer).
  • Aby wykonać arytmetyczną datę i godzinę, prawdopodobnie z wynikiem, który jednoznacznie i jednoznacznie identyfikuje pojedynczy punkt w czasie.

Platforma .NET zawiera DateTimetypy , , TimeSpanDateOnlyDateTimeOffset, TimeOnly, i TimeZoneInfo , które mogą służyć do tworzenia aplikacji, które działają z datami i godzinami.

Uwaga

W tym artykule nie omówiono TimeZone , ponieważ jej funkcjonalność jest prawie całkowicie włączona TimeZoneInfo do klasy. Jeśli to możliwe, użyj TimeZoneInfo klasy zamiast TimeZone klasy .

Struktura DateTimeOffset

Struktura DateTimeOffset reprezentuje wartość daty i godziny wraz z przesunięciem wskazującym, ile ta wartość różni się od czasu UTC. W związku z tym wartość zawsze jednoznacznie identyfikuje pojedynczy punkt w czasie.

Typ DateTimeOffset zawiera wszystkie funkcje DateTime typu wraz z rozpoznawaniem strefy czasowej. To sprawia, że nadaje się do aplikacji, które:

  • Jednoznacznie i jednoznacznie identyfikuje pojedynczy punkt w czasie. Typ DateTimeOffset może służyć do jednoznacznego zdefiniowania znaczenia "teraz", rejestrowania czasów transakcji, rejestrowania czasów systemu lub zdarzeń aplikacji oraz rejestrowania czasu tworzenia i modyfikowania plików.
  • Wykonaj arytmetyczną ogólną datę i godzinę.
  • Zachowaj wiele powiązanych czasów, o ile te czasy są przechowywane jako dwie oddzielne wartości lub dwa elementy członkowskie struktury.

Uwaga

Te zastosowania dla DateTimeOffset wartości są znacznie bardziej typowe niż dla DateTime wartości. W związku z tym należy wziąć pod uwagę DateTimeOffset domyślny typ daty i godziny tworzenia aplikacji.

DateTimeOffset Wartość nie jest powiązana z określoną strefą czasową, ale może pochodzić z różnych stref czasowych. W poniższym przykładzie wymieniono strefy czasowe, do których może należeć liczba DateTimeOffset wartości (w tym lokalny czas standardowy pacyficzny).

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
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

Dane wyjściowe pokazują, że każda wartość daty i godziny w tym przykładzie może należeć do co najmniej trzech różnych stref czasowych. Wartość DateTimeOffset 6/10/2007 pokazuje, że jeśli wartość daty i godziny reprezentuje czas letni, jego przesunięcie z UTC nie musi nawet odpowiadać podstawowemu przesunięcie utc źródłowej strefy czasowej lub przesunięcia z czasu UTC znalezionego w nazwie wyświetlanej. Ponieważ pojedyncza DateTimeOffset wartość nie jest ściśle połączona ze strefą czasową, nie może odzwierciedlać przejścia strefy czasowej do i z czasu letniego. Może to być problematyczne, gdy arytmetyka daty i godziny jest używana do manipulowania wartością DateTimeOffset . Aby zapoznać się z omówieniem sposobu wykonywania arytmetyki daty i godziny w sposób uwzględniający reguły korekty strefy czasowej, zobacz Wykonywanie operacji arytmetycznych z datami i godzinami.

Struktura DateTime

Wartość DateTime definiuje określoną datę i godzinę. Zawiera Kind właściwość, która zawiera ograniczone informacje o strefie czasowej, do której należy ta data i godzina. Wartość DateTimeKind zwracana przez Kind właściwość wskazuje, czy DateTime wartość reprezentuje czas lokalny (DateTimeKind.Local), uniwersalny czas koordynowany (UTC) (DateTimeKind.Utc) lub nieokreślony czas (DateTimeKind.Unspecified).

Struktura DateTime jest odpowiednia dla aplikacji o co najmniej jednej z następujących cech:

  • Praca z abstrakcyjnymi datami i godzinami.
  • Praca z datami i godzinami, dla których brakuje informacji o strefie czasowej.
  • Praca tylko z datami i godzinami UTC.
  • Wykonaj arytmetyczną datę i godzinę, ale są zaniepokojeni ogólnymi wynikami. Na przykład w operacji dodawania, która dodaje sześć miesięcy do określonej daty i godziny, często nie jest ważne, czy wynik jest dostosowywany do czasu letniego.

Jeśli określona DateTime wartość nie reprezentuje czasu UTC, ta wartość daty i godziny jest często niejednoznaczna lub ograniczona w jego przenośności. Jeśli na przykład DateTime wartość reprezentuje czas lokalny, jest to przenośne w tej lokalnej strefie czasowej (czyli jeśli wartość jest deserializowana w innym systemie w tej samej strefie czasowej, ta wartość nadal jednoznacznie identyfikuje pojedynczy punkt w czasie). Poza lokalną strefą czasową ta DateTime wartość może mieć wiele interpretacji. Jeśli właściwość wartości Kind to DateTimeKind.Unspecified, jest jeszcze mniej przenośna: jest teraz niejednoznaczna w tej samej strefie czasowej, a nawet w tym samym systemie, w którym została po raz pierwszy serializowana. Tylko wtedy, gdy DateTime wartość reprezentuje UTC, ta wartość jednoznacznie identyfikuje pojedynczy punkt w czasie niezależnie od systemu lub strefy czasowej, w której jest używana wartość.

Ważne

Podczas zapisywania lub udostępniania DateTime danych użyj czasu UTC i ustaw DateTime właściwość wartości Kind na DateTimeKind.Utc.

Struktura DateOnly

Struktura DateOnly reprezentuje określoną datę bez godziny. Ponieważ nie ma składnika czasu, reprezentuje datę od początku dnia do końca dnia. Ta struktura jest idealna do przechowywania określonych dat, takich jak data urodzenia, data rocznicy, święto lub data związana z firmą.

Chociaż można użyć DateTime podczas ignorowania składnika czasu, istnieje kilka korzyści związanych z używaniem funkcji DateOnly over DateTime:

  • Struktura DateTime może zostać przerzuciona do poprzedniego lub następnego dnia, jeśli zostanie przesunięta przez strefę czasową. DateOnly nie można zrównoważyć przez strefę czasową i zawsze reprezentuje datę ustawioną.
  • Serializowanie DateTime struktury obejmuje składnik czasu, który może zaciemniać intencję danych. DateOnly Ponadto serializuje mniej danych.
  • Gdy kod wchodzi w interakcję z bazą danych, taką jak program SQL Server, wszystkie daty są zwykle przechowywane jako date typ danych, który nie zawiera czasu. DateOnly pasuje do typu bazy danych lepiej.

Aby uzyskać więcej informacji na temat DateOnlyprogramu , zobacz How to use the DateOnly and TimeOnly structures (Jak używać struktur DateOnly i TimeOnly).

Ważne

DateOnly program nie jest dostępny w programie .NET Framework.

Struktura TimeSpan

Struktura TimeSpan reprezentuje przedział czasu. Są to dwa typowe zastosowania:

  • Emocje przedział czasu między dwiema wartościami daty i godziny. Na przykład odejmowanie jednej DateTime wartości z innej zwraca TimeSpan wartość.
  • Pomiar czasu, który upłynął. Na przykład Stopwatch.Elapsed właściwość zwraca TimeSpan wartość, która odzwierciedla interwał czasu, który upłynął od wywołania jednej z Stopwatch metod, które zaczynają mierzyć czas, który upłynął.

TimeSpan Wartość może być również używana jako zamiana wartościDateTime, gdy ta wartość odzwierciedla godzinę bez odwołowania się do określonego dnia. To użycie jest podobne do DateTime.TimeOfDay właściwości i DateTimeOffset.TimeOfDay , które zwracają wartość reprezentującą TimeSpan godzinę bez odwołania do daty. Na przykład struktura TimeSpan może służyć do odzwierciedlenia dziennego otwarcia lub zamknięcia sklepu lub może służyć do reprezentowania czasu wystąpienia dowolnego zdarzenia regularnego.

W poniższym przykładzie zdefiniowano strukturę zawierającą StoreInfoTimeSpanTimeZoneInfo obiekty do otwierania i zamykania magazynu, a także obiekt reprezentujący strefę czasową magazynu. Struktura obejmuje również dwie metody i IsOpenNowIsOpenAt, które wskazują, czy magazyn jest otwarty w czasie określonym przez użytkownika, który ma znajdować się w lokalnej strefie czasowej.

using System;

public struct StoreInfo
{
   public String store;
   public TimeZoneInfo tz;
   public TimeSpan open;
   public TimeSpan close;

   public bool IsOpenNow()
   {
      return IsOpenAt(DateTime.Now.TimeOfDay);
   }

   public bool IsOpenAt(TimeSpan time)
   {
      TimeZoneInfo local = TimeZoneInfo.Local;
      TimeSpan offset = TimeZoneInfo.Local.BaseUtcOffset;

      // Is the store in the same time zone?
      if (tz.Equals(local)) {
         return time >= open & time <= close;
      }
      else {
         TimeSpan delta = TimeSpan.Zero;
         TimeSpan storeDelta = TimeSpan.Zero;

         // Is it daylight saving time in either time zone?
         if (local.IsDaylightSavingTime(DateTime.Now.Date + time))
            delta = local.GetAdjustmentRules()[local.GetAdjustmentRules().Length - 1].DaylightDelta;

         if (tz.IsDaylightSavingTime(TimeZoneInfo.ConvertTime(DateTime.Now.Date + time, local, tz)))
            storeDelta = tz.GetAdjustmentRules()[tz.GetAdjustmentRules().Length - 1].DaylightDelta;

         TimeSpan comparisonTime = time + (offset - tz.BaseUtcOffset).Negate() + (delta - storeDelta).Negate();
         return comparisonTime >= open & comparisonTime <= close;
      }
   }
}
Public Structure StoreInfo
    Dim store As String
    Dim tz As TimeZoneInfo
    Dim open As TimeSpan
    Dim close As TimeSpan

    Public Function IsOpenNow() As Boolean
        Return IsOpenAt(Date.Now.TimeOfDay)
    End Function

    Public Function IsOpenAt(time As TimeSpan) As Boolean
        Dim local As TimeZoneInfo = TimeZoneInfo.Local
        Dim offset As TimeSpan = TimeZoneInfo.Local.BaseUtcOffset

        ' Is the store in the same time zone?
        If tz.Equals(local) Then
            Return time >= open AndAlso time <= close
        Else
            Dim delta As TimeSpan = TimeSpan.Zero
            Dim storeDelta As TimeSpan = TimeSpan.Zero

            ' Is it daylight saving time in either time zone?
            If local.IsDaylightSavingTime(Date.Now.Date + time) Then
                delta = local.GetAdjustmentRules(local.GetAdjustmentRules().Length - 1).DaylightDelta
            End If
            If tz.IsDaylightSavingTime(TimeZoneInfo.ConvertTime(Date.Now.Date + time, local, tz))
                storeDelta = tz.GetAdjustmentRules(tz.GetAdjustmentRules().Length - 1).DaylightDelta
            End If
            Dim comparisonTime As TimeSpan = time + (offset - tz.BaseUtcOffset).Negate() + (delta - storeDelta).Negate
            Return (comparisonTime >= open AndAlso comparisonTime <= close)
        End If
    End Function
End Structure

Struktura StoreInfo może być następnie używana przez kod klienta, taki jak poniżej.

public class Example
{
   public static void Main()
   {
      // Instantiate a StoreInfo object.
      var store103 = new StoreInfo();
      store103.store = "Store #103";
      store103.tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
      // Store opens at 8:00.
      store103.open = new TimeSpan(8, 0, 0);
      // Store closes at 9:30.
      store103.close = new TimeSpan(21, 30, 0);

      Console.WriteLine("Store is open now at {0}: {1}",
                        DateTime.Now.TimeOfDay, store103.IsOpenNow());
      TimeSpan[] times = { new TimeSpan(8, 0, 0), new TimeSpan(21, 0, 0),
                           new TimeSpan(4, 59, 0), new TimeSpan(18, 31, 0) };
      foreach (var time in times)
         Console.WriteLine("Store is open at {0}: {1}",
                           time, store103.IsOpenAt(time));
   }
}
// The example displays the following output:
//       Store is open now at 15:29:01.6129911: True
//       Store is open at 08:00:00: True
//       Store is open at 21:00:00: False
//       Store is open at 04:59:00: False
//       Store is open at 18:31:00: False
Module Example
    Public Sub Main()
        ' Instantiate a StoreInfo object.
        Dim store103 As New StoreInfo()
        store103.store = "Store #103"
        store103.tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time")
        ' Store opens at 8:00.
        store103.open = new TimeSpan(8, 0, 0)
        ' Store closes at 9:30.
        store103.close = new TimeSpan(21, 30, 0)

        Console.WriteLine("Store is open now at {0}: {1}",
                          Date.Now.TimeOfDay, store103.IsOpenNow())
        Dim times() As TimeSpan = {New TimeSpan(8, 0, 0),
                                    New TimeSpan(21, 0, 0),
                                    New TimeSpan(4, 59, 0),
                                    New TimeSpan(18, 31, 0)}
        For Each time In times
            Console.WriteLine("Store is open at {0}: {1}",
                              time, store103.IsOpenAt(time))
        Next
    End Sub
End Module
' The example displays the following output:
'       Store is open now at 15:29:01.6129911: True
'       Store is open at 08:00:00: True
'       Store is open at 21:00:00: False
'       Store is open at 04:59:00: False
'       Store is open at 18:31:00: False

Struktura TimeOnly

Struktura TimeOnly reprezentuje wartość godziny dnia, taką jak dzienny zegar alarmowy lub godzina jedzenia lunchu każdego dnia. TimeOnlyjest ograniczony do zakresu 00:00:00.0000000 - 23:59:59.99999999, określonej godziny dnia.

Przed wprowadzeniem TimeOnly typu programiści zazwyczaj używali DateTime typu lub TimeSpan typu do reprezentowania określonego czasu. Jednak użycie tych struktur do symulowania godziny bez daty może spowodować pewne problemy, które TimeOnly rozwiązuje:

  • TimeSpan reprezentuje czas, który upłynął, na przykład czas mierzony za pomocą stopera. Górny zakres wynosi ponad 29 000 lat, a jego wartość może być ujemna, aby wskazać przechodzenie do tyłu w czasie. Wartość ujemna TimeSpan nie wskazuje określonej godziny dnia.
  • Jeśli TimeSpan jest używana jako pora dnia, istnieje ryzyko, że może to być manipulowane wartością poza 24-godzinnym dniem. TimeOnly nie ma tego ryzyka. Jeśli na przykład zmiana pracy pracownika rozpoczyna się o godzinie 18:00 i trwa 8 godzin, dodanie 8 godzin do TimeOnly struktury przerzuca się do 2:00.
  • Użycie DateTime przez godzinę dnia wymaga, aby dowolna data została skojarzona z godziną, a następnie zlekceważona. Powszechną praktyką jest wybranie DateTime.MinValue (0001-01-01) jako daty, jednak jeśli godziny są odejmowane od DateTime wartości, OutOfRange może wystąpić wyjątek. TimeOnly nie ma tego problemu, ponieważ czas jest przerzucany do przodu i do tyłu wokół 24-godzinnego przedziału czasu.
  • Serializowanie DateTime struktury obejmuje składnik daty, który może zaciemniać intencję danych. TimeOnly Ponadto serializuje mniej danych.

Aby uzyskać więcej informacji na temat TimeOnlyprogramu , zobacz How to use the DateOnly and TimeOnly structures (Jak używać struktur DateOnly i TimeOnly).

Ważne

TimeOnly program nie jest dostępny w programie .NET Framework.

Klasa TimeZoneInfo

Klasa TimeZoneInfo reprezentuje dowolną strefę czasową Ziemi i umożliwia konwersję dowolnej daty i godziny w jednej strefie czasowej na jej odpowiednik w innej strefie czasowej. Klasa TimeZoneInfo umożliwia pracę z datami i godzinami, dzięki czemu każda wartość daty i godziny jednoznacznie identyfikuje pojedynczy punkt w czasie. Klasa TimeZoneInfo jest również rozszerzalna. Chociaż zależy to od informacji o strefie czasowej dostarczonej dla systemów Windows i zdefiniowanych w rejestrze, obsługuje tworzenie niestandardowych stref czasowych. Obsługuje również serializacji i deserializacji informacji o strefie czasowej.

W niektórych przypadkach pełne wykorzystanie TimeZoneInfo klasy może wymagać dalszej pracy programistycznej. Jeśli wartości daty i godziny nie są ściśle powiązane ze strefami czasowymi, do których należą, wymagana jest dalsza praca. Jeśli aplikacja nie udostępnia pewnego mechanizmu łączenia daty i godziny ze skojarzona ze skojarzona strefą czasową, łatwo jest usunąć skojarzenie określonej wartości daty i godziny z jej strefy czasowej. Jedną z metod łączenia tych informacji jest zdefiniowanie klasy lub struktury zawierającej zarówno wartość daty, jak i godziny oraz skojarzony obiekt strefy czasowej.

Aby skorzystać z obsługi strefy czasowej na platformie .NET, musisz znać strefę czasową, do której należy wartość daty i godziny, gdy wystąpi wystąpienie tego obiektu daty i godziny. Strefa czasowa często nie jest znana, szczególnie w aplikacjach internetowych lub sieciowych.

Zobacz też