Condividi tramite


Scegliere tra DateTime, DateOnly, DateTimeOffset, TimeSpan, TimeOnly e TimeZoneInfo

Le applicazioni .NET possono usare informazioni di data e ora in diversi modi. Gli usi più comuni delle informazioni relative a data e ora includono:

  • Per indicare una data soltanto, senza che le informazioni sull'ora siano importanti.
  • Per riflettere solo un'ora, in modo che le informazioni sulla data non siano importanti.
  • Per riflettere una data e un'ora astratte che non sono legate a un'ora specifica e un luogo specifico, ad esempio, la maggior parte dei negozi in una catena internazionale sono aperti nei giorni feriali alle 9:00.
  • Per recuperare informazioni di data e ora da origini esterne a .NET, in genere in cui le informazioni di data e ora vengono archiviate in un tipo di dati semplice.
  • Per identificare in modo univoco un punto nel tempo. Alcune applicazioni richiedono che una data e un'ora siano ambigue solo nel sistema host. Altre app richiedono che siano non ambigue in tutti i sistemi (ovvero, una data serializzata in un sistema può essere deserializzata in modo significativo e usata in un altro sistema in qualsiasi punto del mondo).
  • Per conservare molteplici orari correlati, come l'ora locale dell'utente richiedente e l'ora di ricezione del server per una richiesta web.
  • Per eseguire operazioni aritmetiche di data e ora, possibilmente con un risultato che identifica in modo univoco e non ambiguo un singolo punto nel tempo.

.NET include i tipi DateTime, DateOnly, DateTimeOffset, TimeSpan, TimeOnlye TimeZoneInfo, che possono essere usati per compilare applicazioni che funzionano con date e ore.

Nota

Questo articolo non illustra TimeZone perché la sua funzionalità è quasi interamente incorporata nella classe TimeZoneInfo. Quando possibile, usare la classe TimeZoneInfo anziché la classe TimeZone.

Struttura DateTimeOffset

La struttura DateTimeOffset rappresenta un valore di data e ora, insieme a un offset che indica quanto tale valore differisce rispetto all'ora UTC. Pertanto, il valore identifica sempre in modo univoco un singolo punto nel tempo.

Il tipo di DateTimeOffset include tutte le funzionalità del tipo DateTime insieme alla consapevolezza del fuso orario. In questo modo è adatto per le applicazioni che:

  • Identificare in modo univoco e chiaro un singolo punto nel tempo. Il tipo DateTimeOffset può essere usato per definire in modo univoco il significato di "now", per registrare i tempi delle transazioni, registrare gli orari degli eventi di sistema o dell'applicazione e registrare i tempi di creazione e modifica dei file.
  • Eseguire l'aritmetica generale con date e orari.
  • Conservare più istanti temporali correlati, a condizione che questi siano archiviati come due valori separati o come due elementi di una struttura.

Nota

Questi usi per i valori di DateTimeOffset sono molto più comuni di quelli per i valori DateTime. Di conseguenza, considerare DateTimeOffset come tipo di data e ora predefinito per lo sviluppo di applicazioni.

