Condividi tramite


Esecuzione di operazioni aritmetiche con date e ore

Anche se le strutture DateTime e DateTimeOffset forniscono membri che eseguono operazioni aritmetiche sui relativi valori, i risultati delle operazioni aritmetiche sono molto diversi. Questo articolo esamina queste differenze, le mette in relazione a gradi di consapevolezza del fuso orario nei dati di data e ora e illustra come eseguire operazioni con riconoscimento del fuso orario completo usando dati di data e ora.

Confronti e operazioni aritmetiche con valori DateTime

La proprietà DateTime.Kind consente di assegnare un valore DateTimeKind alla data e all'ora per indicare se rappresenta l'ora locale, l'ora UTC (Coordinated Universal Time) o l'ora in un fuso orario non specificato. Tuttavia, queste informazioni limitate sul fuso orario vengono ignorate durante il confronto o l'esecuzione di una aritmetica di data e ora sui valori DateTimeKind. Nell'esempio seguente, che confronta l'ora locale corrente con l'ora UTC corrente, viene illustrato come vengono ignorate le informazioni sul fuso orario.

using System;

public enum TimeComparison2
{
    EarlierThan = -1,
    TheSameAs = 0,
    LaterThan = 1
}

public class DateManipulation
{
    public static void Main()
    {
        DateTime localTime = DateTime.Now;
        DateTime utcTime = DateTime.UtcNow;

        Console.WriteLine("Difference between {0} and {1} time: {2}:{3} hours",
                          localTime.Kind,
                          utcTime.Kind,
                          (localTime - utcTime).Hours,
                          (localTime - utcTime).Minutes);
        Console.WriteLine("The {0} time is {1} the {2} time.",
                          localTime.Kind,
                          Enum.GetName(typeof(TimeComparison2), localTime.CompareTo(utcTime)),
                          utcTime.Kind);
    }
}
// If run in the U.S. Pacific Standard Time zone, the example displays
// the following output to the console:
//    Difference between Local and Utc time: -7:0 hours
//    The Local time is EarlierThan the Utc time.
Public Enum TimeComparison As Integer
    EarlierThan = -1
    TheSameAs = 0
    LaterThan = 1
End Enum

Module DateManipulation
    Public Sub Main()
        Dim localTime As Date = Date.Now
        Dim utcTime As Date = Date.UtcNow

        Console.WriteLine("Difference between {0} and {1} time: {2}:{3} hours", _
                          localTime.Kind.ToString(), _
                          utcTime.Kind.ToString(), _
                          (localTime - utcTime).Hours, _
                          (localTime - utcTime).Minutes)
        Console.WriteLine("The {0} time is {1} the {2} time.", _
                          localTime.Kind.ToString(), _
                          [Enum].GetName(GetType(TimeComparison), localTime.CompareTo(utcTime)), _
                          utcTime.Kind.ToString())
        ' If run in the U.S. Pacific Standard Time zone, the example displays 
        ' the following output to the console:
        '    Difference between Local and Utc time: -7:0 hours
        '    The Local time is EarlierThan the Utc time.                                                    
    End Sub
End Module

Il metodo CompareTo(DateTime) indica che l'ora locale è precedente (o minore di) all'ora UTC e l'operazione di sottrazione indica che la differenza tra l'ora UTC e l'ora locale per un sistema nel fuso orario standard del Pacifico degli Stati Uniti è di sette ore. Tuttavia, poiché questi due valori forniscono rappresentazioni diverse di un singolo punto nel tempo, in questo caso è chiaro che l'intervallo di tempo è completamente attribuibile all'offset del fuso orario locale rispetto all'ora UTC.

