Dela via


Välj mellan DateTime, DateOnly, DateTimeOffset, TimeSpan, TimeOnly och TimeZoneInfo

.NET-program kan använda information om datum och tid på flera sätt. Vanligare användningar av datum- och tidsinformation är:

  • För att endast återspegla ett datum, så att tidsinformation inte är viktig.
  • För att endast återspegla en tid, så att datuminformationen inte är viktig.
  • För att återspegla ett abstrakt datum och tid som inte är knutet till en viss tid och plats (till exempel de flesta butiker i en internationell kedja öppna på vardagar kl. 09:00).
  • För att hämta datum- och tidsinformation från källor utanför .NET, vanligtvis där datum- och tidsinformation lagras i en enkel datatyp.
  • För att unikt och otvetydigt identifiera en enskild tidpunkt. Vissa program kräver att ett datum och en tid endast är entydiga i värdsystemet. Andra appar kräver att det är entydigt mellan system (det vill: ett datum som serialiseras på ett system kan deserialiseras på ett meningsfullt sätt och användas på ett annat system var som helst i världen).
  • Bevara flera relaterade tider (till exempel den lokala tiden för begäranden och serverns kvittotid för en webbbegäran).
  • För att utföra datum- och tidsaritmetik, möjligen med ett resultat som unikt och otvetydigt identifierar en enskild tidpunkt.

.NET innehåller typerna DateTime, DateOnly, DateTimeOffset, TimeSpan, TimeOnlyoch TimeZoneInfo som alla kan användas för att skapa program som fungerar med datum och tider.

Kommentar

Den här artikeln diskuterar TimeZone inte eftersom dess funktioner nästan helt ingår i TimeZoneInfo klassen. Använd klassen i stället för klassen när det TimeZone är möjligtTimeZoneInfo.

DateTimeOffset-strukturen

Strukturen DateTimeOffset representerar ett datum- och tidsvärde, tillsammans med en förskjutning som anger hur mycket det värdet skiljer sig från UTC. Därför identifierar värdet alltid entydigt en enskild tidpunkt.

Typen DateTimeOffset innehåller alla funktioner av typen DateTime tillsammans med tidszonsmedvetenhet. Detta gör det lämpligt för program som:

  • Identifiera en enskild tidpunkt på ett unikt och otvetydigt sätt. Typen DateTimeOffset kan användas för att entydigt definiera innebörden av "nu", logga transaktionstider, logga tider för system- eller programhändelser och registrera skapande- och ändringstider för filer.
  • Utför allmän datum- och tidsaritmetik.
  • Bevara flera relaterade tider, så länge dessa tider lagras som två separata värden eller som två medlemmar i en struktur.

Kommentar

Dessa användningsområden för DateTimeOffset värden är mycket vanligare än för DateTime värden. Därför bör du betrakta DateTimeOffset som standarddatum och tidstyp för programutveckling.

Ett DateTimeOffset värde är inte kopplat till en viss tidszon, men kan komma från en mängd olika tidszoner. I följande exempel visas de tidszoner som ett antal DateTimeOffset värden (inklusive en lokal Pacific Standard Time) kan tillhöra.

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

Utdata visar att varje datum- och tidsvärde i det här exemplet kan tillhöra minst tre olika tidszoner. Värdet DateTimeOffset 2007-06-10 visar att om ett datum- och tidsvärde representerar en sommartid motsvarar dess förskjutning från UTC inte ens nödvändigtvis den ursprungliga tidszonens bas-UTC-förskjutning eller till förskjutningen från UTC som finns i dess visningsnamn. Eftersom ett enda DateTimeOffset värde inte är nära kopplat till tidszonen kan det inte återspegla en tidszons övergång till och från sommartid. Detta kan vara problematiskt när datum- och tidsaritmetik används för att manipulera ett DateTimeOffset värde. En diskussion om hur du utför datum- och tidsaritmetik på ett sätt som tar hänsyn till en tidszons justeringsregler finns i Utföra aritmetiska åtgärder med datum och tider.

DateTime-strukturen