Un valore DateTimeOffset non è associato a un determinato fuso orario, ma può provenire da un'ampia gamma di fusi orari. L'esempio seguente elenca i fusi orari a cui può appartenere un numero di valori di DateTimeOffset (incluso un'ora solare del Pacifico locale).

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

L'output mostra che ogni valore di data e ora in questo esempio può appartenere ad almeno tre fusi orari diversi. Il valore DateTimeOffset del 10/6/2007 indica che se un valore di data e ora rappresenta un'ora legale, il suo offset dall'UTC potrebbe non corrispondere necessariamente all'offset UTC di base del fuso orario di origine o all'offset UTC indicato nel suo nome visualizzato. Poiché un singolo valore DateTimeOffset non è strettamente associato al proprio fuso orario, non può riflettere la transizione di un fuso orario da e verso l'ora legale. Ciò può essere problematico quando viene usata l'aritmetica di data e ora per modificare un valore DateTimeOffset. Per informazioni su come eseguire operazioni aritmetiche di data e ora in modo da tenere conto delle regole di regolazione di un fuso orario, vedere Esecuzione di operazioni aritmetiche con date e ore.

La struttura DateTime

Un valore DateTime definisce una data e un'ora particolari. Include una proprietà Kind che fornisce informazioni limitate sul fuso orario a cui appartiene tale data e ora. Il valore DateTimeKind restituito dalla proprietà Kind indica se il valore DateTime rappresenta l'ora locale (DateTimeKind.Local), l'ora UTC (Coordinated Universal Time) (DateTimeKind.Utc) o un'ora non specificata (DateTimeKind.Unspecified).

La struttura DateTime è adatta alle applicazioni con una o più delle caratteristiche seguenti:

  • Usare date e ore astratte.
  • Utilizzare date e ore per le quali mancano le informazioni sul fuso orario.
  • Usare solo date e ore UTC.
  • Eseguire l'aritmetica di data e ora e sono interessati ai risultati generali. Ad esempio, in un'operazione che aggiunge sei mesi a una determinata data e ora, spesso non è importante se il risultato viene regolato per l'ora legale.

A meno che un determinato valore DateTime non rappresenti l'ora UTC, tale valore di data e ora è spesso ambiguo o limitato nella portabilità. Ad esempio, se un valore DateTime rappresenta l'ora locale, è portabile all'interno del fuso orario locale, ovvero se il valore viene deserializzato in un altro sistema nello stesso fuso orario, tale valore identifica comunque in modo non ambiguo un singolo punto nel tempo. Al di fuori del fuso orario locale, tale valore DateTime può avere più interpretazioni. Se la proprietà Kind del valore è DateTimeKind.Unspecified, è ancora meno portabile: ora è ambigua all'interno dello stesso fuso orario e possibilmente anche nello stesso sistema in cui è stata serializzata per la prima volta. Solo se un valore DateTime rappresenta l'ora UTC identifica in modo univoco un singolo punto nel tempo indipendentemente dal sistema o dal fuso orario in cui viene usato il valore.

Importante

Quando si salvano o si condividono i dati di DateTime, usare l'ora UTC e impostare la proprietà DateTime del valore Kind su DateTimeKind.Utc.

Struttura DateOnly

La struttura DateOnly rappresenta una data specifica, senza ora. Poiché non dispone di alcun componente temporale, rappresenta una data che va dall'inizio alla fine del giorno. Questa struttura è ideale per archiviare date specifiche, ad esempio una data di nascita, una data di anniversario, una festività o una data di business.

Anche se è possibile usare DateTime ignorando il componente temporale, esistono alcuni vantaggi per l'uso di DateOnly in DateTime:

  • La struttura DateTime può slittare nel giorno precedente o successivo se è causata da uno scostamento del fuso orario. DateOnly non può essere compensato da un fuso orario e rappresenta sempre la data impostata.
  • La serializzazione di una struttura DateTime include il componente tempo, che può oscurare l'intento dei dati. Inoltre, DateOnly serializza meno dati.
  • Quando il codice interagisce con un database, ad esempio SQL Server, le date intere vengono in genere archiviate come tipo di dati date, che non include un'ora. DateOnly si adatta meglio al tipo di database.

Per altre informazioni su DateOnly, vedere Come usare le strutture DateOnly e TimeOnly.

Importante

DateOnly non è disponibile per .NET Framework.

Struttura TimeSpan

La struttura TimeSpan rappresenta un intervallo di tempo. I due usi tipici sono:

  • Riflettere l'intervallo di tempo tra due valori di data e ora. Ad esempio, sottraendo un valore DateTime da un altro restituisce un valore TimeSpan.
  • Misurazione del tempo trascorso. Ad esempio, la proprietà Stopwatch.Elapsed restituisce un valore TimeSpan che riflette l'intervallo di tempo trascorso dalla chiamata a uno dei metodi Stopwatch che inizia a misurare il tempo trascorso.

Un valore TimeSpan può essere usato anche come sostituzione di un valore DateTime quando tale valore riflette un'ora senza riferimento a un determinato giorno. Questo utilizzo è simile alle proprietà DateTime.TimeOfDay e DateTimeOffset.TimeOfDay, che restituiscono un valore TimeSpan che rappresenta l'ora senza riferimento a una data. Ad esempio, la struttura TimeSpan può essere usata per riflettere l'apertura o la chiusura giornaliera di un negozio oppure può essere usata per rappresentare l'ora in cui si verifica qualsiasi evento regolare.

Nell'esempio seguente viene definita una struttura StoreInfo che include oggetti TimeSpan per gli orari di apertura e chiusura del negozio, nonché un oggetto TimeZoneInfo che rappresenta il fuso orario del negozio. La struttura include anche due metodi, IsOpenNow e IsOpenAt, che indicano se il negozio è aperto all'orario specificato dall'utente, il quale si presume si trovi nel fuso orario locale.

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

La struttura StoreInfo può quindi essere usata dal codice client come illustrato di seguito.

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

Struttura TimeOnly

La struttura TimeOnly rappresenta un valore di ora del giorno, ad esempio un orario di sveglia giornaliero o l'ora in cui si mangia il pranzo ogni giorno. TimeOnly è limitato all'intervallo di 00:00:00.00000000 - 23:59:59.99999999999, un'ora specifica del giorno.

Prima dell'introduzione del tipo di TimeOnly, i programmatori usano in genere il tipo DateTime o il tipo TimeSpan per rappresentare un orario specifico. Tuttavia, l'uso di queste strutture per simulare ora senza una data può introdurre alcuni problemi, che TimeOnly risolve.

  • TimeSpan rappresenta il tempo trascorso, come ad esempio il tempo misurato con un cronometro. L'intervallo superiore è superiore a 29.000 anni e il relativo valore può essere negativo per indicare lo spostamento indietro nel tempo. Un TimeSpan negativo non indica un'ora specifica del giorno.
  • Se TimeSpan viene usato come ora del giorno, esiste il rischio che possa essere modificato in un valore al di fuori dell'arco di 24 ore. TimeOnly non ha questo rischio. Ad esempio, se il turno di lavoro di un dipendente inizia alle 18:00 e dura 8 ore, aggiungendo 8 ore alla struttura TimeOnly diventa 2:00.
  • L'uso di DateTime per un'ora del giorno richiede che una data arbitraria sia associata all'ora e quindi ignorata in seguito. È prassi comune scegliere DateTime.MinValue (0001-01-01-01) come data, tuttavia, se le ore vengono sottratte dal valore DateTime, potrebbe verificarsi un'eccezione OutOfRange. TimeOnly non presenta questo problema perché il tempo scorre avanti e indietro intorno all'intervallo di tempo di 24 ore.
  • La serializzazione di una struttura DateTime include il componente data, che potrebbe nascondere la finalità dei dati. Inoltre, TimeOnly serializza meno dati.

Per altre informazioni su TimeOnly, vedere Come usare le strutture DateOnly e TimeOnly.

Importante

TimeOnly non è disponibile per .NET Framework.

Classe TimeZoneInfo

La classe TimeZoneInfo rappresenta uno dei fusi orari della Terra e consente la conversione di qualsiasi data e ora in un fuso orario nell'equivalente in un altro fuso orario. La classe TimeZoneInfo consente di utilizzare date e ore in modo che qualsiasi valore di data e ora identifichi in modo univoco un singolo punto nel tempo. Anche la classe TimeZoneInfo è estendibile. Anche se dipende dalle informazioni sul fuso orario fornite per i sistemi Windows e definite nel Registro di sistema, supporta la creazione di fusi orari personalizzati. Supporta inoltre la serializzazione e la deserializzazione delle informazioni sul fuso orario.

In alcuni casi, sfruttare appieno la classe TimeZoneInfo potrebbe richiedere ulteriori operazioni di sviluppo. Se i valori di data e ora non sono strettamente associati ai fusi orari a cui appartengono, è necessario eseguire ulteriori operazioni. A meno che l'applicazione non fornisca un meccanismo per collegare una data e un'ora al fuso orario associato, è facile che un determinato valore di data e ora diventi disassociato dal fuso orario. Un metodo di collegamento di queste informazioni consiste nel definire una classe o una struttura contenente sia il valore di data che l'ora e l'oggetto fuso orario associato.

Per sfruttare il supporto del fuso orario in .NET, è necessario conoscere il fuso orario a cui appartiene un valore di data e ora quando viene creata un'istanza dell'oggetto data e ora. Il fuso orario spesso non è noto, in particolare nelle app Web o di rete.

Vedere anche