Válasszon a DateTime, a DateOnly, a DateTimeOffset, a TimeSpan, a TimeOnly és a TimeZoneInfo közül

A .NET-alkalmazások többféleképpen használhatják a dátum- és időadatokat. A dátum- és időadatok gyakoribb felhasználási módjai a következők:

  • Csak a dátumot tükrözze, hogy az időadatok ne legyenek fontosak.
  • Ha csak az időt szeretné tükrözni, hogy a dátumadatok ne legyenek fontosak.
  • Egy olyan absztrakt dátum és idő tükrözése, amely nem kötődik egy adott időponthoz és helyhez (például a legtöbb üzlet egy nemzetközi üzletláncban, amely hétköznap reggel 9:00-kor nyílik meg).
  • Dátum- és időadatok lekérése a .NET-en kívüli forrásokból, általában olyan helyekről, ahol a dátum- és időadatok egyszerű adattípusban vannak tárolva.
  • Egyetlen időpont egyedi és egyértelmű azonosítása. Egyes alkalmazások megkövetelik, hogy a dátum és az idő csak a gazdarendszeren legyen egyértelmű. Más alkalmazások esetében egyértelműnek kell lennie a rendszerek között (vagyis az egyik rendszeren szerializált dátumok értelmezhetően deszerializálhatók és használhatók a világ bármely pontján egy másik rendszeren).
  • Több kapcsolódó időpont (például a kérelmező helyi ideje és a kiszolgáló webes kéréshez való beérkezési ideje) megőrzése érdekében.
  • Dátum- és időritkítás végrehajtása, esetleg olyan eredménnyel, amely egyedileg és egyértelműen azonosít egy adott időpontot.

A .NET tartalmazza a DateTime, DateOnly, DateTimeOffset, TimeSpan, TimeOnlyés TimeZoneInfo típusokat, amelyek mindegyike dátumokkal és időpontokkal működő alkalmazások készítésére használható.

Jegyzet

Ez a cikk nem tárgyalja a TimeZone, mert a funkció szinte teljesen be van építve a TimeZoneInfo osztályba. Amikor csak lehetséges, használja a TimeZoneInfo osztályt a TimeZone osztály helyett.

A DateTimeOffset struktúra

A DateTimeOffset szerkezet egy dátum- és időértéket jelöl, valamint egy eltolást, amely azt jelzi, hogy ez az érték mennyiben különbözik az UTC-től. Így az érték mindig egyértelműen azonosít egy adott időpontot.

A DateTimeOffset típus tartalmazza az DateTime típus összes funkcióját, valamint az időzóna-tudatosságot. Ez alkalmassá teszi az olyan alkalmazásokhoz, amelyek:

  • Egyedileg és egyértelműen azonosít egy időpontot. A DateTimeOffset típus használatával egyértelműen meghatározható a "now" jelentése, a tranzakciós idők naplózhatók, naplózhatók a rendszer- vagy alkalmazásesemények időpontjai, valamint rögzíthetők a fájllétrehozás és a módosítás időpontjai.
  • Végezze el az általános dátum- és időszámítást.
  • Őrizze meg a több kapcsolódó időpontot mindaddig, amíg ezek az idők két külön értékként vagy egy struktúra két tagjaként vannak tárolva.

Jegyzet

Ezek a DateTimeOffset értékek használata sokkal gyakoribb, mint DateTime értékek esetében. Ennek eredményeképpen vegye figyelembe DateTimeOffset az alkalmazásfejlesztés alapértelmezett dátum- és időtípusát.

A DateTimeOffset érték nem egy adott időzónához van kötve, hanem különböző időzónákból származhat. Az alábbi példa felsorolja azokat az időzónákat, amelyekhez számos DateTimeOffset érték tartozhat (beleértve a helyi csendes-óceáni téli időt is).

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($"{offsetTime.ToString()} could belong to the following time zones:");
        // 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($"   {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

A kimenet azt mutatja, hogy a példában szereplő dátum- és időértékek legalább három különböző időzónához tartozhatnak. A 2007.06.10-i DateTimeOffset érték azt mutatja, hogy ha egy dátum- és időérték nyári időszámítást jelöl, akkor az UTC-ről való eltolása még csak nem is feltétlenül felel meg az eredeti időzóna alap UTC-eltolásának, vagy a megjelenített névben található UTC eltolásának. Mivel egyetlen DateTimeOffset érték nincs szorosan összekapcsolva az időzónával, nem tükrözi az időzóna nyári időszámításra és téli időszámításra való áttérését. Ez problémás lehet, ha a dátum- és időszámítást egy DateTimeOffset érték manipulálásához használják. Az időzóna beállítási szabályainak figyelembevételével végzett dátum- és időritmetikai műveletekről az Aritmetikai műveletek végrehajtása dátumokkal és időpontokkalcímű témakörben olvashat.

