Поделиться через


Практическое руководство. Сохранение часовых поясов во внедренном ресурсе

Часто приложению, работающему со временем, требуется наличие определенного часового пояса. Однако поскольку доступность отдельных объектов TimeZoneInfo зависит от сведений, хранящихся в локальном реестре пользователя, даже настраиваемые доступные часовые пояса могут отсутствовать. Кроме того, сведения о пользовательских часовых поясах, созданных с помощью метода CreateCustomTimeZone, не хранятся с другими сведениями о часовом поясе в реестре. Чтобы гарантировать, что эти часовые пояса будут доступны, когда потребуется, их можно сериализовать и сохранить, а позже десериализовать и восстановить.

Обычно сериализация объекта TimeZoneInfo происходит отдельно от приложения, соответствующего часовому поясу. В зависимости от хранилища данных, используемого для хранения сериализованных объектов TimeZoneInfo, данные о часовых поясах можно сериализовывать в процессе установки (например, при сохранении данных в ключе реестра), или как часть служебной процедуры, запускаемой перед окончательной компиляцией приложения (например, когда сериализованные данные хранятся в файле ресурсов (.resX) .NET XML).

В дополнение к файлу ресурсов, который компилируется с приложением, для хранения сведений о часовом поясе могут использоваться некоторые другие хранилища. В их числе:

  • Реестр. Обратите внимание, что для хранения пользовательских данных о часовом поясе приложение должно использовать вложенные разделы своего собственного реестра приложения вместо использования подразделов HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Time Zones.

  • Файлы конфигурации.

  • Другие системные файлы.

Для сохранения часового пояса путем сериализации его в файле .resx

  1. Извлеките существующий часовой пояс или создайте новый часовой пояс.

    Сведения об извлечении текущего часового пояса см. в разделах Практическое руководство. Доступ к предварительно определенным объектам UTC и объектам местных часовых поясов и Практическое руководство. Создание экземпляра объекта TimeZoneInfo.

    Чтобы создать новый часовой пояс, вызовите один из вариантов перегруженного метода CreateCustomTimeZone. Дополнительные сведения см. в разделах Практическое руководство. Создание часовых поясов без правил коррекции и Практическое руководство. Создание часовых поясов с правилами коррекции.

  2. Вызовите метод ToSerializedString для создания строки, содержащей данные часового пояса.

  3. Создайте объект StreamWriter, передав имя и при необходимости путь к файлу .resx в конструктор класса StreamWriter.

  4. Создайте объект ResXResourceWriter путем передачи объекта StreamWriter в конструктор класса ResXResourceWriter.

  5. Передайте сериализованную строку часового пояса в метод ResXResourceWriter.AddResource.

  6. Вызовите метод ResXResourceWriter.Generate.

  7. Вызовите метод ResXResourceWriter.Close.

  8. Закройте объект StreamWriter путем вызова его метода Close.

  9. Добавьте созданный файл .resx в проект приложения Visual Studio.

  10. С помощью окна Свойства в Visual Studio, убедитесь, что свойство Действия при построении этого файла .resx задано как Внедренный ресурс.

Пример

В следующем примере объект TimeZoneInfo, представляющий центральное стандартное время и объект TimeZoneInfo, представляющий антарктическое время, сериализуются в файле .NET XML ресурсов под именем SerializedTimeZones.resx. Центральное стандартное время обычно определено в реестре; антарктическое время является пользовательским часовым поясом.

