// // TryCompareStandardDate - // // Helper function that compares the StandardBias and StandardDate portion a // TimeZoneInformation struct to a time zone registry entry // private static Boolean TryCompareStandardDate(TIME_ZONE_INFORMATION timeZone, REGISTRY_TIME_ZONE_INFORMATION registryTimeZoneInfo) { return timeZone.Bias == registryTimeZoneInfo.Bias && timeZone.StandardBias == registryTimeZoneInfo.StandardBias && timeZone.StandardDate.wYear == registryTimeZoneInfo.StandardDate.wYear && timeZone.StandardDate.wMonth == registryTimeZoneInfo.StandardDate.wMonth && timeZone.StandardDate.wDayOfWeek == registryTimeZoneInfo.StandardDate.wDayOfWeek && timeZone.StandardDate.wDay == registryTimeZoneInfo.StandardDate.wDay && timeZone.StandardDate.wHour == registryTimeZoneInfo.StandardDate.wHour && timeZone.StandardDate.wMinute == registryTimeZoneInfo.StandardDate.wMinute && timeZone.StandardDate.wSecond == registryTimeZoneInfo.StandardDate.wSecond && timeZone.StandardDate.wMilliseconds == registryTimeZoneInfo.StandardDate.wMilliseconds; }
// // TryCompareTimeZoneInformationToRegistry - // // Helper function that compares a TimeZoneInformation struct to a time zone registry entry // static unsafe private Boolean TryCompareTimeZoneInformationToRegistry(TIME_ZONE_INFORMATION timeZone, string id, out Boolean dstDisabled) { dstDisabled = false; using (RegistryKey key = RegistryKey.GetBaseKey(RegistryKey.HKEY_LOCAL_MACHINE).OpenSubKey( c_timeZonesRegistryHive + "\\" + id, false )) { if (key == null) { return false; } REGISTRY_TIME_ZONE_INFORMATION registryTimeZoneInfo; Byte[] regValue = (Byte[])key.GetValue(c_timeZoneInfoValue, null, RegistryValueOptions.None) as Byte[]; if (regValue == null || regValue.Length != c_regByteLength) return false; registryTimeZoneInfo = new REGISTRY_TIME_ZONE_INFORMATION(regValue); // // first compare the bias and standard date information between the data from the Win32 API // and the data from the registry... // Boolean result = TryCompareStandardDate(timeZone, registryTimeZoneInfo); if (!result) { return false; } result = dstDisabled || CheckDaylightSavingTimeNotSupported(timeZone) // // since Daylight Saving Time is not "disabled", do a straight comparision between // the Win32 API data and the registry data ... // || (timeZone.DaylightBias == registryTimeZoneInfo.DaylightBias && timeZone.DaylightDate.wYear == registryTimeZoneInfo.DaylightDate.wYear && timeZone.DaylightDate.wMonth == registryTimeZoneInfo.DaylightDate.wMonth && timeZone.DaylightDate.wDayOfWeek == registryTimeZoneInfo.DaylightDate.wDayOfWeek && timeZone.DaylightDate.wDay == registryTimeZoneInfo.DaylightDate.wDay && timeZone.DaylightDate.wHour == registryTimeZoneInfo.DaylightDate.wHour && timeZone.DaylightDate.wMinute == registryTimeZoneInfo.DaylightDate.wMinute && timeZone.DaylightDate.wSecond == registryTimeZoneInfo.DaylightDate.wSecond && timeZone.DaylightDate.wMilliseconds == registryTimeZoneInfo.DaylightDate.wMilliseconds); // Finally compare the "StandardName" string value... // // we do not compare "DaylightName" as this TimeZoneInformation field may contain // either "StandardName" or "DaylightName" depending on the time of year and current machine settings // if (result) { String registryStandardName = key.GetValue(c_standardValue, String.Empty, RegistryValueOptions.None) as String; result = String.Compare(registryStandardName, new String(timeZone.StandardName), StringComparison.Ordinal) == 0; } return result; } }
// // TryCreateAdjustmentRules - // // Helper function that takes // 1. a string representing a <time_zone_name> registry key name // 2. a RegistryTimeZoneInformation struct containing the default rule // 3. an AdjustmentRule[] out-parameter // // returns // TimeZoneInfoResult.InvalidTimeZoneException, // TimeZoneInfoResult.TimeZoneNotFoundException, // TimeZoneInfoResult.Success // // Optional, Dynamic Time Zone Registry Data // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // // HKLM // Software // Microsoft // Windows NT // CurrentVersion // Time Zones // <time_zone_name> // Dynamic DST // * "FirstEntry" REG_DWORD "1980" // First year in the table. If the current year is less than this value, // this entry will be used for DST boundaries // * "LastEntry" REG_DWORD "2038" // Last year in the table. If the current year is greater than this value, // this entry will be used for DST boundaries" // * "<year1>" REG_BINARY REG_TZI_FORMAT // See REGISTRY_TIME_ZONE_INFORMATION // * "<year2>" REG_BINARY REG_TZI_FORMAT // See REGISTRY_TIME_ZONE_INFORMATION // * "<year3>" REG_BINARY REG_TZI_FORMAT // See REGISTRY_TIME_ZONE_INFORMATION // // This method expects that its caller has already Asserted RegistryPermission.Read // private static bool TryCreateAdjustmentRules(string id, REGISTRY_TIME_ZONE_INFORMATION defaultTimeZoneInformation, out AdjustmentRule[] rules, out Exception e, int defaultBaseUtcOffset) { e = null; try { using (RegistryKey dynamicKey = RegistryKey.GetBaseKey(RegistryKey.HKEY_LOCAL_MACHINE).OpenSubKey( c_timeZonesRegistryHive + "\\" + id + "\\Dynamic DST", false )) { if (dynamicKey == null) { AdjustmentRule rule = CreateAdjustmentRuleFromTimeZoneInformation( defaultTimeZoneInformation, DateTime.MinValue.Date, DateTime.MaxValue.Date, defaultBaseUtcOffset); if (rule == null) { rules = null; } else { rules = new AdjustmentRule[1]; rules[0] = rule; } return true; } // // loop over all of the "<time_zone_name>\Dynamic DST" hive entries // // read FirstEntry {MinValue - (year1, 12, 31)} // read MiddleEntry {(yearN, 1, 1) - (yearN, 12, 31)} // read LastEntry {(yearN, 1, 1) - MaxValue } // read the FirstEntry and LastEntry key values (ex: "1980", "2038") Int32 first = (Int32)dynamicKey.GetValue(c_firstEntryValue, -1, RegistryValueOptions.None); Int32 last = (Int32)dynamicKey.GetValue(c_lastEntryValue, -1, RegistryValueOptions.None); if (first == -1 || last == -1 || first > last) { rules = null; return false; } // read the first year entry REGISTRY_TIME_ZONE_INFORMATION dtzi; Byte[] regValue = dynamicKey.GetValue(first.ToString(CultureInfo.InvariantCulture), null, RegistryValueOptions.None) as Byte[]; if (regValue == null || regValue.Length != c_regByteLength) { rules = null; return false; } dtzi = new REGISTRY_TIME_ZONE_INFORMATION(regValue); if (first == last) { // there is just 1 dynamic rule for this time zone. AdjustmentRule rule = CreateAdjustmentRuleFromTimeZoneInformation(dtzi, DateTime.MinValue.Date, DateTime.MaxValue.Date, defaultBaseUtcOffset); if (rule == null) { rules = null; } else { rules = new AdjustmentRule[1]; rules[0] = rule; } return true; } List<AdjustmentRule> rulesList = new List<AdjustmentRule>(1); // there are more than 1 dynamic rules for this time zone. AdjustmentRule firstRule = CreateAdjustmentRuleFromTimeZoneInformation( dtzi, DateTime.MinValue.Date, // MinValue new DateTime(first, 12, 31), // December 31, <FirstYear> defaultBaseUtcOffset); if (firstRule != null) { rulesList.Add(firstRule); } // read the middle year entries for (Int32 i = first + 1; i < last; i++) { regValue = dynamicKey.GetValue(i.ToString(CultureInfo.InvariantCulture), null, RegistryValueOptions.None) as Byte[]; if (regValue == null || regValue.Length != c_regByteLength) { rules = null; return false; } dtzi = new REGISTRY_TIME_ZONE_INFORMATION(regValue); AdjustmentRule middleRule = CreateAdjustmentRuleFromTimeZoneInformation( dtzi, new DateTime(i, 1, 1), // January 01, <Year> new DateTime(i, 12, 31), // December 31, <Year> defaultBaseUtcOffset); if (middleRule != null) { rulesList.Add(middleRule); } } // read the last year entry regValue = dynamicKey.GetValue(last.ToString(CultureInfo.InvariantCulture), null, RegistryValueOptions.None) as Byte[]; dtzi = new REGISTRY_TIME_ZONE_INFORMATION(regValue); if (regValue == null || regValue.Length != c_regByteLength) { rules = null; return false; } AdjustmentRule lastRule = CreateAdjustmentRuleFromTimeZoneInformation( dtzi, new DateTime(last, 1, 1), // January 01, <LastYear> DateTime.MaxValue.Date, // MaxValue defaultBaseUtcOffset); if (lastRule != null) { rulesList.Add(lastRule); } // convert the ArrayList to an AdjustmentRule array rules = rulesList.ToArray(); if (rules != null && rules.Length == 0) { rules = null; } } // end of: using (RegistryKey dynamicKey... } catch (InvalidCastException ex) { // one of the RegistryKey.GetValue calls could not be cast to an expected value type rules = null; e = ex; return false; } catch (ArgumentOutOfRangeException ex) { rules = null; e = ex; return false; } catch (ArgumentException ex) { rules = null; e = ex; return false; } return true; }
// // TransitionTimeFromTimeZoneInformation - // // Converts a REGISTRY_TIME_ZONE_INFORMATION (REG_TZI_FORMAT struct) to a TransitionTime // // * when the argument 'readStart' is true the corresponding daylightTransitionTimeStart field is read // * when the argument 'readStart' is false the corresponding dayightTransitionTimeEnd field is read // private static bool TransitionTimeFromTimeZoneInformation(REGISTRY_TIME_ZONE_INFORMATION timeZoneInformation, out TransitionTime transitionTime, bool readStartDate) { // // SYSTEMTIME - // // If the time zone does not support daylight saving time or if the caller needs // to disable daylight saving time, the wMonth member in the SYSTEMTIME structure // must be zero. If this date is specified, the DaylightDate value in the // TIME_ZONE_INFORMATION structure must also be specified. Otherwise, the system // assumes the time zone data is invalid and no changes will be applied. // bool supportsDst = (timeZoneInformation.StandardDate.wMonth != 0); if (!supportsDst) { transitionTime = default(TransitionTime); return false; } // // SYSTEMTIME - // // * FixedDateRule - // If the Year member is not zero, the transition date is absolute; it will only occur one time // // * FloatingDateRule - // To select the correct day in the month, set the Year member to zero, the Hour and Minute // members to the transition time, the DayOfWeek member to the appropriate weekday, and the // Day member to indicate the occurence of the day of the week within the month (first through fifth). // // Using this notation, specify the 2:00a.m. on the first Sunday in April as follows: // Hour = 2, // Month = 4, // DayOfWeek = 0, // Day = 1. // // Specify 2:00a.m. on the last Thursday in October as follows: // Hour = 2, // Month = 10, // DayOfWeek = 4, // Day = 5. // if (readStartDate) { // // read the "daylightTransitionStart" // if (timeZoneInformation.DaylightDate.wYear == 0) { transitionTime = TransitionTime.CreateFloatingDateRule( new DateTime(1, /* year */ 1, /* month */ 1, /* day */ timeZoneInformation.DaylightDate.wHour, timeZoneInformation.DaylightDate.wMinute, timeZoneInformation.DaylightDate.wSecond, timeZoneInformation.DaylightDate.wMilliseconds), timeZoneInformation.DaylightDate.wMonth, timeZoneInformation.DaylightDate.wDay, /* Week 1-5 */ (DayOfWeek)timeZoneInformation.DaylightDate.wDayOfWeek); } else { transitionTime = TransitionTime.CreateFixedDateRule( new DateTime(1, /* year */ 1, /* month */ 1, /* day */ timeZoneInformation.DaylightDate.wHour, timeZoneInformation.DaylightDate.wMinute, timeZoneInformation.DaylightDate.wSecond, timeZoneInformation.DaylightDate.wMilliseconds), timeZoneInformation.DaylightDate.wMonth, timeZoneInformation.DaylightDate.wDay); } } else { // // read the "daylightTransitionEnd" // if (timeZoneInformation.StandardDate.wYear == 0) { transitionTime = TransitionTime.CreateFloatingDateRule( new DateTime(1, /* year */ 1, /* month */ 1, /* day */ timeZoneInformation.StandardDate.wHour, timeZoneInformation.StandardDate.wMinute, timeZoneInformation.StandardDate.wSecond, timeZoneInformation.StandardDate.wMilliseconds), timeZoneInformation.StandardDate.wMonth, timeZoneInformation.StandardDate.wDay, /* Week 1-5 */ (DayOfWeek)timeZoneInformation.StandardDate.wDayOfWeek); } else { transitionTime = TransitionTime.CreateFixedDateRule( new DateTime(1, /* year */ 1, /* month */ 1, /* day */ timeZoneInformation.StandardDate.wHour, timeZoneInformation.StandardDate.wMinute, timeZoneInformation.StandardDate.wSecond, timeZoneInformation.StandardDate.wMilliseconds), timeZoneInformation.StandardDate.wMonth, timeZoneInformation.StandardDate.wDay); } } return true; }
// // CreateAdjustmentRuleFromTimeZoneInformation- // // Converts a REGISTRY_TIME_ZONE_INFORMATION (REG_TZI_FORMAT struct) to an AdjustmentRule // private static AdjustmentRule CreateAdjustmentRuleFromTimeZoneInformation(REGISTRY_TIME_ZONE_INFORMATION timeZoneInformation, DateTime startDate, DateTime endDate, int defaultBaseUtcOffset) { AdjustmentRule rule; bool supportsDst = (timeZoneInformation.StandardDate.wMonth != 0); if (!supportsDst) { if (timeZoneInformation.Bias == defaultBaseUtcOffset) { // this rule will not contain any information to be used to adjust dates. just ignore it return null; } return rule = AdjustmentRule.CreateAdjustmentRule( startDate, endDate, TimeSpan.Zero, // no daylight saving transition TransitionTime.CreateFixedDateRule(DateTime.MinValue, 1, 1), TransitionTime.CreateFixedDateRule(DateTime.MinValue.AddMilliseconds(1), 1, 1), new TimeSpan(0, defaultBaseUtcOffset - timeZoneInformation.Bias, 0)); // Bias delta is all what we need from this rule } // // Create an AdjustmentRule with TransitionTime objects // TransitionTime daylightTransitionStart; if (!TransitionTimeFromTimeZoneInformation(timeZoneInformation, out daylightTransitionStart, true /* start date */)) { return null; } TransitionTime daylightTransitionEnd; if (!TransitionTimeFromTimeZoneInformation(timeZoneInformation, out daylightTransitionEnd, false /* end date */)) { return null; } if (daylightTransitionStart.Equals(daylightTransitionEnd)) { // this happens when the time zone does support DST but the OS has DST disabled return null; } rule = AdjustmentRule.CreateAdjustmentRule( startDate, endDate, new TimeSpan(0, -timeZoneInformation.DaylightBias, 0), (TransitionTime)daylightTransitionStart, (TransitionTime)daylightTransitionEnd, new TimeSpan(0, defaultBaseUtcOffset - timeZoneInformation.Bias, 0)); return rule; }
// -------- SECTION: constructors -----------------* // // TimeZoneInfo - // // private ctor // private unsafe TimeZoneInfo(TIME_ZONE_INFORMATION zone, Boolean dstDisabled) { if (String.IsNullOrEmpty(new String(zone.StandardName))) { _id = c_localId; // the ID must contain at least 1 character - initialize _id to "Local" } else { _id = new String(zone.StandardName); } _baseUtcOffset = new TimeSpan(0, -(zone.Bias), 0); if (!dstDisabled) { // only create the adjustment rule if DST is enabled REGISTRY_TIME_ZONE_INFORMATION regZone = new REGISTRY_TIME_ZONE_INFORMATION(zone); AdjustmentRule rule = CreateAdjustmentRuleFromTimeZoneInformation(regZone, DateTime.MinValue.Date, DateTime.MaxValue.Date, zone.Bias); if (rule != null) { _adjustmentRules = new AdjustmentRule[1]; _adjustmentRules[0] = rule; } } ValidateTimeZoneInfo(_id, _baseUtcOffset, _adjustmentRules, out _supportsDaylightSavingTime); _displayName = new String(zone.StandardName); _standardDisplayName = new String(zone.StandardName); _daylightDisplayName = new String(zone.DaylightName); }
// // TryGetTimeZoneByRegistryKey - // // Helper function that takes a string representing a <time_zone_name> registry key name // and returns a TimeZoneInfo instance. // // returns // TimeZoneInfoResult.InvalidTimeZoneException, // TimeZoneInfoResult.TimeZoneNotFoundException, // TimeZoneInfoResult.SecurityException, // TimeZoneInfoResult.Success // // // Standard Time Zone Registry Data // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // HKLM // Software // Microsoft // Windows NT // CurrentVersion // Time Zones // <time_zone_name> // * STD, REG_SZ "Standard Time Name" // (For OS installed zones, this will always be English) // * MUI_STD, REG_SZ "@tzres.dll,-1234" // Indirect string to localized resource for Standard Time, // add "%windir%\system32\" after "@" // * DLT, REG_SZ "Daylight Time Name" // (For OS installed zones, this will always be English) // * MUI_DLT, REG_SZ "@tzres.dll,-1234" // Indirect string to localized resource for Daylight Time, // add "%windir%\system32\" after "@" // * Display, REG_SZ "Display Name like (GMT-8:00) Pacific Time..." // * MUI_Display, REG_SZ "@tzres.dll,-1234" // Indirect string to localized resource for the Display, // add "%windir%\system32\" after "@" // * TZI, REG_BINARY REG_TZI_FORMAT // See REGISTRY_TIME_ZONE_INFORMATION // private static TimeZoneInfoResult TryGetTimeZoneByRegistryKey(string id, out TimeZoneInfo value, out Exception e) { e = null; using (RegistryKey key = RegistryKey.GetBaseKey(RegistryKey.HKEY_LOCAL_MACHINE).OpenSubKey( c_timeZonesRegistryHive + "\\" + id, false )) { if (key == null) { value = null; return TimeZoneInfoResult.TimeZoneNotFoundException; } REGISTRY_TIME_ZONE_INFORMATION defaultTimeZoneInformation; Byte[] regValue = key.GetValue(c_timeZoneInfoValue, null, RegistryValueOptions.None) as Byte[]; if (regValue == null || regValue.Length != c_regByteLength) { // the registry value could not be cast to a byte array value = null; return TimeZoneInfoResult.InvalidTimeZoneException; } defaultTimeZoneInformation = new REGISTRY_TIME_ZONE_INFORMATION(regValue); AdjustmentRule[] adjustmentRules; if (!TryCreateAdjustmentRules(id, defaultTimeZoneInformation, out adjustmentRules, out e, defaultTimeZoneInformation.Bias)) { value = null; return TimeZoneInfoResult.InvalidTimeZoneException; } string displayName; string standardName; string daylightName; if (!TryGetLocalizedNamesByRegistryKey(key, out displayName, out standardName, out daylightName)) { value = null; return TimeZoneInfoResult.InvalidTimeZoneException; } try { value = new TimeZoneInfo( id, new TimeSpan(0, -(defaultTimeZoneInformation.Bias), 0), displayName, standardName, daylightName, adjustmentRules, false); return TimeZoneInfoResult.Success; } catch (ArgumentException ex) { // TimeZoneInfo constructor can throw ArgumentException and InvalidTimeZoneException value = null; e = ex; return TimeZoneInfoResult.InvalidTimeZoneException; } catch (InvalidTimeZoneException ex) { // TimeZoneInfo constructor can throw ArgumentException and InvalidTimeZoneException value = null; e = ex; return TimeZoneInfoResult.InvalidTimeZoneException; } } }