In generale, la proprietà DateTime.Kind non influisce sui risultati restituiti da Kind metodi di confronto e aritmetici (come indica il confronto di due punti identici nel tempo), anche se può influire sull'interpretazione di tali risultati. Per esempio:

  • Risultato di qualsiasi operazione aritmetica eseguita su due valori di data e ora i cui DateTime.Kind proprietà sono entrambi uguali DateTimeKind riflette l'intervallo di tempo effettivo tra i due valori. Analogamente, il confronto di due valori di data e ora riflette in modo accurato la relazione tra le ore.

  • Il risultato di qualsiasi operazione aritmetica o di confronto eseguita su due valori di data e ora le cui proprietà DateTime.Kind sono entrambe uguali a DateTimeKind o su due valori di data e ora con proprietà DateTime.Kind diverse riflette la differenza nell'ora di clock tra i due valori.

  • Le operazioni aritmetiche o di confronto sui valori di data e ora locali non considerano se un determinato valore è ambiguo o non valido, né tiene conto dell'effetto di eventuali regole di rettifica risultanti dalla transizione del fuso orario locale all'ora legale o dall'ora legale.

  • Qualsiasi operazione che confronta o calcola la differenza tra utc e un'ora locale include un intervallo di tempo uguale all'offset del fuso orario locale rispetto all'ora UTC nel risultato.

  • Qualsiasi operazione che confronta o calcola la differenza tra un'ora non specificata e l'ora UTC o l'ora locale riflette l'ora dell'orologio semplice. Le differenze di fuso orario non vengono considerate e il risultato non riflette l'applicazione delle regole di regolazione del fuso orario.

  • Qualsiasi operazione che confronta o calcola la differenza tra due tempi non specificati può includere un intervallo sconosciuto che riflette la differenza tra l'ora in due fusi orari diversi.

Esistono molti scenari in cui le differenze di fuso orario non influiscono sui calcoli di data e ora (per una descrizione di alcuni di questi scenari, vedere Scelta tra DateTime, DateTimeOffset, TimeSpan e TimeZoneInfo) o in cui il contesto dei dati di data e ora definisce il significato delle operazioni di confronto o aritmetiche.

Confronti e operazioni aritmetiche con valori DateTimeOffset

Un valore DateTimeOffset include non solo una data e un'ora, ma anche un offset che definisce in modo univoco tale data e ora rispetto all'ora UTC. Questo offset consente di definire l'uguaglianza in modo diverso rispetto ai valori di DateTime. Mentre DateTime valori sono uguali se hanno lo stesso valore di data e ora, DateTimeOffset valori sono uguali se entrambi fanno riferimento allo stesso punto nel tempo. Se usato nei confronti e nella maggior parte delle operazioni aritmetiche che determinano l'intervallo tra due date e ore, un valore DateTimeOffset è più accurato e meno necessario per l'interpretazione. L'esempio seguente, che è il DateTimeOffset equivalente all'esempio precedente che ha confrontato i valori DateTimeOffset locali e UTC, illustra questa differenza nel comportamento.

using System;

public enum TimeComparison
{
    EarlierThan = -1,
    TheSameAs = 0,
    LaterThan = 1
}

public class DateTimeOffsetManipulation
{
    public static void Main()
    {
        DateTimeOffset localTime = DateTimeOffset.Now;
        DateTimeOffset utcTime = DateTimeOffset.UtcNow;

        Console.WriteLine($"Difference between local time and UTC: {(localTime - utcTime).Hours}:{(localTime - utcTime).Minutes:D2} hours");
        Console.WriteLine($"The local time is {Enum.GetName(typeof(TimeComparison), localTime.CompareTo(utcTime))} UTC.");
    }
}
// Regardless of the local time zone, the example displays
// the following output to the console:
//    Difference between local time and UTC: 0:00 hours.
//    The local time is TheSameAs UTC.
Public Enum TimeComparison As Integer
    EarlierThan = -1
    TheSameAs = 0
    LaterThan = 1
End Enum

Module DateTimeOffsetManipulation
    Public Sub Main()
        Dim localTime As DateTimeOffset = DateTimeOffset.Now
        Dim utcTime As DateTimeOffset = DateTimeOffset.UtcNow

        Console.WriteLine("Difference between local time and UTC: {0}:{1:D2} hours.", _
                          (localTime - utcTime).Hours, _
                          (localTime - utcTime).Minutes)
        Console.WriteLine("The local time is {0} UTC.", _
                          [Enum].GetName(GetType(TimeComparison), localTime.CompareTo(utcTime)))
    End Sub
