Menyimpan dan memulihkan zona waktu
Kelas TimeZoneInfo bergantung pada registri untuk mengambil data zona waktu yang telah ditentukan sebelumnya. Namun, registri adalah struktur dinamis. Selain itu, informasi zona waktu yang dikandung registri digunakan oleh sistem operasi terutama untuk menangani penyesuaian waktu dan konversi untuk tahun ini. Ini memiliki dua implikasi utama untuk aplikasi yang mengandalkan data zona waktu yang akurat:
Zona waktu yang diperlukan oleh aplikasi tidak dapat ditentukan dalam registri, atau telah diganti namanya atau dihapus dari registri.
Zona waktu yang ditentukan dalam registri tidak dapat memiliki informasi tentang aturan penyesuaian tertentu yang diperlukan untuk konversi zona waktu historis.
Kelas TimeZoneInfo membahas batasan ini melalui dukungannya untuk serialisasi (menyimpan) dan deserialisasi (memulihkan) data zona waktu.
Serialisasi dan deserialisasi zona waktu
Menyimpan dan memulihkan zona waktu dengan menserialisasikan dan mendeserialisasi data zona waktu hanya melibatkan dua panggilan metode:
Anda dapat menserialisasikan TimeZoneInfo objek dengan memanggil metode objek tersebut ToSerializedString. Metode ini tidak mengambil parameter dan mengembalikan string yang berisi informasi zona waktu.
Anda dapat mendeserialisasi TimeZoneInfo objek dari string berseri dengan meneruskan string tersebut
static
ke metode (Shared
dalam Visual BasicTimeZoneInfo.FromSerializedString).
Skenario serialisasi dan deserialisasi
Kemampuan untuk menyimpan (atau menserialisasikan) TimeZoneInfo objek ke string dan memulihkan (atau mendeserialisasi) untuk digunakan nanti meningkatkan utilitas dan fleksibilitas TimeZoneInfo kelas. Bagian ini memeriksa beberapa situasi di mana serialisasi dan deserialisasi paling berguna.
Menserialisasikan dan mendeserialisasi data zona waktu dalam aplikasi
Zona waktu berseri dapat dipulihkan dari string saat diperlukan. Aplikasi dapat melakukan ini jika zona waktu yang diambil dari registri tidak dapat mengonversi tanggal dan waktu dengan benar dalam rentang tanggal tertentu. Misalnya, data zona waktu di registri XP Windows mendukung aturan penyesuaian tunggal, sementara zona waktu yang ditentukan dalam registri Windows Vista biasanya memberikan informasi tentang dua aturan penyesuaian. Ini berarti bahwa konversi waktu historis bisa tidak akurat. Serialisasi dan deserialisasi data zona waktu dapat menangani batasan ini.
Dalam contoh berikut, kelas kustom TimeZoneInfo yang tidak memiliki aturan penyesuaian didefinisikan untuk mewakili zona Waktu Standar Timur AS dari 1883 hingga 1917, sebelum pengenalan waktu musim panas di Amerika Serikat. Zona waktu kustom diserialisasikan dalam variabel yang memiliki cakupan global. Metode konversi zona waktu, ConvertUtcTime
, dilewatkan waktu Waktu Universal Terkoordinasi untuk dikonversi. Jika tanggal dan waktu terjadi pada tahun 1917 atau lebih lama, zona Waktu Standar Timur kustom dipulihkan dari string berseri dan menggantikan zona waktu yang diambil dari registri.
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
Menangani pengecualian zona waktu
Karena registri adalah struktur dinamis, kontennya tunduk pada modifikasi yang tidak disengaja atau disengaja. Ini berarti bahwa zona waktu yang harus didefinisikan dalam registri dan yang diperlukan agar aplikasi berhasil dijalankan mungkin tidak ada. Tanpa dukungan untuk serialisasi zona waktu dan deserialisasi, Anda memiliki sedikit pilihan tetapi untuk menangani yang dihasilkan TimeZoneNotFoundException dengan mengakhiri aplikasi. Namun, dengan menggunakan serialisasi dan deserialisasi zona waktu, Anda dapat menangani hal yang tidak terduga TimeZoneNotFoundException dengan memulihkan zona waktu yang diperlukan dari string yang diserialisasikan, dan aplikasi akan terus berjalan.
Contoh berikut membuat dan menserialisasikan zona Waktu Standar Pusat kustom. Kemudian mencoba mengambil zona Waktu Standar Pusat dari registri. Jika operasi pengambilan melempar TimeZoneNotFoundException atau InvalidTimeZoneException, handler pengecualian mendeserialisasi zona waktu.
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
Menyimpan string berseri dan memulihkannya saat diperlukan
Contoh sebelumnya telah menyimpan informasi zona waktu ke variabel string dan memulihkannya saat diperlukan. Namun, string yang berisi informasi zona waktu berseri dapat disimpan di beberapa media penyimpanan, seperti file eksternal, file sumber daya yang disematkan dalam aplikasi, atau registri. (Perhatikan bahwa informasi tentang zona waktu kustom harus disimpan selain dari kunci zona waktu sistem di registri.)
Menyimpan string zona waktu berseri dengan cara ini juga memisahkan rutinitas pembuatan zona waktu dari aplikasi itu sendiri. Misalnya, rutinitas pembuatan zona waktu dapat menjalankan dan membuat file data yang berisi informasi zona waktu historis yang dapat digunakan aplikasi. File data kemudian dapat diinstal dengan aplikasi, dan dapat dibuka dan satu atau beberapa zona waktunya dapat dideserialisasi ketika aplikasi memerlukannya.
Untuk contoh yang menggunakan sumber daya yang disematkan untuk menyimpan data zona waktu serial, lihat Cara: Menyimpan zona waktu ke sumber daya yang disematkan dan Cara: Memulihkan zona waktu dari sumber daya yang disematkan.