Private Sub SerializeTimeZones()
   Dim writeStream As TextWriter
   Dim resources As New Dictionary(Of String, String)
   ' Determine if .resx file exists
   If File.Exists(resxName) Then
      ' Open reader
      Dim readStream As TextReader = New StreamReader(resxName)
      Dim resReader As New ResXResourceReader(readStream)
      For Each item As DictionaryEntry In resReader
         If Not (CStr(item.Key) = "CentralStandardTime" Or _
                 CStr(item.Key) = "PalmerStandardTime") Then
            resources.Add(CStr(item.Key), CStr(item.Value))
         End If
      Next
      readStream.Close()
      ' Delete file, since write method creates duplicate xml headers
      File.Delete(resxName)
   End If

   ' Open stream to write to .resx file
   Try
      writeStream = New StreamWriter(resxName, True)
   Catch e As FileNotFoundException
      ' Handle failure to find file
      Console.WriteLine("{0}: The file {1} could not be found.", e.GetType().Name, resxName)
      Exit Sub
   End Try

   ' Get resource writer
   Dim resWriter As ResXResourceWriter = New ResXResourceWriter(writeStream)

   ' Add resources from existing file
   For Each item As KeyValuePair(Of String, String) In resources
      resWriter.AddResource(item.Key, item.Value)
   Next
   ' Serialize Central Standard Time
   Try
      Dim cst As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")
      resWriter.AddResource(cst.Id.Replace(" ", String.Empty), cst.ToSerializedString())
   Catch
      Console.WriteLine("The Central Standard Time zone could not be found.")
   End Try

   ' Create time zone for Palmer, Antarctica
   '
   ' Define transition times to/from DST
   Dim startTransition As TimeZoneInfo.TransitionTime = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(#4:00:00 AM#, 10, 2, DayOfWeek.Sunday)
   Dim endTransition As TimeZoneInfo.TransitionTime = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(#3:00:00 AM#, 3, 2, DayOfWeek.Sunday)
   ' Define adjustment rule
   Dim delta As TimeSpan = New TimeSpan(1, 0, 0)
   Dim adjustment As TimeZoneInfo.AdjustmentRule = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(#10/1/1999#, Date.MaxValue.Date, delta, startTransition, endTransition)
   ' Create array for adjustment rules
   Dim adjustments() As TimeZoneInfo.AdjustmentRule = {adjustment}
   ' Define other custom time zone arguments
   Dim DisplayName As String = "(GMT-04:00) Antarctica/Palmer Time"
   Dim standardName As String = "Palmer Standard Time"
   Dim daylightName As String = "Palmer Daylight Time"
   Dim offset As TimeSpan = New TimeSpan(-4, 0, 0)
   Dim palmer As TimeZoneInfo = TimeZoneInfo.CreateCustomTimeZone(standardName, offset, DisplayName, standardName, daylightName, adjustments)
   resWriter.AddResource(palmer.Id.Replace(" ", String.Empty), palmer.ToSerializedString())

   ' Save changes to .resx file 
   resWriter.Generate()
   resWriter.Close()
   writeStream.Close()
End Sub
TimeZoneSerialization()
{
   TextWriter writeStream;
   Dictionary<string, string> resources = new Dictionary<string, string>();
   // Determine if .resx file exists
   if (File.Exists(resxName))
   {
      // Open reader
      TextReader readStream = new StreamReader(resxName);
      ResXResourceReader resReader = new ResXResourceReader(readStream);
      foreach (DictionaryEntry item in resReader)
      {
         if (! (((string) item.Key) == "CentralStandardTime" || 
                ((string) item.Key) == "PalmerStandardTime" )) 
            resources.Add((string)item.Key, (string) item.Value);
      }
      readStream.Close();
      // Delete file, since write method creates duplicate xml headers
      File.Delete(resxName);
   }

   // Open stream to write to .resx file
   try
   {
      writeStream = new StreamWriter(resxName, true);
   }
   catch (FileNotFoundException e)
   {
      // Handle failure to find file
      Console.WriteLine("{0}: The file {1} could not be found.", e.GetType().Name, resxName);
      return;
   }

   // Get resource writer
   ResXResourceWriter resWriter = new ResXResourceWriter(writeStream);

   // Add resources from existing file
   foreach (KeyValuePair<string, string> item in resources)
   {
      resWriter.AddResource(item.Key, item.Value);
   }

   // Serialize Central Standard Time
   try
   {
      TimeZoneInfo cst = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
      resWriter.AddResource(cst.Id.Replace(" ", string.Empty), cst.ToSerializedString());
   }
   catch (TimeZoneNotFoundException)
   {
      Console.WriteLine("The Central Standard Time zone could not be found.");
   }

   // Create time zone for Palmer, Antarctica
   //
   // Define transition times to/from DST
   TimeZoneInfo.TransitionTime startTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 4, 0, 0), 
                                                                                              10, 2, DayOfWeek.Sunday);
   TimeZoneInfo.TransitionTime endTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 3, 0, 0), 
                                                                                            3, 2, DayOfWeek.Sunday);
   // Define adjustment rule
   TimeSpan delta = new TimeSpan(1, 0, 0);
   TimeZoneInfo.AdjustmentRule adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new DateTime(1999, 10, 1), 
                                         DateTime.MaxValue.Date, delta, startTransition, endTransition);
   // Create array for adjustment rules
   TimeZoneInfo.AdjustmentRule[] adjustments = {adjustment};
   // Define other custom time zone arguments
   string DisplayName = "(GMT-04:00) Antarctica/Palmer Time";
   string standardName = "Palmer Standard Time";
   string daylightName = "Palmer Daylight Time";
   TimeSpan offset = new TimeSpan(-4, 0, 0);
   TimeZoneInfo palmer = TimeZoneInfo.CreateCustomTimeZone(standardName, offset, DisplayName, standardName, daylightName, adjustments);
   resWriter.AddResource(palmer.Id.Replace(" ", String.Empty), palmer.ToSerializedString());

   // Save changes to .resx file 
   resWriter.Generate();
   resWriter.Close();
   writeStream.Close();
}

Этот пример сериализует объекты TimeZoneInfo таким образом, что они становятся доступными в файле ресурсов во время компиляции.

Поскольку метод ResXResourceWriter.Generate добавляет полные сведения заголовка в файл ресурсов .NET XML, то он не может использоваться для добавления ресурсов в существующий файл. Этот пример проводит обработку путем проверки файла SerializedTimeZones.resx и, если он существует, сохранения всех его ресурсов, отличных от этих двух сериализованных часовых поясов в универсальный объект Dictionary<TKey, TValue>. Затем существующий файл удаляется, и существующие ресурсы добавляются в новый файл SerializedTimeZones.resx. Сериализованные данные часового пояса также добавляются в этот файл.

Ключ (или Имя) полей ресурсов не должен содержать пробелы. Метод Replace(String, String) вызывается для удаления всех пробелов в идентификаторах часовых поясов, прежде чем они помещаются в файл ресурсов.

Компиляция кода

Для этого примера требуется:

  • Чтобы ссылки на System.Windows.Forms.dll и System.Core.dll были добавлены в проект.

  • Чтобы был осуществлен импорт следующих пространств имен:

    Imports System.Globalization
    Imports System.IO
    Imports System.Reflection
    Imports System.Resources
    
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Reflection;
    using System.Resources;
    using System.Windows.Forms;
    

См. также

Задачи

Практическое руководство. Восстановление часовых поясов из внедренного ресурса

Основные понятия

Общие сведения о часовых поясах

Другие ресурсы

Даты, время и часовые пояса