End Module
' Regardless of the local time zone, the example displays 
' the following output to the console:
'    Difference between local time and UTC: 0:00 hours.
'    The local time is TheSameAs UTC.
'          Console.WriteLine(e.GetType().Name)

In questo esempio, il metodo CompareTo indica che l'ora locale corrente e l'ora UTC corrente sono uguali e la sottrazione dei valori CompareTo(DateTimeOffset) indica che la differenza tra le due volte è TimeSpan.Zero.

La limitazione principale dell'uso dei valori DateTimeOffset nell'aritmetica di data e ora è che, anche se i valori DateTimeOffset hanno una certa consapevolezza del fuso orario, non sono completamente consapevoli del fuso orario. Anche se l'offset del valore DateTimeOffset riflette l'offset di un fuso orario rispetto all'ora UTC quando a una variabile DateTimeOffset viene assegnato un valore, diventa disassociato dal fuso orario successivamente. Poiché non è più direttamente associato a un'ora identificabile, l'addizione e la sottrazione degli intervalli di data e ora non considera le regole di regolazione di un fuso orario.

Per illustrare, la transizione all'ora legale nel fuso orario standard degli Stati Uniti si verifica alle 2:00 del 9 marzo 2008. Tenendo presente che l'aggiunta di un intervallo di due ore e mezzo a un orario standard centrale delle 1:30 il 9 marzo 2008 dovrebbe produrre una data e un'ora di 5:00 il 9 marzo 2008. Tuttavia, come illustrato nell'esempio seguente, il risultato dell'aggiunta è 4:00 A.M. il 9 marzo 2008. Il risultato di questa operazione rappresenta il punto nel tempo corretto, anche se non è l'ora nel fuso orario in cui si è interessati, ovvero non ha lo scostamento orario previsto.

using System;

public class IntervalArithmetic
{
    public static void Main()
    {
        DateTime generalTime = new DateTime(2008, 3, 9, 1, 30, 0);
        const string tzName = "Central Standard Time";
        TimeSpan twoAndAHalfHours = new TimeSpan(2, 30, 0);

        // Instantiate DateTimeOffset value to have correct CST offset
        try
        {
            DateTimeOffset centralTime1 = new DateTimeOffset(generalTime,
                       TimeZoneInfo.FindSystemTimeZoneById(tzName).GetUtcOffset(generalTime));

            // Add two and a half hours
            DateTimeOffset centralTime2 = centralTime1.Add(twoAndAHalfHours);
            // Display result
            Console.WriteLine($"{centralTime1} + {twoAndAHalfHours.ToString()} hours = {centralTime2}");
        }
        catch (TimeZoneNotFoundException)
        {
            Console.WriteLine("Unable to retrieve Central Standard Time zone information.");
        }
    }
}
// The example displays the following output to the console:
//    3/9/2008 1:30:00 AM -06:00 + 02:30:00 hours = 3/9/2008 4:00:00 AM -06:00
Module IntervalArithmetic
    Public Sub Main()
        Dim generalTime As Date = #03/09/2008 1:30AM#
        Const tzName As String = "Central Standard Time"
        Dim twoAndAHalfHours As New TimeSpan(2, 30, 0)

        ' Instantiate DateTimeOffset value to have correct CST offset
        Try
            Dim centralTime1 As New DateTimeOffset(generalTime, _
                       TimeZoneInfo.FindSystemTimeZoneById(tzName).GetUtcOffset(generalTime))

            ' Add two and a half hours      
            Dim centralTime2 As DateTimeOffset = centralTime1.Add(twoAndAHalfHours)
            ' Display result
            Console.WriteLine("{0} + {1} hours = {2}", centralTime1, _
                                                       twoAndAHalfHours.ToString(), _
                                                       centralTime2)
        Catch e As TimeZoneNotFoundException
            Console.WriteLine("Unable to retrieve Central Standard Time zone information.")
        End Try
    End Sub
