چند سالی هست (از سال 2009) که آپدیتهای daylight saving time ویندوز شامل حال تنظیمات رسمی ایران نمیشود. برای نمونه، همین یکی دو روز قبل بود که ساعت ویندوز به صورت خودکار تغییر کرد؛ درحالیکه باید در انتهای روز 30 شهریور اینکار صورت میگرفت.
اطلاعات daylight saving time یا بازه صرفه جویی زمانی ویندوز در دو مدخل رجیستری زیر ثبت میشوند:
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation]
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\Iran Standard Time]
تصویر سوم مرتبط است به ویندوزهای ویستا به بعد که مفهوم dynamic daylight saving time در آنها معرفی شده است.
در اینجا یک نمونه اطلاعات زمانی ثبت شده مرتبط با ایران را مشاهده میکنید:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\Iran Standard Time]
"Display"="(GMT+03:30) Tehran"
"Dlt"="Iran Daylight Time"
"Std"="Iran Standard Time"
"MapID"="-1,72"
"Index"=dword:000000a0
"TZI"=hex:2e,ff,ff,ff,00,00,00,00,c4,ff,ff,ff,00,00,09,00,04,00,03,00,17,00,3b,\
00,3b,00,00,00,00,00,03,00,02,00,03,00,17,00,3b,00,3b,00,00,00
TZI ایی که در اینجا وجود دارد، دارای یک چنین ساختاری است:
using System.Runtime.InteropServices;
namespace TimeZoneInfo.Core
{
[StructLayout(LayoutKind.Sequential)]
public struct TZI
{
public int Bias;
public int StandardBias;
public int DaylightBias;
public SystemTime StandardDate;
public SystemTime DaylightDate;
}
}
و SystemTime آن نیز به نحو زیر تعریف شده است:
using System;
using System.Runtime.InteropServices;
namespace TimeZoneInfo.Core
{
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct SystemTime
{
public short Year;
public short Month;
public short DayOfWeek;
public short Day;
public short Hour;
public short Minute;
public short Second;
public short Milliseconds;
}
}
برای مثال اگر اطلاعات درج شده در TZI به صورت زیر باشد:
2C 01 00 00 00 00 00 00
C4 FF FF FF 00 00 0A 00
00 00 05 00 02 00 00 00
00 00 00 00 00 00 04 00
00 00 01 00 02 00 00 00
00 00 00 00
نمونه رمزگشایی شده آن به نحو ذیل خواهد بود:
(little-endian) => (big-endian)
2C 01 00 00 => 00 00 01 2C = 300 Bias
00 00 00 00 => 00 00 00 00 = 0 Std Bias
C4 FF FF FF => FF FF FF C4 = 4294967236 Dlt Bias
( SYSTEM TIME ) StandardDate
00 00 => 00 00 = Year
0A 00 => 00 0A = Month
00 00 => 00 00 = Day of Week
05 00 => 00 05 = Day
02 00 => 00 02 = Hour
00 00 => 00 00 = Minutes
00 00 => 00 00 = Seconds
00 00 => 00 00 = Milliseconds
( SYSTEM TIME ) DaylightDate
00 00 => 00 00 = Year
04 00 => 00 04 = Month
00 00 => 00 00 = Day of Week
01 00 => 00 01 = Day
02 00 => 00 02 = Hour
00 00 => 00 00 = Minutes
00 00 => 00 00 = Seconds
00 00 => 00 00 = Milliseconds
در ساختار SystemTime متناظر با TZI، فیلد Day دارای مقدار روز در یک ماه نیست. به معنای شماره هفته است. مثلا پنج شنبه (DayOfWeek) هفته سوم (Day) ماه 9 سال 2012.
همچنین Day از یک شروع میشود و DayOfWeek از صفر. Year اگر صفر وارد شود به معنای زمان نسبی است و برای سال بعد نیز میتواند کاربرد داشته باشد (و عموما صفر تعریف شده است).
بنابراین برای تبدیل DateTime به SystemTime سازگار با TZI به فرمول زیر خواهیم رسید:
public static SystemTime ToSystemTime(DateTime time)
{
var result = new SystemTime
{
Year = 0, // سال نسبی وارد میشود نه مطلق
Month = (short)time.Month,
DayOfWeek = (short)time.DayOfWeek,
Hour = (short)time.Hour,
Minute = (short)time.Minute,
Second = (short)time.Second,
Milliseconds = (short)time.Millisecond
};
int weekdayOfMonth = 1; // شماره هفته است نه شماره روز
for (int dd = time.Day; dd > 7; dd -= 7)
weekdayOfMonth++;
result.Day = (short)weekdayOfMonth;
return result;
}
در ادامه نیاز خواهیم داشت تا ساختار TZI سفارشی و بازسازی شده خودمان را بتوانیم به آرایهای از بایتها تبدیل کنیم تا بتوان در همان مدخل رجیستری نوشت. اینکار را توسط متد SerializeByteArray زیر میتوان انجام داد:
using System;
using System.Runtime.InteropServices;
namespace TimeZoneInfo.Core
{
public static class ByteUtils
{
public static Byte[] SerializeByteArray<T>(T msg) where T : struct
{
int objsize = Marshal.SizeOf(typeof(T));
Byte[] ret = new Byte[objsize];
IntPtr buff = Marshal.AllocHGlobal(objsize);
Marshal.StructureToPtr(msg, buff, true);
Marshal.Copy(buff, ret, 0, objsize);
Marshal.FreeHGlobal(buff);
return ret;
}
}
}
و اگر اینکار را تا بیش از 90 سال بعد بر اساس تاریخ ایران انجام داده و مداخل رجیستری ویندوز را تکمیل کنیم، خروجی آن فایل reg زیر خواهد بود که به سادگی با کلیک راست و انتخاب گزینهی merge به رجیستری ویندوز اضافه شده و تا چندین سال بعد، مشکل تنظیمات DST را برطرف خواهد کرد:
پس از اعمال تغییرات فوق، نیاز است یکبار ویندوز را ری استارت کنید.