Skip to content

Commit 5a85300

Browse files
MaximLipninscott-ferguson-unity
authored andcommitted
Port 13553 "Fix transition offset logic" to master
1 parent ad6f94d commit 5a85300

File tree

2 files changed

+75
-18
lines changed

2 files changed

+75
-18
lines changed

mcs/class/corlib/System/TimeZoneInfo.cs

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -805,7 +805,7 @@ public TimeSpan GetUtcOffset (DateTimeOffset dateTimeOffset)
805805
return GetUtcOffset (dateTimeOffset.UtcDateTime, out isDST);
806806
}
807807

808-
private TimeSpan GetUtcOffset (DateTime dateTime, out bool isDST)
808+
private TimeSpan GetUtcOffset (DateTime dateTime, out bool isDST, bool forOffset = false)
809809
{
810810
isDST = false;
811811

@@ -817,7 +817,7 @@ private TimeSpan GetUtcOffset (DateTime dateTime, out bool isDST)
817817
tz = TimeZoneInfo.Local;
818818

819819
bool isTzDst;
820-
var tzOffset = GetUtcOffsetHelper (dateTime, tz, out isTzDst);
820+
var tzOffset = GetUtcOffsetHelper (dateTime, tz, out isTzDst, forOffset);
821821

822822
if (tz == this) {
823823
isDST = isTzDst;
@@ -828,11 +828,11 @@ private TimeSpan GetUtcOffset (DateTime dateTime, out bool isDST)
828828
if (!TryAddTicks (dateTime, -tzOffset.Ticks, out utcDateTime, DateTimeKind.Utc))
829829
return BaseUtcOffset;
830830

831-
return GetUtcOffsetHelper (utcDateTime, this, out isDST);
831+
return GetUtcOffsetHelper (utcDateTime, this, out isDST, forOffset);
832832
}
833833

834834
// This is an helper method used by the method above, do not use this on its own.
835-
private static TimeSpan GetUtcOffsetHelper (DateTime dateTime, TimeZoneInfo tz, out bool isDST)
835+
private static TimeSpan GetUtcOffsetHelper (DateTime dateTime, TimeZoneInfo tz, out bool isDST, bool forOffset = false)
836836
{
837837
if (dateTime.Kind == DateTimeKind.Local && tz != TimeZoneInfo.Local)
838838
throw new Exception ();
@@ -843,7 +843,7 @@ private static TimeSpan GetUtcOffsetHelper (DateTime dateTime, TimeZoneInfo tz,
843843
return TimeSpan.Zero;
844844

845845
TimeSpan offset;
846-
if (tz.TryGetTransitionOffset(dateTime, out offset, out isDST))
846+
if (tz.TryGetTransitionOffset(dateTime, out offset, out isDST, forOffset))
847847
return offset;
848848

849849
if (dateTime.Kind == DateTimeKind.Utc) {
@@ -870,10 +870,12 @@ private static TimeSpan GetUtcOffsetHelper (DateTime dateTime, TimeZoneInfo tz,
870870

871871
if (tzRule != null && tz.IsInDST (tzRule, dateTime)) {
872872
// Replicate what .NET does when given a time which falls into the hour which is lost when
873-
// DST starts. isDST should always be true but the offset should be BaseUtcOffset without the
873+
// DST starts. isDST should be false and the offset should be BaseUtcOffset without the
874874
// DST delta while in that hour.
875-
isDST = true;
875+
if (forOffset)
876+
isDST = true;
876877
if (tz.IsInDST (tzRule, dstUtcDateTime)) {
878+
isDST = true;
877879
return tz.BaseUtcOffset + tzRule.DaylightDelta;
878880
} else {
879881
return tz.BaseUtcOffset;
@@ -982,7 +984,21 @@ internal bool IsDaylightSavingTime (DateTime dateTime, TimeZoneInfoOptions flags
982984

983985
public bool IsDaylightSavingTime (DateTimeOffset dateTimeOffset)
984986
{
985-
return IsDaylightSavingTime (dateTimeOffset.DateTime);
987+
var dateTime = dateTimeOffset.DateTime;
988+
989+
if (dateTime.Kind == DateTimeKind.Local && IsInvalidTime (dateTime))
990+
throw new ArgumentException ("dateTime is invalid and Kind is Local");
991+
992+
if (this == TimeZoneInfo.Utc)
993+
return false;
994+
995+
if (!SupportsDaylightSavingTime)
996+
return false;
997+
998+
bool isDst;
999+
GetUtcOffset (dateTime, out isDst, true);
1000+
1001+
return isDst;
9861002
}
9871003

9881004
internal DaylightTime GetDaylightChanges (int year)
@@ -1219,7 +1235,7 @@ private AdjustmentRule GetApplicableRule (DateTime dateTime)
12191235
return null;
12201236
}
12211237

1222-
private bool TryGetTransitionOffset (DateTime dateTime, out TimeSpan offset,out bool isDst)
1238+
private bool TryGetTransitionOffset (DateTime dateTime, out TimeSpan offset, out bool isDst, bool forOffset = false)
12231239
{
12241240
offset = BaseUtcOffset;
12251241
isDst = false;
@@ -1240,13 +1256,22 @@ private bool TryGetTransitionOffset (DateTime dateTime, out TimeSpan offset,out
12401256
return false;
12411257
}
12421258

1243-
AdjustmentRule current = GetApplicableRule(date);
1259+
AdjustmentRule current = GetApplicableRule (date);
12441260
if (current != null) {
1245-
DateTime tStart = TransitionPoint(current.DaylightTransitionStart, date.Year);
1246-
DateTime tEnd = TransitionPoint(current.DaylightTransitionEnd, date.Year);
1261+
DateTime tStart = TransitionPoint (current.DaylightTransitionStart, date.Year);
1262+
DateTime tEnd = TransitionPoint (current.DaylightTransitionEnd, date.Year);
1263+
TryAddTicks (tStart, -BaseUtcOffset.Ticks, out tStart, DateTimeKind.Utc);
1264+
TryAddTicks (tEnd, -BaseUtcOffset.Ticks, out tEnd, DateTimeKind.Utc);
12471265
if ((date >= tStart) && (date <= tEnd)) {
1248-
offset = baseUtcOffset + current.DaylightDelta;
1249-
isDst = true;
1266+
if (forOffset)
1267+
isDst = true;
1268+
offset = baseUtcOffset;
1269+
if (date >= new DateTime (tStart.Ticks + current.DaylightDelta.Ticks, DateTimeKind.Utc))
1270+
{
1271+
offset += current.DaylightDelta;
1272+
isDst = true;
1273+
}
1274+
12501275
return true;
12511276
}
12521277
}

mcs/class/corlib/Test/System/TimeZoneInfoTest.cs

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ public static string MapTimeZoneId (string id)
5555
return "GTB Standard Time";
5656
case "US/Eastern":
5757
return "Eastern Standard Time";
58+
case "US/Central":
59+
return "Central Standard Time";
5860
case "US/Pacific":
5961
return "Pacific Standard Time";
6062
case "Australia/Sydney":
@@ -760,13 +762,18 @@ public void TestAthensDST ()
760762
[Test]
761763
public void TestAthensDST_InDSTDelta ()
762764
{
763-
// In .NET GetUtcOffset() returns the BaseUtcOffset for times within the hour
764-
// lost when DST starts but IsDaylightSavingTime() returns true.
765+
// In .NET/.Net Core GetUtcOffset() returns the BaseUtcOffset for times within the hour
766+
// lost when DST starts and IsDaylightSavingTime() returns false for datetime and true for datetimeoffset
765767

766768
TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById (MapTimeZoneId ("Europe/Athens"));
767769

768-
var date = new DateTime (2014, 3, 30 , 3, 0, 0);
769-
Assert.IsTrue (tzi.IsDaylightSavingTime (date));
770+
var date = new DateTime (2014, 3, 30 , 2, 0, 0);
771+
Assert.IsFalse (tzi.IsDaylightSavingTime (date));
772+
Assert.AreEqual (new TimeSpan (2, 0, 0), tzi.GetUtcOffset (date));
773+
Assert.IsFalse (tzi.IsDaylightSavingTime (new DateTimeOffset (date, tzi.GetUtcOffset (date))));
774+
775+
date = new DateTime (2014, 3, 30 , 3, 0, 0);
776+
Assert.IsFalse (tzi.IsDaylightSavingTime (date));
770777
Assert.AreEqual (new TimeSpan (2, 0, 0), tzi.GetUtcOffset (date));
771778
Assert.IsTrue (tzi.IsDaylightSavingTime (new DateTimeOffset (date, tzi.GetUtcOffset (date))));
772779

@@ -842,6 +849,31 @@ public void TestIsDST_DateTimeOffset ()
842849
dateOffset = new DateTimeOffset (date, offset);
843850
Assert.IsTrue (tzi.IsDaylightSavingTime (dateOffset));
844851
}
852+
853+
// https://github.com/mono/mono/issues/9664
854+
[Test]
855+
public void Bug_9664 ()
856+
{
857+
TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById (MapTimeZoneId ("US/Central"));
858+
var date = new DateTime (2019, 3, 9, 21, 0, 0);
859+
Assert.IsFalse (tzi.IsDaylightSavingTime (date));
860+
Assert.AreEqual (new TimeSpan (-6, 0, 0), tzi.GetUtcOffset (date));
861+
862+
tzi = TimeZoneInfo.FindSystemTimeZoneById (MapTimeZoneId ("US/Central"));
863+
date = new DateTime (2019, 3, 10, 2, 0, 0);
864+
Assert.IsFalse (tzi.IsDaylightSavingTime (date));
865+
Assert.AreEqual (new TimeSpan (-6, 0, 0), tzi.GetUtcOffset (date));
866+
867+
tzi = TimeZoneInfo.FindSystemTimeZoneById (MapTimeZoneId ("US/Central"));
868+
date = new DateTime (2019, 3, 10, 2, 30, 0);
869+
Assert.IsFalse (tzi.IsDaylightSavingTime (date));
870+
Assert.AreEqual (new TimeSpan (-6, 0, 0), tzi.GetUtcOffset (date));
871+
872+
tzi = TimeZoneInfo.FindSystemTimeZoneById (MapTimeZoneId ("US/Central"));
873+
date = new DateTime (2019, 3, 10, 3, 0, 0);
874+
Assert.IsTrue (tzi.IsDaylightSavingTime (date));
875+
Assert.AreEqual (new TimeSpan (-5, 0, 0), tzi.GetUtcOffset (date));
876+
}
845877
}
846878

847879
[TestFixture]

0 commit comments

Comments
 (0)