Merge pull request #1696 from esdrubal/tzrefactor
[mono.git] / mcs / class / corlib / System / TimeZoneInfo.cs
index f6f8285bbf75059d31debf865c0332446836a3d1..0c17412242c835af7107d559a158ce9be1e25444 100644 (file)
@@ -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<TimeZoneInfo>, ISerializable, IDeserializationCallback
+       partial class TimeZoneInfo
        {
                TimeSpan baseUtcOffset;
                public TimeSpan BaseUtcOffset {
@@ -233,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;
@@ -280,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)
@@ -315,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)
@@ -375,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)
@@ -435,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 ();
@@ -726,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 ();
@@ -766,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)) {
@@ -893,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 {
@@ -1023,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;
@@ -1055,19 +1124,13 @@ namespace System
                        DateTime date = dateTime;
 
                        if (dateTime.Kind == DateTimeKind.Local && this != TimeZoneInfo.Local) {
-                               var ticks = date.ToUniversalTime ().Ticks + BaseUtcOffset.Ticks;
-                               if (ticks < DateTime.MinValue.Ticks || ticks > DateTime.MaxValue.Ticks)
+                               if (!TryAddTicks (date.ToUniversalTime (), BaseUtcOffset.Ticks, out date, DateTimeKind.Utc))
                                        return false;
-
-                               date = new DateTime (ticks, DateTimeKind.Utc);
                        }
 
                        if (dateTime.Kind != DateTimeKind.Utc) {
-                               var ticks = date.Ticks - BaseUtcOffset.Ticks;
-                               if (ticks < DateTime.MinValue.Ticks || ticks > DateTime.MaxValue.Ticks)
+                               if (!TryAddTicks (date, -BaseUtcOffset.Ticks, out date, DateTimeKind.Utc))
                                        return false;
-
-                               date = new DateTime (ticks, DateTimeKind.Utc);
                        }
 
                        for (var i =  transitions.Count - 1; i >= 0; i--) {
@@ -1203,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);
@@ -1224,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;
@@ -1302,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
        }