Ett DateTime värde definierar ett visst datum och en viss tid. Den innehåller en Kind egenskap som innehåller begränsad information om tidszonen som datum och tid tillhör. Värdet DateTimeKind som returneras av Kind egenskapen anger om DateTime värdet representerar den lokala tiden (DateTimeKind.Local), Coordinated Universal Time (UTC) (DateTimeKind.Utc) eller en ospecificerad tid (DateTimeKind.Unspecified).

Strukturen DateTime är lämplig för program med en eller flera av följande egenskaper:

  • Arbeta med abstrakta datum och tider.
  • Arbeta med datum och tider för vilka tidszonsinformation saknas.
  • Arbeta endast med UTC-datum och -tider.
  • Utför datum- och tidsaritmetik, men bryr sig om allmänna resultat. I en tilläggsåtgärd som till exempel lägger till sex månader till ett visst datum och en viss tid är det ofta inte viktigt om resultatet justeras för sommartid.

Om inte ett visst DateTime värde representerar UTC är det datum- och tidsvärdet ofta tvetydigt eller begränsat i dess portabilitet. Om ett DateTime värde till exempel representerar den lokala tiden är det portabelt inom den lokala tidszonen (det vill säga om värdet deserialiseras på ett annat system i samma tidszon identifierar värdet fortfarande entydigt en enskild tidpunkt). Utanför den lokala tidszonen kan det DateTime värdet ha flera tolkningar. Om värdets Kind egenskap är DateTimeKind.Unspecifiedär det ännu mindre portabelt: det är nu tvetydigt inom samma tidszon och eventuellt även i samma system där det först serialiserades. Endast om ett DateTime värde representerar UTC identifierar det värdet entydigt en enskild tidpunkt oavsett systemet eller tidszonen där värdet används.

Viktigt!

När du sparar eller delar DateTime data använder du UTC och anger DateTime värdets Kind egenskap till DateTimeKind.Utc.

DateOnly-strukturen

Strukturen DateOnly representerar ett specifikt datum, utan tid. Eftersom den inte har någon tidskomponent representerar den ett datum från dagens början till slutet av dagen. Den här strukturen är perfekt för att lagra specifika datum, till exempel ett födelsedatum, ett jubileumsdatum, en helgdag eller ett affärsrelaterat datum.

Även om du kan använda DateTime när du ignorerar tidskomponenten finns det några fördelar med att använda DateOnly över DateTime:

  • Strukturen DateTime kan rullas in i föregående eller nästa dag om den förskjuts av en tidszon. DateOnly kan inte förskjutas av en tidszon och representerar alltid det datum som angavs.
  • Serialisering av en DateTime struktur innehåller tidskomponenten, vilket kan dölja avsikten med data. DateOnly Dessutom serialiserar mindre data.
  • När koden interagerar med en databas, till exempel SQL Server, lagras hela datum vanligtvis som date datatyp, vilket inte omfattar någon tid. DateOnly matchar databastypen bättre.

Mer information om DateOnlyfinns i Så här använder du strukturerna DateOnly och TimeOnly.

Viktigt!

DateOnly är inte tillgängligt i .NET Framework.

TimeSpan-strukturen

Strukturen TimeSpan representerar ett tidsintervall. Dess två typiska användningsområden är:

  • Visar tidsintervallet mellan två datum- och tidsvärden. Om du till exempel subtraherar ett DateTime värde från ett annat returneras ett TimeSpan värde.
  • Mätning av förfluten tid. Egenskapen returnerar till exempel Stopwatch.Elapsed ett TimeSpan värde som återspeglar det tidsintervall som har förflutit sedan anropet till en av de Stopwatch metoder som börjar mäta förfluten tid.

Ett TimeSpan värde kan också användas som ersättning för ett DateTime värde när det värdet återspeglar en tid utan referens till en viss dag. Den här användningen liknar DateTime.TimeOfDay egenskaperna och DateTimeOffset.TimeOfDay som returnerar ett TimeSpan värde som representerar tiden utan referens till ett datum. Strukturen kan till exempel TimeSpan användas för att återspegla en butiks dagliga öppnings- eller stängningstid, eller så kan den användas för att representera tidpunkten då en vanlig händelse inträffar.