A DateTime-struktúra

Egy DateTime érték egy adott dátumot és időpontot határoz meg. Tartalmaz egy Kind tulajdonságot, amely korlátozott információt nyújt arról az időzónáról, amelyhez az adott dátum és idő tartozik. A DateTimeKind tulajdonság által visszaadott Kind érték azt jelzi, hogy a DateTime érték a helyi időt (DateTimeKind.Local), az egyezményes világidőt (UTC) (DateTimeKind.Utc) vagy egy meghatározatlan időpontot (DateTimeKind.Unspecified) jelöl.

A DateTime szerkezet az alábbi jellemzők közül egy vagy több jellemzővel rendelkező alkalmazásokhoz alkalmas:

  • Dolgozzon absztrakt dátumokkal és időpontokkal.
  • Olyan dátumokkal és időpontokkal dolgozhat, amelyekről hiányzik az időzóna információja.
  • Csak UTC-dátumokkal és időpontokkal dolgozhat.
  • Végezzen dátum- és időszámításokat, de az általános eredményekre összpontosítson. Egy hat hónapot egy adott dátumhoz és időponthoz hozzáfűző műveletnél például gyakran nem fontos, hogy az eredmény a nyári időszámításhoz legyen-e igazítva.

Ha egy adott DateTime érték nem az UTC értéket jelöli, ez a dátum- és időérték gyakran nem egyértelmű vagy korlátozott a hordozhatóságában. Ha például egy DateTime érték a helyi időt jelöli, akkor a helyi időzónán belül hordozható (vagyis ha az érték deszerializálva van egy másik, ugyanabban az időzónában lévő rendszeren, akkor az érték továbbra is egyértelműen azonosítja az adott időpontot). A helyi időzónán kívül ez a DateTime érték több értelmezést is tartalmazhat. Ha az érték Kind tulajdonsága DateTimeKind.Unspecified, akkor még kevésbé hordozható: most már nem egyértelmű ugyanazon az időzónán belül, és valószínűleg ugyanazon a rendszeren is, ahol először szerializálták. Csak akkor, ha egy DateTime érték az UTC értéket jelöli, az az érték egyértelműen azonosít egy időpontot, függetlenül attól a rendszertől vagy időzónától, amelyben az értéket használják.

Fontos

DateTime adatok mentésekor vagy megosztásakor használja az UTC értéket, és állítsa a DateTime érték Kind tulajdonságát DateTimeKind.Utc.

A DateOnly struktúra

A DateOnly szerkezet egy adott dátumot jelöl, idő nélkül. Mivel nincs időösszetevője, a nap elejétől a nap végéig egy dátumot jelöl. Ez a szerkezet ideális adott dátumok, például születési dátum, évfordulós dátum, ünnep vagy üzleti dátum tárolására.

Bár használhatná a DateTime-t az időösszetevő figyelmen kívül hagyásával, a DateOnly-nek több előnye van a DateTime-vel szemben.

  • A DateTime szerkezet az előző vagy a következő napra kerülhet, ha egy időzóna eltolása történik. DateOnly nem lehet egy időzónával eltolni, és mindig a beállított dátumot jelöli.
  • A DateTime struktúra szerializálása magában foglalja az időösszetevőt, amely elhomályosíthatja az adatok szándékát. Emellett DateOnly kevesebb adatot szerializál.
  • Amikor a kód egy adatbázissal (például az SQL Serverrel) kommunikál, a rendszer általában date adattípusként tárolja a teljes dátumokat, ami nem tartalmaz időt. DateOnly jobban megfelel az adatbázis típusának.

A DateOnlytémáról további információt a DateOnly és TimeOnly struktúrák használatának módját bemutatócímű részben talál.

Fontos

DateOnly nem érhető el a .NET-keretrendszerhez.

A TimeSpan struktúra

A TimeSpan szerkezet egy időintervallumot jelöl. Két jellemző felhasználási módja a következő:

  • A két dátum és időérték közötti időintervallumot tükrözi. Egy DateTime érték kivonása például egy TimeSpan értéket ad vissza.
  • Az eltelt idő mérése. A Stopwatch.Elapsed tulajdonság például egy TimeSpan értéket ad vissza, amely az eltelt idő mérésére szolgáló Stopwatch metódus hívása óta eltelt időintervallumot tükrözi.

A TimeSpan érték DateTime érték helyettesítésére is használható, ha ez az érték egy adott napra való hivatkozás nélküli időt tükröz. Ez a használat hasonló a DateTime.TimeOfDay és DateTimeOffset.TimeOfDay tulajdonságokhoz, amelyek TimeSpan értéket ad vissza, amely dátumra való hivatkozás nélkül jelöli az időt. A TimeSpan szerkezet például az áruház napi nyitvatartási vagy záróidejét tükrözheti, vagy a szokásos események bekövetkezésének időpontját ábrázolhatja.

