X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2Fcorlib%2FSystem%2FTimeZoneInfo.cs;h=0c17412242c835af7107d559a158ce9be1e25444;hb=a27db5252facf504e7995e6abe41ab5f1d3ec506;hp=a6b4e5233a22c6e3cfefde7ccb461460056e5b6e;hpb=93ce056a764e8048f33548a3744ba2bd84072043;p=mono.git diff --git a/mcs/class/corlib/System/TimeZoneInfo.cs b/mcs/class/corlib/System/TimeZoneInfo.cs index a6b4e5233a2..0c17412242c 100644 --- a/mcs/class/corlib/System/TimeZoneInfo.cs +++ b/mcs/class/corlib/System/TimeZoneInfo.cs @@ -45,14 +45,7 @@ using Microsoft.Win32; namespace System { -#if MOBILE - [TypeForwardedFrom (Consts.AssemblySystem_Core)] -#else - [TypeForwardedFrom (Consts.AssemblySystemCore_3_5)] -#endif - [SerializableAttribute] - public - sealed partial class TimeZoneInfo : IEquatable, ISerializable, IDeserializationCallback + partial class TimeZoneInfo { TimeSpan baseUtcOffset; public TimeSpan BaseUtcOffset { @@ -112,6 +105,8 @@ namespace System #if !NET_2_1 if (IsWindows && LocalZoneKey != null) { string name = (string)LocalZoneKey.GetValue ("TimeZoneKeyName"); + if (name == null) + name = (string)LocalZoneKey.GetValue ("StandardName"); // windows xp name = TrimSpecial (name); if (name != null) return TimeZoneInfo.FindSystemTimeZoneById (name); @@ -192,6 +187,8 @@ namespace System /// private static string TrimSpecial (string str) { + if (str == null) + return str; var Istart = 0; while (Istart < str.Length && !char.IsLetterOrDigit(str[Istart])) Istart++; var Iend = str.Length - 1; @@ -229,6 +226,18 @@ namespace System } #endif + private static bool TryAddTicks (DateTime date, long ticks, out DateTime result, DateTimeKind kind = DateTimeKind.Unspecified) + { + var resultTicks = date.Ticks + ticks; + if (resultTicks < DateTime.MinValue.Ticks || resultTicks > DateTime.MaxValue.Ticks) { + result = default (DateTime); + return false; + } + + result = new DateTime (resultTicks, kind); + return true; + } + public static void ClearCachedData () { local = null; @@ -276,17 +285,13 @@ namespace System { if (destinationTimeZone == null) throw new ArgumentNullException("destinationTimeZone"); - + var utcDateTime = dateTimeOffset.UtcDateTime; - AdjustmentRule rule = destinationTimeZone.GetApplicableRule (utcDateTime); - - if (rule != null && destinationTimeZone.IsDaylightSavingTime(utcDateTime)) { - var offset = destinationTimeZone.BaseUtcOffset + rule.DaylightDelta; - return new DateTimeOffset(DateTime.SpecifyKind(utcDateTime, DateTimeKind.Unspecified) + offset, offset); - } - else { - return new DateTimeOffset(DateTime.SpecifyKind(utcDateTime, DateTimeKind.Unspecified) + destinationTimeZone.BaseUtcOffset, destinationTimeZone.BaseUtcOffset); - } + + bool isDst; + var utcOffset = destinationTimeZone.GetUtcOffset(utcDateTime, out isDst); + + return new DateTimeOffset(DateTime.SpecifyKind(utcDateTime, DateTimeKind.Unspecified) + utcOffset, utcOffset); } public static DateTime ConvertTimeBySystemTimeZoneId (DateTime dateTime, string destinationTimeZoneId) @@ -311,19 +316,16 @@ namespace System if (this == TimeZoneInfo.Utc) return DateTime.SpecifyKind (dateTime, DateTimeKind.Utc); - - //FIXME: do not rely on DateTime implementation ! - if (this == TimeZoneInfo.Local) - { - return dateTime.ToLocalTime (); - } + var utcOffset = GetUtcOffset (dateTime); - AdjustmentRule rule = GetApplicableRule (dateTime); - if (rule != null && IsDaylightSavingTime (DateTime.SpecifyKind (dateTime, DateTimeKind.Utc))) - return DateTime.SpecifyKind (dateTime + BaseUtcOffset + rule.DaylightDelta , DateTimeKind.Unspecified); - else - return DateTime.SpecifyKind (dateTime + BaseUtcOffset, DateTimeKind.Unspecified); + var kind = (this == TimeZoneInfo.Local)? DateTimeKind.Local : DateTimeKind.Unspecified; + + DateTime result; + if (!TryAddTicks (dateTime, utcOffset.Ticks, out result, kind)) + return DateTime.SpecifyKind (DateTime.MaxValue, kind); + + return result; } public static DateTime ConvertTimeFromUtc (DateTime dateTime, TimeZoneInfo destinationTimeZone) @@ -371,22 +373,14 @@ namespace System if (dateTime.Kind == DateTimeKind.Utc) return dateTime; - if (sourceTimeZone.IsAmbiguousTime (dateTime) || !sourceTimeZone.IsDaylightSavingTime (dateTime)) { - var ticks = dateTime.Ticks - sourceTimeZone.BaseUtcOffset.Ticks; - if (ticks < DateTime.MinValue.Ticks) - ticks = DateTime.MinValue.Ticks; - else if (ticks > DateTime.MaxValue.Ticks) - ticks = DateTime.MaxValue.Ticks; + bool isDst; + var utcOffset = sourceTimeZone.GetUtcOffset (dateTime, out isDst); - return new DateTime (ticks, DateTimeKind.Utc); - } - - AdjustmentRule rule = sourceTimeZone.GetApplicableRule (dateTime); - if (rule != null) - return DateTime.SpecifyKind (dateTime - sourceTimeZone.BaseUtcOffset - rule.DaylightDelta, DateTimeKind.Utc); - else - return DateTime.SpecifyKind (dateTime - sourceTimeZone.BaseUtcOffset, DateTimeKind.Utc); - + DateTime utcDateTime; + if (!TryAddTicks (dateTime, -utcOffset.Ticks, out utcDateTime, DateTimeKind.Utc)) + return DateTime.SpecifyKind (DateTime.MinValue, DateTimeKind.Utc); + + return utcDateTime; } static internal TimeSpan GetDateTimeNowUtcOffsetFromUtc(DateTime time, out Boolean isAmbiguousLocalDst) @@ -431,6 +425,8 @@ namespace System #if !NET_2_1 if (TimeZoneKey != null) { + if (id == "Coordinated Universal Time") + id = "UTC"; //windows xp exception for "StandardName" property RegistryKey key = TimeZoneKey.OpenSubKey (id, false); if (key == null) throw new TimeZoneNotFoundException (); @@ -722,23 +718,22 @@ namespace System tz = TimeZoneInfo.Local; bool isTzDst; - var tzOffset = GetUtcOffset (dateTime, tz, out isTzDst); + var tzOffset = GetUtcOffsetHelper (dateTime, tz, out isTzDst); if (tz == this) { isDST = isTzDst; return tzOffset; } - var utcTicks = dateTime.Ticks - tzOffset.Ticks; - if (utcTicks < 0 || utcTicks > DateTime.MaxValue.Ticks) + DateTime utcDateTime; + if (!TryAddTicks (dateTime, -tzOffset.Ticks, out utcDateTime, DateTimeKind.Utc)) return BaseUtcOffset; - var utcDateTime = new DateTime (utcTicks, DateTimeKind.Utc); - - return GetUtcOffset (utcDateTime, this, out isDST); + return GetUtcOffsetHelper (utcDateTime, this, out isDST); } - private static TimeSpan GetUtcOffset (DateTime dateTime, TimeZoneInfo tz, out bool isDST) + // This is an helper method used by the method above, do not use this on its own. + private static TimeSpan GetUtcOffsetHelper (DateTime dateTime, TimeZoneInfo tz, out bool isDST) { if (dateTime.Kind == DateTimeKind.Local && tz != TimeZoneInfo.Local) throw new Exception (); @@ -762,20 +757,16 @@ namespace System return tz.BaseUtcOffset; } - var stdTicks = dateTime.Ticks - tz.BaseUtcOffset.Ticks; - if (stdTicks < 0 || stdTicks > DateTime.MaxValue.Ticks) + DateTime stdUtcDateTime; + if (!TryAddTicks (dateTime, -tz.BaseUtcOffset.Ticks, out stdUtcDateTime, DateTimeKind.Utc)) return tz.BaseUtcOffset; - var stdUtcDateTime = new DateTime (stdTicks, DateTimeKind.Utc); var tzRule = tz.GetApplicableRule (stdUtcDateTime); DateTime dstUtcDateTime = DateTime.MinValue; if (tzRule != null) { - var dstTicks = stdUtcDateTime.Ticks - tzRule.DaylightDelta.Ticks; - if (dstTicks < 0 || dstTicks > DateTime.MaxValue.Ticks) + if (!TryAddTicks (stdUtcDateTime, -tzRule.DaylightDelta.Ticks, out dstUtcDateTime, DateTimeKind.Utc)) return tz.BaseUtcOffset; - - dstUtcDateTime = new DateTime (dstTicks, DateTimeKind.Utc); } if (tzRule != null && tz.IsInDST (tzRule, stdUtcDateTime) && tz.IsInDST (tzRule, dstUtcDateTime)) { @@ -889,6 +880,85 @@ namespace System throw new NotImplementedException (); } + internal DaylightTime GetDaylightChanges (int year) + { + DateTime start = DateTime.MinValue, end = DateTime.MinValue; + TimeSpan delta = new TimeSpan (); + + if (transitions != null) { + end = DateTime.MaxValue; + for (var i = transitions.Count - 1; i >= 0; i--) { + var pair = transitions [i]; + DateTime ttime = pair.Key; + TimeType ttype = pair.Value; + + if (ttype.IsDst) { + // DaylightTime.Delta is relative to the current BaseUtcOffset. + var d = new TimeSpan (0, 0, ttype.Offset) - BaseUtcOffset; + // Handle DST gradients + if (start != DateTime.MinValue && delta != d) + end = start; + + start = ttime; + delta = d; + + if (ttime.Year <= year) + break; + } else { + if (ttime.Year < year) + break; + + end = ttime; + start = DateTime.MinValue; + } + } + + // DaylightTime.Start is relative to the Standard time. + if (start != DateTime.MinValue) + start += BaseUtcOffset; + + // DaylightTime.End is relative to the DST time. + if (end != DateTime.MaxValue) + end += BaseUtcOffset + delta; + } else { + AdjustmentRule rule = null; + foreach (var r in GetAdjustmentRules ()) { + if (r.DateEnd.Year < year) + continue; + if (r.DateStart.Year > year) + break; + rule = r; + } + if (rule != null) { + start = TransitionPoint (rule.DaylightTransitionStart, year); + end = TransitionPoint (rule.DaylightTransitionEnd, year); + delta = rule.DaylightDelta; + } + } + + if (start == DateTime.MinValue || end == DateTime.MinValue) + return new DaylightTime (new DateTime (), new DateTime (), new TimeSpan ()); + + return new DaylightTime (start, end, delta); + } + + public bool IsInvalidTime (DateTime dateTime) + { + if (dateTime.Kind == DateTimeKind.Utc) + return false; + if (dateTime.Kind == DateTimeKind.Local && this != Local) + return false; + + AdjustmentRule rule = GetApplicableRule (dateTime); + if (rule != null) { + DateTime tpoint = TransitionPoint (rule.DaylightTransitionStart, dateTime.Year); + if (dateTime >= tpoint && dateTime < tpoint + rule.DaylightDelta) + return true; + } + + return false; + } + void IDeserializationCallback.OnDeserialization (object sender) { try { @@ -1019,10 +1089,13 @@ namespace System //Applicable rules are in standard time DateTime date = dateTime; - if (dateTime.Kind == DateTimeKind.Local && this != TimeZoneInfo.Local) - date = date.ToUniversalTime () + BaseUtcOffset; - else if (dateTime.Kind == DateTimeKind.Utc && this != TimeZoneInfo.Utc) - date = date + BaseUtcOffset; + if (dateTime.Kind == DateTimeKind.Local && this != TimeZoneInfo.Local) { + if (!TryAddTicks (date.ToUniversalTime (), BaseUtcOffset.Ticks, out date)) + return null; + } else if (dateTime.Kind == DateTimeKind.Utc && this != TimeZoneInfo.Utc) { + if (!TryAddTicks (date, BaseUtcOffset.Ticks, out date)) + return null; + } // get the date component of the datetime date = date.Date; @@ -1050,13 +1123,14 @@ namespace System //Transitions are in UTC DateTime date = dateTime; - if (dateTime.Kind == DateTimeKind.Local && this != TimeZoneInfo.Local) - date = date.ToUniversalTime () + BaseUtcOffset; + if (dateTime.Kind == DateTimeKind.Local && this != TimeZoneInfo.Local) { + if (!TryAddTicks (date.ToUniversalTime (), BaseUtcOffset.Ticks, out date, DateTimeKind.Utc)) + return false; + } if (dateTime.Kind != DateTimeKind.Utc) { - if (date.Ticks < BaseUtcOffset.Ticks) + if (!TryAddTicks (date, -BaseUtcOffset.Ticks, out date, DateTimeKind.Utc)) return false; - date = date - BaseUtcOffset; } for (var i = transitions.Count - 1; i >= 0; i--) { @@ -1192,6 +1266,14 @@ namespace System if (dst_end.Date == new DateTime (dst_end.Year, 1, 1) && dst_end.Year > dst_start.Year) dst_end -= new TimeSpan (24, 0, 0); + /* + * AdjustmentRule specifies a DST period that starts and ends within a year. + * When we have a DST period longer than a year, the generated AdjustmentRule may not be usable. + * Thus we fallback to the transitions. + */ + if (dst_start.AddYears (1) < dst_end) + storeTransition = true; + DateTime dateStart, dateEnd; if (dst_start.Month < 7) dateStart = new DateTime (dst_start.Year, 1, 1); @@ -1213,8 +1295,12 @@ namespace System } else { if (daylightDisplayName != ttype.Name) daylightDisplayName = ttype.Name; - if (dstDelta.TotalSeconds != ttype.Offset - baseUtcOffset.TotalSeconds) - dstDelta = new TimeSpan(0, 0, ttype.Offset) - baseUtcOffset; + if (dstDelta.TotalSeconds != ttype.Offset - baseUtcOffset.TotalSeconds) { + // Round to nearest minute, since it's not possible to create an adjustment rule + // with sub-minute precision ("The TimeSpan parameter cannot be specified more precisely than whole minutes.") + // This happens with Europe/Dublin, which had an offset of 34 minutes and 39 seconds in 1916. + dstDelta = new TimeSpan (0, 0, ttype.Offset - ttype.Offset % 60) - baseUtcOffset; + } dst_start = ttime; dst_observed = true; @@ -1291,333 +1377,32 @@ namespace System } #region reference sources - // Shortcut for TimeZoneInfo.Local.GetUtcOffset - internal static TimeSpan GetLocalUtcOffset(DateTime dateTime, TimeZoneInfoOptions flags) - { - bool dst; - return Local.GetUtcOffset (dateTime, out dst); - } - - internal TimeSpan GetUtcOffset(DateTime dateTime, TimeZoneInfoOptions flags) - { - bool dst; - return GetUtcOffset (dateTime, out dst); - } - - // used by GetUtcOffsetFromUtc (DateTime.Now, DateTime.ToLocalTime) for max/min whole-day range checks - private static DateTime s_maxDateOnly = new DateTime(9999, 12, 31); - private static DateTime s_minDateOnly = new DateTime(1, 1, 2); - - static internal TimeSpan GetUtcOffsetFromUtc (DateTime time, TimeZoneInfo zone, out Boolean isDaylightSavings, out Boolean isAmbiguousLocalDst) - { - isDaylightSavings = false; - isAmbiguousLocalDst = false; - TimeSpan baseOffset = zone.BaseUtcOffset; - Int32 year; - AdjustmentRule rule; - - if (time > s_maxDateOnly) { - rule = zone.GetAdjustmentRuleForTime(DateTime.MaxValue); - year = 9999; - } - else if (time < s_minDateOnly) { - rule = zone.GetAdjustmentRuleForTime(DateTime.MinValue); - year = 1; - } - else { - DateTime targetTime = time + baseOffset; - year = time.Year; - rule = zone.GetAdjustmentRuleForTime(targetTime); - } - - if (rule != null) { - isDaylightSavings = GetIsDaylightSavingsFromUtc(time, year, zone.baseUtcOffset, rule, out isAmbiguousLocalDst); - baseOffset += (isDaylightSavings ? rule.DaylightDelta : TimeSpan.Zero /* */); - } - - return baseOffset; - } - - // assumes dateTime is in the current time zone's time - private AdjustmentRule GetAdjustmentRuleForTime(DateTime dateTime) { - if (adjustmentRules == null || adjustmentRules.Length == 0) { - return null; - } - -#if WINXP_AND_WIN2K3_SUPPORT - // On pre-Vista versions of Windows if you run "cmd /c date" or "cmd /c time" to update the system time - // the operating system doesn't pick up the correct time zone adjustment rule (it stays on the currently loaded rule). - // We need to use the OS API data in this scenario instead of the loaded adjustment rules from the registry for - // consistency. Otherwise DateTime.Now might not match the time displayed in the system tray. - if (!Environment.IsWindowsVistaOrAbove && s_cachedData.GetCorrespondingKind(this) == DateTimeKind.Local) { - return s_cachedData.GetOneYearLocalFromLocal(dateTime.Year).rule; - } -#endif - // Only check the whole-date portion of the dateTime - - // This is because the AdjustmentRule DateStart & DateEnd are stored as - // Date-only values {4/2/2006 - 10/28/2006} but actually represent the - // time span {4/2/2006@00:00:00.00000 - 10/28/2006@23:59:59.99999} - DateTime date = dateTime.Date; - - for (int i = 0; i < adjustmentRules.Length; i++) { - if (adjustmentRules[i].DateStart <= date && adjustmentRules[i].DateEnd >= date) { - return adjustmentRules[i]; - } - } - - return null; - } - - // - // GetIsDaylightSavingsFromUtc - - // - // Helper function that checks if a given dateTime is in Daylight Saving Time (DST) - // This function assumes the dateTime is in UTC and AdjustmentRule is in a different time zone - // - static private Boolean GetIsDaylightSavingsFromUtc(DateTime time, Int32 Year, TimeSpan utc, AdjustmentRule rule, out Boolean isAmbiguousLocalDst) { - isAmbiguousLocalDst = false; - - if (rule == null) { - return false; - } - - // Get the daylight changes for the year of the specified time. - TimeSpan offset = utc; /* */ - DaylightTime daylightTime = GetDaylightTime(Year, rule); - - // The start and end times represent the range of universal times that are in DST for that year. - // Within that there is an ambiguous hour, usually right at the end, but at the beginning in - // the unusual case of a negative daylight savings delta. - DateTime startTime = daylightTime.Start - offset; - DateTime endTime = daylightTime.End - offset - rule.DaylightDelta; /* */ - DateTime ambiguousStart; - DateTime ambiguousEnd; - if (daylightTime.Delta.Ticks > 0) { - ambiguousStart = endTime - daylightTime.Delta; - ambiguousEnd = endTime; - } else { - ambiguousStart = startTime; - ambiguousEnd = startTime - daylightTime.Delta; - } - - Boolean isDst = CheckIsDst(startTime, time, endTime); - - // See if the resulting local time becomes ambiguous. This must be captured here or the - // DateTime will not be able to round-trip back to UTC accurately. - if (isDst) { - isAmbiguousLocalDst = (time >= ambiguousStart && time < ambiguousEnd); - - if (!isAmbiguousLocalDst && ambiguousStart.Year != ambiguousEnd.Year) { - // there exists an extreme corner case where the start or end period is on a year boundary and - // because of this the comparison above might have been performed for a year-early or a year-later - // than it should have been. - DateTime ambiguousStartModified; - DateTime ambiguousEndModified; - try { - ambiguousStartModified = ambiguousStart.AddYears(1); - ambiguousEndModified = ambiguousEnd.AddYears(1); - isAmbiguousLocalDst = (time >= ambiguousStart && time < ambiguousEnd); - } - catch (ArgumentOutOfRangeException) {} - - if (!isAmbiguousLocalDst) { - try { - ambiguousStartModified = ambiguousStart.AddYears(-1); - ambiguousEndModified = ambiguousEnd.AddYears(-1); - isAmbiguousLocalDst = (time >= ambiguousStart && time < ambiguousEnd); - } - catch (ArgumentOutOfRangeException) {} - } - - } - } - - return isDst; - } - - - static private Boolean CheckIsDst(DateTime startTime, DateTime time, DateTime endTime) { - Boolean isDst; - - int startTimeYear = startTime.Year; - int endTimeYear = endTime.Year; - - if (startTimeYear != endTimeYear) { - endTime = endTime.AddYears(startTimeYear - endTimeYear); - } - - int timeYear = time.Year; - - if (startTimeYear != timeYear) { - time = time.AddYears(startTimeYear - timeYear); - } - - if (startTime > endTime) { - // In southern hemisphere, the daylight saving time starts later in the year, and ends in the beginning of next year. - // Note, the summer in the southern hemisphere begins late in the year. - isDst = (time < endTime || time >= startTime); - } - else { - // In northern hemisphere, the daylight saving time starts in the middle of the year. - isDst = (time >= startTime && time < endTime); - } - return isDst; - } - - // - // GetDaylightTime - - // - // Helper function that returns a DaylightTime from a year and AdjustmentRule - // - static private DaylightTime GetDaylightTime(Int32 year, AdjustmentRule rule) { - TimeSpan delta = rule.DaylightDelta; - DateTime startTime = TransitionTimeToDateTime(year, rule.DaylightTransitionStart); - DateTime endTime = TransitionTimeToDateTime(year, rule.DaylightTransitionEnd); - return new DaylightTime(startTime, endTime, delta); - } - - // - // TransitionTimeToDateTime - - // - // Helper function that converts a year and TransitionTime into a DateTime - // - static private DateTime TransitionTimeToDateTime(Int32 year, TransitionTime transitionTime) { - DateTime value; - DateTime timeOfDay = transitionTime.TimeOfDay; - - if (transitionTime.IsFixedDateRule) { - // create a DateTime from the passed in year and the properties on the transitionTime - - // if the day is out of range for the month then use the last day of the month - Int32 day = DateTime.DaysInMonth(year, transitionTime.Month); - - value = new DateTime(year, transitionTime.Month, (day < transitionTime.Day) ? day : transitionTime.Day, - timeOfDay.Hour, timeOfDay.Minute, timeOfDay.Second, timeOfDay.Millisecond); - } - else { - if (transitionTime.Week <= 4) { - // - // Get the (transitionTime.Week)th Sunday. - // - value = new DateTime(year, transitionTime.Month, 1, - timeOfDay.Hour, timeOfDay.Minute, timeOfDay.Second, timeOfDay.Millisecond); - - int dayOfWeek = (int)value.DayOfWeek; - int delta = (int)transitionTime.DayOfWeek - dayOfWeek; - if (delta < 0) { - delta += 7; - } - delta += 7 * (transitionTime.Week - 1); - - if (delta > 0) { - value = value.AddDays(delta); - } - } - else { - // - // If TransitionWeek is greater than 4, we will get the last week. - // - Int32 daysInMonth = DateTime.DaysInMonth(year, transitionTime.Month); - value = new DateTime(year, transitionTime.Month, daysInMonth, - timeOfDay.Hour, timeOfDay.Minute, timeOfDay.Second, timeOfDay.Millisecond); - - // This is the day of week for the last day of the month. - int dayOfWeek = (int)value.DayOfWeek; - int delta = dayOfWeek - (int)transitionTime.DayOfWeek; - if (delta < 0) { - delta += 7; - } - - if (delta > 0) { - value = value.AddDays(-delta); - } - } - } - return value; - } - - // - // IsInvalidTime - - // - // returns true when dateTime falls into a "hole in time". - // - public Boolean IsInvalidTime(DateTime dateTime) { - Boolean isInvalid = false; - - if ( (dateTime.Kind == DateTimeKind.Unspecified) - || (dateTime.Kind == DateTimeKind.Local && this == Local) ) { - - // only check Unspecified and (Local when this TimeZoneInfo instance is Local) - AdjustmentRule rule = GetAdjustmentRuleForTime(dateTime); - - - if (rule != null) { - DaylightTime daylightTime = GetDaylightTime(dateTime.Year, rule); - isInvalid = GetIsInvalidTime(dateTime, rule, daylightTime); - } - else { - isInvalid = false; - } - } - - return isInvalid; - } - - // - // GetIsInvalidTime - - // - // Helper function that checks if a given DateTime is in an invalid time ("time hole") - // A "time hole" occurs at a DST transition point when time jumps forward; - // For example, in Pacific Standard Time on Sunday, April 2, 2006 time jumps from - // 1:59:59.9999999 to 3AM. The time range 2AM to 2:59:59.9999999AM is the "time hole". - // A "time hole" is not limited to only occurring at the start of DST, and may occur at - // the end of DST as well. - // - static private Boolean GetIsInvalidTime(DateTime time, AdjustmentRule rule, DaylightTime daylightTime) { - Boolean isInvalid = false; - if (rule == null || rule.DaylightDelta == TimeSpan.Zero) { - return isInvalid; - } - - DateTime startInvalidTime; - DateTime endInvalidTime; - - // if at DST start we transition forward in time then there is an ambiguous time range at the DST end - if (rule.DaylightDelta < TimeSpan.Zero) { - startInvalidTime = daylightTime.End; - endInvalidTime = daylightTime.End - rule.DaylightDelta; /* */ - } - else { - startInvalidTime = daylightTime.Start; - endInvalidTime = daylightTime.Start + rule.DaylightDelta; /* */ - } - - isInvalid = (time >= startInvalidTime && time < endInvalidTime); - - if (!isInvalid && startInvalidTime.Year != endInvalidTime.Year) { - // there exists an extreme corner case where the start or end period is on a year boundary and - // because of this the comparison above might have been performed for a year-early or a year-later - // than it should have been. - DateTime startModifiedInvalidTime; - DateTime endModifiedInvalidTime; - try { - startModifiedInvalidTime = startInvalidTime.AddYears(1); - endModifiedInvalidTime = endInvalidTime.AddYears(1); - isInvalid = (time >= startModifiedInvalidTime && time < endModifiedInvalidTime); - } - catch (ArgumentOutOfRangeException) {} - - if (!isInvalid) { - try { - startModifiedInvalidTime = startInvalidTime.AddYears(-1); - endModifiedInvalidTime = endInvalidTime.AddYears(-1); - isInvalid = (time >= startModifiedInvalidTime && time < endModifiedInvalidTime); - } - catch (ArgumentOutOfRangeException) {} - } - } - return isInvalid; - } + // Shortcut for TimeZoneInfo.Local.GetUtcOffset + internal static TimeSpan GetLocalUtcOffset(DateTime dateTime, TimeZoneInfoOptions flags) + { + bool dst; + return Local.GetUtcOffset (dateTime, out dst); + } + + internal TimeSpan GetUtcOffset(DateTime dateTime, TimeZoneInfoOptions flags) + { + bool dst; + return GetUtcOffset (dateTime, out dst); + } + + static internal TimeSpan GetUtcOffsetFromUtc (DateTime time, TimeZoneInfo zone, out Boolean isDaylightSavings, out Boolean isAmbiguousLocalDst) + { + isDaylightSavings = false; + isAmbiguousLocalDst = false; + TimeSpan baseOffset = zone.BaseUtcOffset; + + if (zone.IsAmbiguousTime (time)) { + isAmbiguousLocalDst = true; + return baseOffset; + } + + return zone.GetUtcOffset (time, out isDaylightSavings); + } #endregion }