Speichern und Wiederherstellen von Zeitzonen
Die TimeZoneInfo-Klasse ruft vordefinierte Zeitzonendaten aus der Registrierung ab. Die Registrierung ist jedoch eine dynamische Struktur. Darüber hinaus werden die in der Registrierung enthaltenen Zeitzoneninformationen vom Betriebssystem hauptsächlich verwendet, um Zeitanpassungen und Konvertierungen für das aktuelle Jahr zu verarbeiten. Dies hat zwei wesentliche Auswirkungen auf Anwendungen, die präzise Zeitzonendaten benötigen:
Möglicherweise ist eine Zeitzone, die für eine Anwendung erforderlich ist, nicht in der Registrierung definiert oder wurde umbenannt bzw. aus der Registrierung entfernt.
Für eine Zeitzone, die in der Registrierung definiert ist, fehlen möglicherweise Informationen zu speziellen Anpassungsregeln, die für zurückliegende Zeitzonenkonvertierungen erforderlich sind.
Die TimeZoneInfo-Klasse beseitigt diese Einschränkungen durch die Unterstützung der Serialisierung (Speicherung) und Deserialisierung (Wiederherstellung) von Zeitzonendaten.
Serialisierung und Deserialisierung von Zeitzonen
Das Speichern und Wiederherstellen einer Zeitzone durch Serialisieren und Deserialisieren von Zeitzonendaten umfasst lediglich zwei Methodenaufrufe:
Sie können ein TimeZoneInfo-Objekt serialisieren, indem Sie die ToSerializedString-Methode dieses Objekts aufrufen. Die Methode akzeptiert keine Parameter und gibt eine Zeichenfolge zurück, die Zeitzoneninformationen enthält.
Sie können ein TimeZoneInfo-Objekt aus einer serialisierten Zeichenfolge deserialisieren, indem Sie diese Zeichenfolge an die
static
TimeZoneInfo.FromSerializedString-Methode (Shared
in Visual Basic) übergeben.
Serialisierungs- und Deserialisierungsszenarien
Die Möglichkeit, ein TimeZoneInfo-Objekt in einer Zeichenfolge zu speichern (zu serialisieren) und es für die spätere Verwendung wiederherzustellen (zu deserialisieren), erweitert die Einsatzmöglichkeiten und die Flexibilität der TimeZoneInfo-Klasse. In diesem Abschnitt werden einige der Situationen untersucht, in denen Serialisierung und Deserialisierung am nützlichsten sind.
Serialisieren und Deserialisieren von Zeitzonendaten in einer Anwendung
Eine serialisierte Zeitzone kann bei Bedarf aus einer Zeichenfolge wiederhergestellt werden. Eine Anwendung kann dies ausführen, wenn die aus der Registrierung abgerufene Zeitzone ein Datum und eine Uhrzeit innerhalb eines bestimmten Datumsbereichs nicht ordnungsgemäß konvertieren kann. Beispielsweise unterstützen Zeitzonendaten in der Windows XP-Registrierung eine einzelne Anpassungsregel, während in der Windows Vista-Registrierung definierte Zeitzonen in der Regel Informationen zu zwei Anpassungsregeln bereitstellen. Dies bedeutet, dass frühere Zeitkonvertierungen möglicherweise ungenau sind. Die Serialisierung und Deserialisierung von Zeitzonendaten kann diese Einschränkung aufheben.
Im folgenden Beispiel wird eine benutzerdefinierte TimeZoneInfo-Klasse ohne Anpassungsregeln definiert, um die Zeitzone „Eastern Standard Time“ von 1883 bis 1917 vor der Einführung der Sommerzeit in der USA darzustellen. Die benutzerdefinierte Zeitzone wird in einer globalen Variable serialisiert. Der ConvertUtcTime
-Methode zur Zeitzonenkonvertierung werden die zu konvertierenden Zeitdaten in koordinierter Weltzeit (UTC) übergeben. Wenn Datum und die Uhrzeit im Jahr 1917 oder früher liegen, wird die benutzerdefinierte Zeitzone „Eastern Standard Time“ aus einer serialisierten Zeichenfolge wiederhergestellt, um die Zeitzone zu ersetzen, die aus der Registrierung abgerufen wurde.
using System;
public class TimeZoneSerialization
{
static string serializedEst;
public static void Main()
{
// Retrieve Eastern Standard Time zone from registry
try
{
TimeZoneSerialization tzs = new TimeZoneSerialization();
TimeZoneInfo est = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
// Create custom Eastern Time Zone for historical (pre-1918) conversions
CreateTimeZone();
// Call conversion function with one current and one pre-1918 date and time
Console.WriteLine(ConvertUtcTime(DateTime.UtcNow, est));
Console.WriteLine(ConvertUtcTime(new DateTime(1900, 11, 15, 9, 32, 00, DateTimeKind.Utc), est));
}
catch (TimeZoneNotFoundException)
{
Console.WriteLine("The Eastern Standard Time zone is not in the registry.");
}
catch (InvalidTimeZoneException)
{
Console.WriteLine("Data on the Eastern Standard Time Zone in the registry is corrupted.");
}
}
private static void CreateTimeZone()
{
// Create a simple Eastern Standard time zone
// without adjustment rules for 1883-1918
TimeZoneInfo earlyEstZone = TimeZoneInfo.CreateCustomTimeZone("Eastern Standard Time",
new TimeSpan(-5, 0, 0),
" (GMT-05:00) Eastern Time (United States)",
"Eastern Standard Time");
serializedEst = earlyEstZone.ToSerializedString();
}
private static DateTime ConvertUtcTime(DateTime utcDate, TimeZoneInfo tz)
{
// Use time zone object from registry
if (utcDate.Year > 1917)
{
return TimeZoneInfo.ConvertTimeFromUtc(utcDate, tz);
}
// Handle dates before introduction of DST
else
{
// Restore serialized time zone object
tz = TimeZoneInfo.FromSerializedString(serializedEst);
return TimeZoneInfo.ConvertTimeFromUtc(utcDate, tz);
}
}
}
Module TimeZoneSerialization
Dim serializedEst As String
Public Sub Main()
' Retrieve Eastern Standard Time zone from registry
Try
Dim est As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time")
' Create custom Eastern Time Zone for historical (pre-1918) conversions
CreateTimeZone()
' Call conversion function with one current and one pre-1918 date and time
Console.WriteLine(ConvertUtcTime(Date.UtcNow, est))
Console.WriteLine(ConvertUtcTime(New Date(1900, 11, 15, 9, 32, 00, DateTimeKind.Utc), est))
Catch e As TimeZoneNotFoundException
Console.WriteLine("The Eastern Standard Time zone is not in the registry.")
Catch e As InvalidTimeZoneException
Console.WriteLine("Data on the Eastern Standard Time Zone in the registry is corrupted.")
End Try
End Sub
Private Sub CreateTimeZone()
' Create a simple Eastern Standard time zone
' without adjustment rules for 1883-1918
Dim earlyEstZone As TimeZoneInfo = TimeZoneInfo.CreateCustomTimeZone("Eastern Standard Time", _
New TimeSpan(-5, 0, 0), _
" (GMT-05:00) Eastern Time (United States)", _
"Eastern Standard Time")
serializedEst = earlyEstZone.ToSerializedString()
End Sub
Private Function ConvertUtcTime(utcDate As Date, tz As TimeZoneInfo) As Date
' Use time zone object from registry
If Year(utcDate) > 1917 Then
Return TimeZoneInfo.ConvertTimeFromUtc(utcDate, tz)
' Handle dates before introduction of DST
Else
' Restore serialized time zone object
tz = TimeZoneInfo.FromSerializedString(serializedEst)
Return TimeZoneInfo.ConvertTimeFromUtc(utcDate, tz)
End If
End Function
End Module
Behandeln von Zeitzonenausnahmen
Da es sich bei der Registrierung um eine dynamische Struktur handelt, kann ihr Inhalt versehentlich oder absichtlich geändert werden. Dies kann dazu führen, dass eine Zeitzone, die in der Registrierung definiert sein sollte und die für die erfolgreiche Ausführung einer Anwendung erforderlich ist, möglicherweise nicht vorhanden ist. Ohne Unterstützung für die Zeitzonenserialisierung und -deserialisierung haben Sie kaum eine andere Wahl, als die resultierende TimeZoneNotFoundException zu behandeln, indem Sie die Anwendung beenden. Mithilfe der Zeitzonenserialisierung und -deserialisierung können Sie jedoch unerwartete TimeZoneNotFoundException verarbeiten, indem Sie die erforderliche Zeitzone aus einer serialisierten Zeichenfolge wiederherstellen, sodass die Anwendungsausführung fortgesetzt werden kann.
Im folgenden Beispiel wird die benutzerdefinierte Zeitzone „Central Standard Time“ erstellt und serialisiert. Anschließend wird versucht, die Zeitzone „Central Standard Time“ aus der Registrierung abzurufen. Wenn der Abrufvorgang eine TimeZoneNotFoundException oder eine InvalidTimeZoneException auslöst, deserialisiert der Ausnahmehandler die Zeitzone.
using System;
using System.Collections.Generic;
public class TimeZoneApplication
{
// Define collection of custom time zones
private Dictionary<string, string> customTimeZones = new Dictionary<string, string>();
private TimeZoneInfo cst;
public TimeZoneApplication()
{
// Create custom Central Standard Time
//
// Declare necessary TimeZoneInfo.AdjustmentRule objects for time zone
TimeZoneInfo customTimeZone;
TimeSpan delta = new TimeSpan(1, 0, 0);
TimeZoneInfo.AdjustmentRule adjustment;
List<TimeZoneInfo.AdjustmentRule> adjustmentList = new List<TimeZoneInfo.AdjustmentRule>();
// Declare transition time variables to hold transition time information
TimeZoneInfo.TransitionTime transitionRuleStart, transitionRuleEnd;
// Define end rule (for 1976-2006)
transitionRuleEnd = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 2, 0, 0), 10, 5, DayOfWeek.Sunday);
// Define rule (1976-1986)
transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 2, 0, 0), 04, 05, DayOfWeek.Sunday);
adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new DateTime(1976, 1, 1), new DateTime(1986, 12, 31), delta, transitionRuleStart, transitionRuleEnd);
adjustmentList.Add(adjustment);
// Define rule (1987-2006)
transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 2, 0, 0), 04, 01, DayOfWeek.Sunday);
adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new DateTime(1987, 1, 1), new DateTime(2006, 12, 31), delta, transitionRuleStart, transitionRuleEnd);
adjustmentList.Add(adjustment);
// Define rule (2007- )
transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 2, 0, 0), 03, 02, DayOfWeek.Sunday);
transitionRuleEnd = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 2, 0, 0), 11, 01, DayOfWeek.Sunday);
adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new DateTime(2007, 01, 01), DateTime.MaxValue.Date, delta, transitionRuleStart, transitionRuleEnd);
adjustmentList.Add(adjustment);
// Create custom U.S. Central Standard Time zone
customTimeZone = TimeZoneInfo.CreateCustomTimeZone("Central Standard Time",
new TimeSpan(-6, 0, 0),
"(GMT-06:00) Central Time (US Only)", "Central Standard Time",
"Central Daylight Time", adjustmentList.ToArray());
// Add time zone to collection
customTimeZones.Add(customTimeZone.Id, customTimeZone.ToSerializedString());
// Create any other required time zones
}
public static void Main()
{
TimeZoneApplication tza = new TimeZoneApplication();
tza.AppEntryPoint();
}
private void AppEntryPoint()
{
try
{
cst = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
}
catch (TimeZoneNotFoundException)
{
if (customTimeZones.ContainsKey("Central Standard Time"))
HandleTimeZoneException("Central Standard Time");
}
catch (InvalidTimeZoneException)
{
if (customTimeZones.ContainsKey("Central Standard Time"))
HandleTimeZoneException("Central Standard Time");
}
if (cst == null)
{
Console.WriteLine("Unable to load Central Standard Time zone.");
return;
}
DateTime currentTime = DateTime.Now;
Console.WriteLine("The current {0} time is {1}.",
TimeZoneInfo.Local.IsDaylightSavingTime(currentTime) ?
TimeZoneInfo.Local.StandardName :
TimeZoneInfo.Local.DaylightName,
currentTime.ToString("f"));
Console.WriteLine("The current {0} time is {1}.",
cst.IsDaylightSavingTime(currentTime) ?
cst.StandardName :
cst.DaylightName,
TimeZoneInfo.ConvertTime(currentTime, TimeZoneInfo.Local, cst).ToString("f"));
}
private void HandleTimeZoneException(string timeZoneName)
{
string tzString = customTimeZones[timeZoneName];
cst = TimeZoneInfo.FromSerializedString(tzString);
}
}
Imports System.Collections.Generic
Public Class TimeZoneApplication
' Define collection of custom time zones
Private customTimeZones As New Dictionary(Of String, String)
Private cst As TimeZoneInfo
Public Sub New()
' Define custom Central Standard Time
'
' Declare necessary TimeZoneInfo.AdjustmentRule objects for time zone
Dim customTimeZone As TimeZoneInfo
Dim delta As New TimeSpan(1, 0, 0)
Dim adjustment As TimeZoneInfo.AdjustmentRule
Dim adjustmentList As New List(Of TimeZoneInfo.AdjustmentRule)
' Declare transition time variables to hold transition time information
Dim transitionRuleStart, transitionRuleEnd As TimeZoneInfo.TransitionTime
' Define end rule (for 1976-2006)
transitionRuleEnd = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(#02:00:00AM#, 10, 5, DayOfWeek.Sunday)
' Define rule (1976-1986)
transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(#2:00:00AM#, 04, 05, DayOfWeek.Sunday)
adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(#01/01/1976#, #12/31/1986#, delta, transitionRuleStart, transitionRuleEnd)
adjustmentList.Add(adjustment)
' Define rule (1987-2006)
transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(#2:00:00AM#, 04, 01, DayOfWeek.Sunday)
adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(#01/01/1987#, #12/31/2006#, delta, transitionRuleStart, transitionRuleEnd)
adjustmentList.Add(adjustment)
' Define rule (2007- )
transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(#2:00:00AM#, 03, 02, DayOfWeek.Sunday)
transitionRuleEnd = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(#2:00:00AM#, 11, 01, DayOfWeek.Sunday)
adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(#01/01/2007#, Date.MaxValue.Date, delta, transitionRuleStart, transitionRuleEnd)
adjustmentList.Add(adjustment)
' Create custom time zone
customTimeZone = TimeZoneInfo.CreateCustomTimeZone("Central Standard Time", _
New TimeSpan(-6, 0, 0), _
"(GMT-06:00) Central Time (US Only)", "Central Standard Time", _
"Central Daylight Time", adjustmentList.ToArray())
' Add time zone to collection
customTimeZones.Add(customTimeZone.Id, customTimeZone.ToSerializedString())
' Create any other required time zones
End Sub
Public Shared Sub Main()
Dim tza As New TimeZoneApplication()
tza.AppEntryPoint()
End Sub
Private Sub AppEntryPoint()
Try
cst = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")
Catch e As TimeZoneNotFoundException
If customTimeZones.ContainsKey("Central Standard Time")
HandleTimeZoneException("Central Standard Time")
End If
Catch e As InvalidTimeZoneException
If customTimeZones.ContainsKey("Central Standard Time")
HandleTimeZoneException("Central Standard Time")
End If
End Try
If cst Is Nothing Then
Console.WriteLine("Unable to load Central Standard Time zone.")
Return
End If
Dim currentTime As Date = Date.Now
Console.WriteLine("The current {0} time is {1}.", _
IIf(TimeZoneInfo.Local.IsDaylightSavingTime(currentTime), _
TimeZoneInfo.Local.StandardName, _
TimeZoneInfo.Local.DaylightName), _
currentTime.ToString("f"))
Console.WriteLine("The current {0} time is {1}.", _
IIf(cst.IsDaylightSavingTime(currentTime), _
cst.StandardName, _
cst.DaylightName), _
TimeZoneInfo.ConvertTime(currentTime, TimeZoneInfo.Local, cst).ToString("f"))
End Sub
Private Sub HandleTimeZoneException(timeZoneName As String)
Dim tzString As String = customTimeZones.Item(timeZoneName)
cst = TimeZoneInfo.FromSerializedString(tzString)
End Sub
End Class
Speichern einer serialisierten Zeichenfolge und Wiederherstellung bei Bedarf
In den vorherigen Beispielen wurden Zeitzoneninformationen in einer Zeichenfolgenvariable gespeichert und bei Bedarf wiederhergestellt. Die Zeichenfolge mit den serialisierten Zeitzoneninformationen kann jedoch selbst auf einem Speichermedium gespeichert werden, z. B. in einer externen Datei, einer in die Anwendung eingebetteten Ressourcendatei oder der Registrierung. (Beachten Sie, dass Informationen zu benutzerdefinierten Zeitzonen nicht in den Zeitzonenschlüsseln des Systems in der Registrierung gespeichert werden sollten.)
Durch dieses Verfahren zur Speicherung einer serialisierten Zeitzonenzeichenfolge wird auch die Routine zur Zeitzonenerstellung von der Anwendung selbst getrennt. So könnte beispielsweise eine Routine zur Zeitzonenerstellung eine Datendatei ausführen und erstellen, die historische Zeitzoneninformationen enthält, die von Anwendungen verwenden werden können. Die Datendatei kann dann mit der Anwendung installiert werden, von der sie geöffnet werden kann, um Zeitzonen zu deserialisieren, wenn die Anwendung sie benötigt.
Ein Beispiel, das eine eingebettete Ressource zum Speichern serialisierter Zeitzonendaten verwendet, finden Sie unter Speichern von Zeitzonen in einer eingebetteten Ressource und Wiederherstellen von Zeitzonen aus einer eingebetteten Ressource.