Az alábbi példa egy StoreInfo struktúrát határoz meg, amely TimeSpan objektumokat tartalmaz az áruház megnyitásához és bezárásához, valamint egy TimeZoneInfo objektumot, amely az áruház időzónáját jelöli. A struktúra két metódust is tartalmaz, IsOpenNow és IsOpenAt, amelyek azt jelzik, hogy az áruház a felhasználó által megadott időpontban van-e nyitva, és a rendszer feltételezi, hogy a helyi időzónában van.

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

A StoreInfo struktúrát az ügyfélkód az alábbihoz hasonlóan használhatja.

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 {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 {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: True
//       Store is open at 04:59:00: False
//       Store is open at 18:31:00: True
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: True
'       Store is open at 04:59:00: False
'       Store is open at 18:31:00: True

A TimeOnly struktúra

A TimeOnly szerkezet egy napközbeni értéket jelöl, például egy napi ébresztőórát, vagy azt, hogy mikor ebédel minden nap. TimeOnly a 00:00:00.0000000 - 23:59:59.9999999tartományára korlátozódik, egy adott napszakra.

A TimeOnly típus bevezetése előtt a programozók általában a DateTime típust vagy a TimeSpan típust használták egy adott időpont megjelenítéséhez. Ha azonban ezeket a struktúrákat a dátum nélküli idő szimulálására használja, problémákat okozhat, amelyeket TimeOnly megoldani:

  • TimeSpan az eltelt időt jelöli, például egy stopperrel mért időt. A felső tartomány több mint 29 000 év, és értéke negatív lehet az időben való visszalépés jelzésére. A negatív TimeSpan nem jelzi a nap adott időpontját.
  • Ha TimeSpan napidőként használják, fennáll a veszélye annak, hogy a 24 órás napon kívülre is módosítható. TimeOnly nincs ilyen kockázata. Ha például egy alkalmazott műszakja 18:00-kor kezdődik, és 8 órán át tart, akkor 8 órát hozzáadva a TimeOnly struktúrához átlépünk 2:00-ra.
  • A DateTime egy napszak megadásához való használatához tetszőleges dátumot kell társítani az időhöz, amit később figyelmen kívül kell hagyni. Gyakori eljárás, hogy dátumként DateTime.MinValue (0001-01-01) választ, de ha az órákat kivonja a DateTime értékből, OutOfRange kivétel léphet fel. A TimeOnly-nál nem áll fenn ez a probléma, mivel az idő előre és hátra mozog a 24 órás időintervallumon belül.
  • A DateTime struktúra szerializálása magában foglalja a dátumösszetevőt, amely elhomályosíthatja az adatok szándékát. Emellett TimeOnly kevesebb adatot szerializál.

A TimeOnlytémáról további információt a DateOnly és TimeOnly struktúrák használatának módját bemutatócímű részben talál.

Fontos

TimeOnly nem érhető el a .NET-keretrendszerhez.

A TimeZoneInfo osztály

A TimeZoneInfo osztály a Föld időzónáinak bármelyikét jelöli, és lehetővé teszi az egy időzónában lévő dátumok és időpontok egy másik időzónában lévő megfelelőjének konvertálását. A TimeZoneInfo osztály lehetővé teszi a dátumokkal és időpontokkal való munkát, hogy minden dátum- és időérték egyértelműen azonosuljon egy adott időponttal. A TimeZoneInfo osztály is bővíthető. Bár a Windows-rendszerekhez megadott és a beállításjegyzékben meghatározott időzóna-információktól függ, támogatja az egyéni időzónák létrehozását. Támogatja az időzóna-információk szerializálását és deszerializálását is.

Bizonyos esetekben a TimeZoneInfo osztály teljes kihasználása további fejlesztési munkát igényelhet. Ha a dátum- és időértékek nincsenek szorosan összekapcsolva azokkal az időzónákkal, amelyekhez tartoznak, további munkára van szükség. Ha az alkalmazás nem biztosít valamilyen mechanizmust egy dátum és idő társításához a hozzá tartozó időzónával, könnyen előfordulhat, hogy egy adott dátum és idő érték el lesz különítve az időzónától. Az információk összekapcsolásának egyik módja egy olyan osztály vagy struktúra definiálása, amely tartalmazza a dátum- és időértéket, valamint a hozzá tartozó időzóna-objektumot.

A .NET időzóna-támogatásának kihasználásához ismernie kell azt az időzónát, amelyhez egy dátum- és időérték tartozik az adott dátum- és időobjektum példányosításakor. Az időzóna gyakran nem ismert, különösen webes vagy hálózati alkalmazásokban.

Lásd még: