Wykonywanie operacji arytmetycznych na wartościach dat i godzin
Chociaż zarówno struktury, jak DateTime i DateTimeOffset zapewniają elementy członkowskie, które wykonują operacje arytmetyczne na ich wartościach, wyniki operacji arytmetycznych są bardzo różne. W tym artykule omówiono te różnice, odnosi się do stopnia świadomości strefy czasowej w danych daty i godziny oraz omówiono sposób wykonywania w pełni świadomych operacji strefy czasowej przy użyciu danych daty i godziny.
Porównania i operacje arytmetyczne z wartościami DateTime
Właściwość DateTime.Kind umożliwia przypisanie DateTimeKind wartości do daty i godziny, aby wskazać, czy reprezentuje czas lokalny, uniwersalny czas koordynowany (UTC) lub godzinę w nieokreślonej strefie czasowej. Jednak te ograniczone informacje o strefie czasowej są ignorowane podczas porównywania lub wykonywania arytmetyki daty i godziny dla DateTimeKind wartości. Poniższy przykład, który porównuje bieżącą godzinę lokalną z bieżącą godziną UTC, ilustruje sposób ignorowania informacji o strefie czasowej.
using System;
public enum TimeComparison
{
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(TimeComparison), 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
Metoda CompareTo(DateTime) zgłasza, że czas lokalny jest wcześniejszy niż (lub mniej niż) czasu UTC, a operacja odejmowania wskazuje, że różnica między utc a czasem lokalnym dla systemu w strefie czasowej Pacyfik Pacyfik (czas standardowy) stanów Zjednoczonych wynosi siedem godzin. Jednak ponieważ te dwie wartości zapewniają różne reprezentacje pojedynczego punktu w czasie, w tym przypadku jest jasne, że interwał czasu jest całkowicie przypisany do przesunięcia lokalnej strefy czasowej z czasu UTC.
Ogólnie rzecz biorąc, DateTime.Kind właściwość nie ma wpływu na wyniki zwracane przez Kind metody porównania i arytmetyczne (jak wskazuje porównanie dwóch identycznych punktów w czasie), chociaż może mieć wpływ na interpretację tych wyników. Na przykład:
Wynik dowolnej operacji arytmetycznej wykonywanej na dwóch wartościach daty i godziny, których DateTime.Kind właściwości są równe DateTimeKind , odzwierciedlają rzeczywisty przedział czasu między dwiema wartościami. Podobnie porównanie dwóch takich wartości daty i godziny dokładnie odzwierciedla relację między godzinami.
Wynik dowolnej operacji arytmetycznej lub porównania wykonywanej na dwóch wartościach daty i godziny, których DateTime.Kind właściwości są równe DateTimeKind lub na dwóch wartościach daty i godziny z różnymi DateTime.Kind wartościami właściwości odzwierciedlają różnicę w czasie zegara między dwiema wartościami.
Operacje arytmetyczne lub porównawcze w lokalnych wartościach daty i godziny nie uwzględniają, czy określona wartość jest niejednoznaczna, czy nieprawidłowa, ani nie uwzględniają wpływu żadnych reguł korekty, które wynikają z przejścia lokalnej strefy czasowej do lub z czasu letniego.
Każda operacja, która porównuje lub oblicza różnicę między czasem UTC a czasem lokalnym, obejmuje interwał czasu równy przesunięciom lokalnej strefy czasowej z czasu UTC w wyniku.
Każda operacja, która porównuje lub oblicza różnicę między nieokreślonym czasem a czasem UTC lub czasem lokalnym, odzwierciedla prosty czas zegara. Różnice strefy czasowej nie są brane pod uwagę, a wynik nie odzwierciedla zastosowania reguł korekty strefy czasowej.
Każda operacja, która porównuje lub oblicza różnicę między dwoma nieokreślonymi godzinami, może zawierać nieznany interwał, który odzwierciedla różnicę między czasem w dwóch różnych strefach czasowych.
Istnieje wiele scenariuszy, w których różnice strefy czasowej nie mają wpływu na obliczenia daty i godziny (w celu omówienia niektórych z tych scenariuszy zobacz Wybieranie między wartościami DateTime, DateTimeOffset, TimeSpan i TimeZoneInfo) lub w których kontekst danych daty i godziny definiuje znaczenie operacji porównawczych lub arytmetycznych.
Porównania i operacje arytmetyczne z wartościami DateTimeOffset
DateTimeOffset Wartość zawiera nie tylko datę i godzinę, ale także przesunięcie, które jednoznacznie definiuje datę i godzinę względem czasu UTC. To przesunięcie umożliwia zdefiniowanie równości inaczej niż dla DateTime wartości. Wartości DateTime są równe, jeśli mają tę samą wartość daty i godziny, wartości są równe, DateTimeOffset jeśli oba odwołują się do tego samego punktu w czasie. W przypadku użycia w porównaniach i w większości operacji arytmetycznych, które określają interwał między dwiema datami i godzinami, DateTimeOffset wartość jest dokładniejsza i mniej potrzebna do interpretacji. Poniższy przykład, który jest DateTimeOffset odpowiednikiem poprzedniego przykładu, który porównał wartości lokalne i UTC DateTimeOffset , ilustruje tę różnicę w zachowaniu.
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: {0}:{1:D2} hours",
(localTime - utcTime).Hours,
(localTime - utcTime).Minutes);
Console.WriteLine("The local time is {0} UTC.",
Enum.GetName(typeof(TimeComparison), localTime.CompareTo(utcTime)));
}
}
// 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)
W tym przykładzie CompareTo metoda wskazuje, że bieżąca godzina lokalna i bieżąca godzina UTC są równe, a odejmowanie CompareTo(DateTimeOffset) wartości wskazuje, że różnica między dwoma razy wynosi TimeSpan.Zero.
Głównym ograniczeniem używania DateTimeOffset wartości w arytmetyce daty i godziny jest to, że chociaż DateTimeOffset wartości mają pewną świadomość strefy czasowej, nie są w pełni świadomi strefy czasowej. DateTimeOffset Mimo że przesunięcie wartości odzwierciedla przesunięcie strefy czasowej z czasu UTC, gdy DateTimeOffset zmienna jest po raz pierwszy przypisana wartości, staje się ona odłączona od strefy czasowej. Ponieważ nie jest już bezpośrednio skojarzony z rozpoznawalnym czasem, dodawanie i odejmowanie interwałów daty i godziny nie uwzględnia reguł korekty strefy czasowej.
Aby zilustrować, przejście do czasu letniego w centralnej strefie czasowej w USA odbywa się o godzinie 2:00 w dniu 9 marca 2008 r. Mając to na uwadze, dodanie dwugodzinnego interwału do centralnego czasu standardowego 1:30 w dniu 9 marca 2008 r., powinno spowodować wygenerowanie daty i godziny 5:00 w dniu 9 marca 2008 r. Jednak jak pokazano w poniższym przykładzie, wynik dodania wynosi 4:00 w dniu 9 marca 2008 r. Wynik tej operacji reprezentuje prawidłowy punkt w czasie, chociaż nie jest to czas w strefie czasowej, w której jesteśmy zainteresowani (czyli nie ma oczekiwanego przesunięcia strefy czasowej).
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("{0} + {1} hours = {2}", centralTime1,
twoAndAHalfHours.ToString(),
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
Operacje arytmetyczne z czasami w strefach czasowych
Klasa TimeZoneInfo zawiera metody konwersji, które automatycznie stosują korekty, gdy konwertują czasy z jednej strefy czasowej na inną. Te metody konwersji obejmują:
Metody ConvertTime i ConvertTimeBySystemTimeZoneId , które konwertują czasy między dowolnymi dwiema strefami czasowymi.
Metody ConvertTimeFromUtc i ConvertTimeToUtc , które konwertują czas w określonej strefie czasowej na UTC lub konwertują czas UTC na godzinę w określonej strefie czasowej.
Aby uzyskać szczegółowe informacje, zobacz Konwertowanie czasów między strefami czasowymi.
Klasa TimeZoneInfo nie udostępnia żadnych metod, które automatycznie stosują reguły korekty podczas wykonywania arytmetyki daty i godziny. Można jednak zastosować reguły korekty, konwertując czas w strefie czasowej na UTC, wykonując operację arytmetyczną, a następnie konwertując z czasu UTC z powrotem na czas w strefie czasowej. Aby uzyskać szczegółowe informacje, zobacz How to: Use time zones in date and time arithmetic (Jak używać stref czasowych w arytmetyce daty i godziny).
Na przykład poniższy kod jest podobny do poprzedniego kodu, który dodał dwie i pół godziny do 2:00 w dniu 9 marca 2008 r. Jednak ponieważ konwertuje środkowy czas standardowy na UTC przed wykonaniem arytmetyki daty i godziny, a następnie konwertuje wynik z czasu UTC z powrotem na środkowy czas standardowy, wynikowy czas odzwierciedla przejście centralnej standardowej strefy czasowej na czas letni.
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("{0} + {1} hours = {2}", centralTime1,
twoAndAHalfHours.ToString(),
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