internal static AdjustmentRule CreateAdjustmentRuleFromTimeZoneInformation(ref TIME_ZONE_INFORMATION timeZoneInformation, DateTime startDate, DateTime endDate, int defaultBaseUtcOffset) { 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(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 noDaylightTransitions: false)); } // // 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); } return(AdjustmentRule.CreateAdjustmentRule( startDate, endDate, new TimeSpan(0, -timeZoneInformation.DaylightBias, 0), (TransitionTime)daylightTransitionStart, (TransitionTime)daylightTransitionEnd, new TimeSpan(0, defaultBaseUtcOffset - timeZoneInformation.Bias, 0), noDaylightTransitions: false)); }
// // TransitionTimeFromTimeZoneInformation - // // Converts a TimeZoneInformation (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(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); }
private static TimeZoneInfoResult TryGetFullTimeZoneInformation(TimeZoneInformation timeZoneInformation, out TimeZoneInfo value, out Exception e, int defaultBaseUtcOffset) { uint firstYear, lastYear; AdjustmentRule rule; AdjustmentRule[] zoneRules = null; value = null; e = null; // // First get the adjustment rules // if (Interop.mincore.GetDynamicTimeZoneInformationEffectiveYears(ref timeZoneInformation.Dtzi, out firstYear, out lastYear) != 0) { rule = CreateAdjustmentRuleFromTimeZoneInformation(timeZoneInformation, DateTime.MinValue.Date, DateTime.MaxValue.Date, defaultBaseUtcOffset); if (rule != null) { zoneRules = new AdjustmentRule[1] { rule }; } } else { if (firstYear == lastYear) { // there is just 1 dynamic rule for this time zone. rule = CreateAdjustmentRuleFromTimeZoneInformation(timeZoneInformation, DateTime.MinValue.Date, DateTime.MaxValue.Date, defaultBaseUtcOffset); if (rule != null) { zoneRules = new AdjustmentRule[1] { rule }; } } else { TIME_ZONE_INFORMATION tzdi = new TIME_ZONE_INFORMATION(); LowLevelList <AdjustmentRule> rules = new LowLevelList <AdjustmentRule>(); // // First rule // if (!Interop.mincore.GetTimeZoneInformationForYear((ushort)firstYear, ref timeZoneInformation.Dtzi, out tzdi)) { return(TimeZoneInfoResult.InvalidTimeZoneException); } rule = CreateAdjustmentRuleFromTimeZoneInformation(ref tzdi, DateTime.MinValue.Date, new DateTime((int)firstYear, 12, 31), defaultBaseUtcOffset); if (rule != null) { rules.Add(rule); } for (uint i = firstYear + 1; i < lastYear; i++) { if (!Interop.mincore.GetTimeZoneInformationForYear((ushort)i, ref timeZoneInformation.Dtzi, out tzdi)) { return(TimeZoneInfoResult.InvalidTimeZoneException); } rule = CreateAdjustmentRuleFromTimeZoneInformation(ref tzdi, new DateTime((int)i, 1, 1), new DateTime((int)i, 12, 31), defaultBaseUtcOffset); if (rule != null) { rules.Add(rule); } } // // Last rule // if (!Interop.mincore.GetTimeZoneInformationForYear((ushort)lastYear, ref timeZoneInformation.Dtzi, out tzdi)) { return(TimeZoneInfoResult.InvalidTimeZoneException); } rule = CreateAdjustmentRuleFromTimeZoneInformation(ref tzdi, new DateTime((int)lastYear, 1, 1), DateTime.MaxValue.Date, defaultBaseUtcOffset); if (rule != null) { rules.Add(rule); } if (rules.Count > 0) { zoneRules = rules.ToArray(); } } } // // Create TimeZoneInfo object // try { // Note that all names we have are localized names as Windows always return the localized names value = new TimeZoneInfo( timeZoneInformation.TimeZoneKeyName, new TimeSpan(0, -(timeZoneInformation.Dtzi.Bias), 0), timeZoneInformation.StandardName, // we use the display name as the standared names timeZoneInformation.StandardName, timeZoneInformation.DaylightName, zoneRules, false); return(System.TimeZoneInfo.TimeZoneInfoResult.Success); } catch (ArgumentException ex) { // TimeZoneInfo constructor can throw ArgumentException and InvalidTimeZoneException value = null; e = ex; return(System.TimeZoneInfo.TimeZoneInfoResult.InvalidTimeZoneException); } catch (InvalidTimeZoneException ex) { // TimeZoneInfo constructor can throw ArgumentException and InvalidTimeZoneException value = null; e = ex; return(System.TimeZoneInfo.TimeZoneInfoResult.InvalidTimeZoneException); } }
// // 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; } }
// // GetLocalTimeZoneFromWin32Data - // // Helper function used by 'GetLocalTimeZone()' - this function wraps a bunch of // try/catch logic for handling the TimeZoneInfo private constructor that takes // a TIME_ZONE_INFORMATION structure. // private static TimeZoneInfo GetLocalTimeZoneFromWin32Data(TIME_ZONE_INFORMATION timeZoneInformation, Boolean dstDisabled) { // first try to create the TimeZoneInfo with the original 'dstDisabled' flag try { return new TimeZoneInfo(timeZoneInformation, dstDisabled); } catch (ArgumentException) { } catch (InvalidTimeZoneException) { } // if 'dstDisabled' was false then try passing in 'true' as a last ditch effort if (!dstDisabled) { try { return new TimeZoneInfo(timeZoneInformation, true); } catch (ArgumentException) { } catch (InvalidTimeZoneException) { } } // the data returned from Windows is completely bogus; return a dummy entry return CreateCustomTimeZone(c_localId, TimeSpan.Zero, c_localId, c_localId); }
// // GetLocalTimeZone - // // Helper function for retrieving the local system time zone. // // returns a new TimeZoneInfo instance // // may throw COMException, TimeZoneNotFoundException, InvalidTimeZoneException // // assumes cachedData lock is taken // static unsafe private TimeZoneInfo GetLocalTimeZone(CachedData cachedData) { String id = null; // // Try using the "kernel32!GetDynamicTimeZoneInformation" API to get the "id" // Interop.mincore.TIME_DYNAMIC_ZONE_INFORMATION dynamicTimeZoneInformation = new Interop.mincore.TIME_DYNAMIC_ZONE_INFORMATION(); // call kernel32!GetDynamicTimeZoneInformation... long result = Interop.mincore.GetDynamicTimeZoneInformation(out dynamicTimeZoneInformation); if (result == Interop.mincore.TIME_ZONE_ID_INVALID) { // return a dummy entry return CreateCustomTimeZone(c_localId, TimeSpan.Zero, c_localId, c_localId); } TIME_ZONE_INFORMATION timeZoneInformation = new TIME_ZONE_INFORMATION(dynamicTimeZoneInformation); Boolean dstDisabled = dynamicTimeZoneInformation.DynamicDaylightTimeDisabled != 0; // check to see if we can use the key name returned from the API call if (!String.IsNullOrEmpty(new String(dynamicTimeZoneInformation.TimeZoneKeyName))) { TimeZoneInfo zone; Exception ex; if (TryGetTimeZone(new String(dynamicTimeZoneInformation.TimeZoneKeyName), dstDisabled, out zone, out ex, cachedData) == TimeZoneInfoResult.Success) { // successfully loaded the time zone from the registry return zone; } } // the key name was not returned or it pointed to a bogus entry - search for the entry ourselves id = FindIdFromTimeZoneInformation(timeZoneInformation, out dstDisabled); if (id != null) { TimeZoneInfo zone; Exception ex; if (TryGetTimeZone(id, dstDisabled, out zone, out ex, cachedData) == TimeZoneInfoResult.Success) { // successfully loaded the time zone from the registry return zone; } } // We could not find the data in the registry. Fall back to using // the data from the Win32 API return GetLocalTimeZoneFromWin32Data(timeZoneInformation, dstDisabled); }
// // FindIdFromTimeZoneInformation - // // Helper function that searches the registry for a time zone entry // that matches the TimeZoneInformation struct // private static String FindIdFromTimeZoneInformation(TIME_ZONE_INFORMATION timeZone, out Boolean dstDisabled) { dstDisabled = false; using (RegistryKey key = RegistryKey.GetBaseKey(RegistryKey.HKEY_LOCAL_MACHINE).OpenSubKey( c_timeZonesRegistryHive, false )) { if (key == null) { return null; } foreach (string keyName in key.GetSubKeyNames()) { if (TryCompareTimeZoneInformationToRegistry(timeZone, keyName, out dstDisabled)) { return keyName; } } } return null; }
// ----- SECTION: internal static utility methods ----------------* // // CheckDaylightSavingTimeNotSupported - // // Helper function to check if the current TimeZoneInformation struct does not support DST. This // check returns true when the DaylightDate == StandardDate // // This check is only meant to be used for "Local". // private static Boolean CheckDaylightSavingTimeNotSupported(TIME_ZONE_INFORMATION timeZone) { return (timeZone.DaylightDate.wYear == timeZone.StandardDate.wYear && timeZone.DaylightDate.wMonth == timeZone.StandardDate.wMonth && timeZone.DaylightDate.wDayOfWeek == timeZone.StandardDate.wDayOfWeek && timeZone.DaylightDate.wDay == timeZone.StandardDate.wDay && timeZone.DaylightDate.wHour == timeZone.StandardDate.wHour && timeZone.DaylightDate.wMinute == timeZone.StandardDate.wMinute && timeZone.DaylightDate.wSecond == timeZone.StandardDate.wSecond && timeZone.DaylightDate.wMilliseconds == timeZone.StandardDate.wMilliseconds); }
// -------- 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); }