Tijdzones opslaan en herstellen
De TimeZoneInfo klasse is afhankelijk van het register om vooraf gedefinieerde tijdzonegegevens op te halen. Het register is echter een dynamische structuur. Daarnaast wordt de tijdzone-informatie die het register bevat, voornamelijk door het besturingssysteem gebruikt om tijdaanpassingen en conversies voor het huidige jaar af te handelen. Dit heeft twee belangrijke gevolgen voor toepassingen die afhankelijk zijn van nauwkeurige tijdzonegegevens:
Een tijdzone die door een toepassing is vereist, kan niet worden gedefinieerd in het register of de naam ervan is gewijzigd of verwijderd uit het register.
Een tijdzone die in het register is gedefinieerd, kan geen informatie bevatten over de specifieke aanpassingsregels die nodig zijn voor historische tijdzoneconversies.
De TimeZoneInfo klasse behandelt deze beperkingen via de ondersteuning voor serialisatie (opslaan) en deserialisatie (herstellen) van tijdzonegegevens.
Serialisatie en deserialisatie van tijdzones
Het opslaan en herstellen van een tijdzone door het serialiseren en deserialiseren van tijdzonegegevens omvat slechts twee methodeaanroepen:
U kunt een TimeZoneInfo object serialiseren door de methode van ToSerializedString dat object aan te roepen. De methode gebruikt geen parameters en retourneert een tekenreeks die tijdzone-informatie bevat.
U kunt een TimeZoneInfo object deserialiseren vanuit een geserialiseerde tekenreeks door die tekenreeks door te geven aan de
static
methode (Shared
in Visual Basic). TimeZoneInfo.FromSerializedString
Serialisatie- en deserialisatiescenario's
De mogelijkheid om een TimeZoneInfo object op te slaan (of te serialiseren) naar een tekenreeks en om het te herstellen (of deserialiseren) voor later gebruik verhoogt zowel het hulpprogramma als de flexibiliteit van de TimeZoneInfo klasse. In deze sectie worden enkele van de situaties onderzocht waarin serialisatie en deserialisatie het nuttigst zijn.
Tijdzonegegevens serialiseren en deserialiseren in een toepassing
Een geserialiseerde tijdzone kan worden hersteld vanuit een tekenreeks wanneer deze nodig is. Een toepassing kan dit doen als de tijdzone die is opgehaald uit het register, een datum en tijd binnen een bepaald datumbereik niet correct kan converteren. Tijdzonegegevens in het Windows XP-register ondersteunen bijvoorbeeld één aanpassingsregel, terwijl tijdzones die in het Windows Vista-register zijn gedefinieerd, doorgaans informatie bevatten over twee aanpassingsregels. Dit betekent dat historische tijdconversies mogelijk onnauwkeurig zijn. Serialisatie en deserialisatie van tijdzonegegevens kunnen deze beperking afhandelen.
In het volgende voorbeeld wordt een aangepaste TimeZoneInfo klasse die geen aanpassingsregels heeft gedefinieerd om de zone U.S. Eastern Standard Time van 1883 tot 1917 weer te geven vóór de introductie van zomertijd in de Verenigde Staten. De aangepaste tijdzone wordt geserialiseerd in een variabele met globaal bereik. De tijdzoneconversiemethode, wordt ConvertUtcTime
UTC-tijden (Coordinated Universal Time) doorgegeven om te converteren. Als de datum en tijd plaatsvinden in 1917 of eerder, wordt de aangepaste Eastern Standard Time-zone hersteld van een geserialiseerde tekenreeks en wordt de tijdzone vervangen die uit het register is opgehaald.
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
Tijdzone-uitzonderingen verwerken
Omdat het register een dynamische structuur is, kan de inhoud per ongeluk of opzettelijk worden gewijzigd. Dit betekent dat een tijdzone die moet worden gedefinieerd in het register en dat vereist is voor een toepassing die kan worden uitgevoerd, afwezig is. Zonder ondersteuning voor serialisatie en deserialisatie van tijdzones hebt u weinig keuze, maar kunt u het resultaat TimeZoneNotFoundException afhandelen door de toepassing te beëindigen. Door tijdzones te serialiseren en deserialisatie te gebruiken, kunt u echter een onverwachte TimeZoneNotFoundException bewerking uitvoeren door de vereiste tijdzone te herstellen vanuit een geserialiseerde tekenreeks en blijft de toepassing actief.
In het volgende voorbeeld wordt een aangepaste Central Standard Time-zone gemaakt en geserialiseerd. Vervolgens wordt geprobeerd de Central Standard Time-zone op te halen uit het register. Als de ophaalbewerking een TimeZoneNotFoundException of een InvalidTimeZoneExceptiongenereert, wordt de tijdzone gedeserialiseerd door de uitzonderingshandler.
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
Een geserialiseerde tekenreeks opslaan en deze herstellen wanneer dat nodig is
In de vorige voorbeelden zijn tijdzonegegevens opgeslagen in een tekenreeksvariabele en hersteld wanneer dat nodig is. De tekenreeks met geserialiseerde tijdzonegegevens kan echter zelf worden opgeslagen in een opslagmedium, zoals een extern bestand, een bronbestand dat is ingesloten in de toepassing of het register. (Houd er rekening mee dat informatie over aangepaste tijdzones moet worden opgeslagen, behalve de tijdzonesleutels van het systeem in het register.)
Als u een geserialiseerde tijdzonetekenreeks op deze manier opslaat, wordt ook de routine voor het maken van tijdzones gescheiden van de toepassing zelf. Een routine voor het maken van een tijdzone kan bijvoorbeeld een gegevensbestand uitvoeren en maken dat historische tijdzonegegevens bevat die een toepassing kan gebruiken. Het gegevensbestand kan vervolgens worden geïnstalleerd met de toepassing en kan worden geopend en een of meer van de tijdzones kunnen worden gedeserialiseerd wanneer de toepassing deze nodig heeft.
Voor een voorbeeld waarin een ingesloten resource wordt gebruikt om geserialiseerde tijdzonegegevens op te slaan, raadpleegt u Het volgende: Tijdzones opslaan in een ingesloten resource en Procedures: Tijdzones herstellen vanuit een ingesloten resource.