End Module
' The example displays the following output to the console:
'    3/9/2008 1:30:00 AM -06:00 + 02:30:00 hours = 3/9/2008 4:00:00 AM -06:00

Operazioni aritmetiche con orari nei fusi orari

La classe TimeZoneInfo include metodi di conversione che applicano automaticamente le regolazioni quando convertono le ore da un fuso orario a un altro. Questi metodi di conversione includono:

Per informazioni dettagliate, vedere Conversione di orari tra fusi orari.

La classe TimeZoneInfo non fornisce metodi che applicano automaticamente regole di regolazione quando si esegue l'aritmetica di data e ora. Tuttavia, è possibile applicare regole di adeguamento convertendo l'ora in un fuso orario in UTC, eseguendo l'operazione aritmetica e quindi convertendo dal tempo UTC al tempo nel fuso orario. Per informazioni dettagliate, vedere Procedura: Usare i fusi orari nell'aritmetica delle date e degli orari.

Ad esempio, il codice seguente è simile al codice precedente che ha aggiunto due e mezza ore alle 2:00 del 9 marzo 2008. Tuttavia, poiché converte l'ora standard centrale in UTC prima di eseguire calcoli di data e ora e quindi converte il risultato dall'UTC all'ora standard centrale, l'ora risultante riflette il passaggio del fuso orario standard centrale alla sua transizione all'ora legale.

using System;

public class TimeZoneAwareArithmetic
{
    public static void Main()
    {
        const string tzName = "Central Standard Time";

        DateTime generalTime = new DateTime(2008, 3, 9, 1, 30, 0);
        TimeZoneInfo cst = TimeZoneInfo.FindSystemTimeZoneById(tzName);
        TimeSpan twoAndAHalfHours = new TimeSpan(2, 30, 0);

        // Instantiate DateTimeOffset value to have correct CST offset
        try
        {
            DateTimeOffset centralTime1 = new DateTimeOffset(generalTime,
                                          cst.GetUtcOffset(generalTime));

            // Add two and a half hours
            DateTimeOffset utcTime = centralTime1.ToUniversalTime();
            utcTime += twoAndAHalfHours;

            DateTimeOffset centralTime2 = TimeZoneInfo.ConvertTime(utcTime, cst);
            // Display result
            Console.WriteLine($"{centralTime1} + {twoAndAHalfHours.ToString()} hours = {centralTime2}");
        }
        catch (TimeZoneNotFoundException)
        {
            Console.WriteLine("Unable to retrieve Central Standard Time zone information.");
        }
    }
}
// The example displays the following output to the console:
//    3/9/2008 1:30:00 AM -06:00 + 02:30:00 hours = 3/9/2008 5:00:00 AM -05:00
Module TimeZoneAwareArithmetic
    Public Sub Main()
        Const tzName As String = "Central Standard Time"

        Dim generalTime As Date = #03/09/2008 1:30AM#
        Dim cst As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(tzName)
        Dim twoAndAHalfHours As New TimeSpan(2, 30, 0)

        ' Instantiate DateTimeOffset value to have correct CST offset
        Try
            Dim centralTime1 As New DateTimeOffset(generalTime, _
                       cst.GetUtcOffset(generalTime))

            ' Add two and a half hours 
            Dim utcTime As DateTimeOffset = centralTime1.ToUniversalTime()
            utcTime += twoAndAHalfHours

            Dim centralTime2 As DateTimeOffset = TimeZoneInfo.ConvertTime(utcTime, cst)
            ' Display result
            Console.WriteLine("{0} + {1} hours = {2}", centralTime1, _
                                                       twoAndAHalfHours.ToString(), _
                                                       centralTime2)
        Catch e As TimeZoneNotFoundException
            Console.WriteLine("Unable to retrieve Central Standard Time zone information.")
        End Try
    End Sub
End Module
' The example displays the following output to the console:
'    3/9/2008 1:30:00 AM -06:00 + 02:30:00 hours = 3/9/2008 5:00:00 AM -05:00

Vedere anche