+ private TimeSpan GetUtcOffset (DateTime dateTime, out bool isDST)
+ {
+ isDST = false;
+
+ TimeZoneInfo tz = this;
+ if (dateTime.Kind == DateTimeKind.Utc)
+ tz = TimeZoneInfo.Utc;
+
+ if (dateTime.Kind == DateTimeKind.Local)
+ tz = TimeZoneInfo.Local;
+
+ bool isTzDst;
+ var tzOffset = GetUtcOffset (dateTime, tz, out isTzDst);
+
+ if (tz == this) {
+ isDST = isTzDst;
+ return tzOffset;
+ }
+
+ var utcTicks = dateTime.Ticks - tzOffset.Ticks;
+ if (utcTicks < 0 || utcTicks > DateTime.MaxValue.Ticks)
+ return BaseUtcOffset;
+
+ var utcDateTime = new DateTime (utcTicks, DateTimeKind.Utc);
+
+ return GetUtcOffset (utcDateTime, this, out isDST);
+ }
+
+ private static TimeSpan GetUtcOffset (DateTime dateTime, TimeZoneInfo tz, out bool isDST)
+ {
+ if (dateTime.Kind == DateTimeKind.Local && tz != TimeZoneInfo.Local)
+ throw new Exception ();
+
+ isDST = false;
+
+ if (tz == TimeZoneInfo.Utc)
+ return TimeSpan.Zero;
+
+ TimeSpan offset;
+ if (tz.TryGetTransitionOffset(dateTime, out offset, out isDST))
+ return offset;
+
+ if (dateTime.Kind == DateTimeKind.Utc) {
+ var utcRule = tz.GetApplicableRule (dateTime);
+ if (utcRule != null && tz.IsInDST (utcRule, dateTime)) {
+ isDST = true;
+ return tz.BaseUtcOffset + utcRule.DaylightDelta;
+ }
+
+ return tz.BaseUtcOffset;
+ }
+
+ var stdTicks = dateTime.Ticks - tz.BaseUtcOffset.Ticks;
+ if (stdTicks < 0 || stdTicks > DateTime.MaxValue.Ticks)
+ 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)
+ return tz.BaseUtcOffset;
+
+ dstUtcDateTime = new DateTime (dstTicks, DateTimeKind.Utc);
+ }
+
+ if (tzRule != null && tz.IsInDST (tzRule, stdUtcDateTime) && tz.IsInDST (tzRule, dstUtcDateTime)) {
+ isDST = true;
+ return tz.BaseUtcOffset + tzRule.DaylightDelta;
+ }
+
+ return tz.BaseUtcOffset;
+ }
+