I följande exempel definieras en StoreInfo struktur som innehåller TimeSpan objekt för butiksöppnings- och stängningstider samt ett TimeZoneInfo objekt som representerar butikens tidszon. Strukturen innehåller också två metoder, IsOpenNow och IsOpenAt, som anger om arkivet är öppet vid en tidpunkt som anges av användaren, som antas vara i den lokala tidszonen.

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

Strukturen StoreInfo kan sedan användas av klientkod som följande.

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

Strukturen TimeOnly

Strukturen TimeOnly representerar ett tidsvärde, till exempel en daglig väckarklocka eller vilken tid du äter lunch varje dag. TimeOnlyär begränsat till intervallet 00:00:00.0000000 - 23:59:59.99999999, en viss tid på dagen.

Innan typen TimeOnly introducerades använde programmerare vanligtvis antingen DateTime typen eller TimeSpan typen för att representera en viss tid. Att använda dessa strukturer för att simulera en tid utan datum kan dock medföra vissa problem, vilket TimeOnly löser:

  • TimeSpan representerar förfluten tid, till exempel tid som mäts med ett stoppur. Det övre intervallet är mer än 29 000 år och dess värde kan vara negativt för att indikera att det rör sig bakåt i tiden. Ett negativt TimeSpan indikerar inte en viss tid på dagen.
  • Om TimeSpan används som en tid på dagen finns det en risk att det kan manipuleras till ett värde utanför 24-timmarsdagen. TimeOnly har inte den här risken. Om till exempel en anställds arbetsskift börjar kl. 18:00 och varar i 8 timmar, så övergår 8 timmar till TimeOnly strukturen till 2:00.
  • Användning DateTime under en tid på dagen kräver att ett godtyckligt datum associeras med tiden och sedan ignoreras senare. Det är vanligt att välja DateTime.MinValue (0001-01-01) som datum, men om timmar subtraheras från DateTime värdet kan ett OutOfRange undantag inträffa. TimeOnly har inte det här problemet när tiden rullar framåt och bakåt runt tidsramen på 24 timmar.
  • Serialisering av en DateTime struktur innehåller datumkomponenten, vilket kan dölja avsikten med data. TimeOnly Dessutom serialiserar mindre data.

Mer information om TimeOnlyfinns i Så här använder du strukturerna DateOnly och TimeOnly.

Viktigt!

TimeOnly är inte tillgängligt i .NET Framework.

Klassen TimeZoneInfo

Klassen TimeZoneInfo representerar någon av jordens tidszoner och möjliggör konvertering av datum och tid i en tidszon till dess motsvarighet i en annan tidszon. Klassen TimeZoneInfo gör det möjligt att arbeta med datum och tider så att ett datum- och tidsvärde entydigt identifierar en enskild tidpunkt. Klassen TimeZoneInfo är också utökningsbar. Även om det beror på tidszonsinformation som tillhandahålls för Windows-system och definieras i registret, stöder det skapandet av anpassade tidszoner. Det stöder också serialisering och deserialisering av tidszonsinformation.

I vissa fall kan det krävas ytterligare utvecklingsarbete för att dra full nytta av TimeZoneInfo klassen. Om datum- och tidsvärdena inte är nära kopplade till de tidszoner som de tillhör krävs ytterligare arbete. Såvida inte ditt program tillhandahåller någon mekanism för att länka ett datum och en tid till den associerade tidszonen är det enkelt att koppla bort ett visst datum- och tidsvärde från tidszonen. En metod för att länka den här informationen är att definiera en klass eller struktur som innehåller både datum- och tidsvärdet och dess associerade tidszonsobjekt.

Om du vill dra nytta av tidszonsstöd i .NET måste du känna till tidszonen som ett datum- och tidsvärde tillhör när det datum- och tidsobjektet instansieras. Tidszonen är ofta inte känd, särskilt i webb- eller nätverksappar.

Se även