提交 21b58c96 编写于 作者: M Maxim Lipnin 提交者: Scott Ferguson

Fix time zone issue when jumping into DST (#16430)

Addresses an issue with jumping into DST for some time zones when the incorrect date-time offset is returned for date-time in UTC (which comes from DateTime.Now). The fix is to just check if the incoming date-time is in UTC.

Also added a set of tests for some time zones to verify jumping into DST in general.

Fixes https://github.com/mono/mono/issues/16395
上级 93b75f92
......@@ -1252,10 +1252,13 @@ namespace System
return false;
}
var isUtc = false;
if (dateTime.Kind != DateTimeKind.Utc) {
if (!TryAddTicks (date, -BaseUtcOffset.Ticks, out date, DateTimeKind.Utc))
return false;
}
} else
isUtc = true;
AdjustmentRule current = GetApplicableRule (date);
if (current != null) {
......@@ -1267,7 +1270,7 @@ namespace System
if (forOffset)
isDst = true;
offset = baseUtcOffset;
if (date >= new DateTime (tStart.Ticks + current.DaylightDelta.Ticks, DateTimeKind.Utc))
if (isUtc || (date >= new DateTime (tStart.Ticks + current.DaylightDelta.Ticks, DateTimeKind.Utc)))
{
offset += current.DaylightDelta;
isDst = true;
......
......@@ -53,6 +53,11 @@ namespace MonoTests.System
return "New Zealand Standard Time";
case "Europe/Athens":
return "GTB Standard Time";
case "Europe/Chisinau":
return "E. Europe Standard Time";
case "America/New_York":
return "Eastern Standard Time";
case "America/Chicago":
case "US/Eastern":
return "Eastern Standard Time";
case "US/Central":
......@@ -63,18 +68,57 @@ namespace MonoTests.System
case "Australia/Melbourne":
return "AUS Eastern Standard Time";
case "Europe/Brussels":
case "Europe/Copenhagen":
case "Europe/Paris":
case "Europe/Madrid":
return "Romance Standard Time";
case "Africa/Kinshasa":
return "W. Central Africa Standard Time";
case "Europe/Rome":
case "Europe/Vatican":
case "Europe/Vienna":
case "Europe/Berlin":
case "Europe/Luxembourg":
case "Europe/Malta":
case "Europe/Monaco":
case "Europe/Amsterdam":
case "Europe/Oslo":
case "Europe/San_Marino":
return "W. Europe Standard Time";
case "Canada/Eastern":
return "Eastern Standard Time";
case "Asia/Tehran":
return "Iran Standard Time";
case "Europe/Guernsey":
case "Europe/Dublin":
case "Europe/Isle_of_Man":
case "Europe/Jersey":
case "Europe/Lisbon":
case "Europe/London":
return "GMT Standard Time";
case "America/Havana":
return "Cuba Standard Time";
case "America/Anchorage":
return "Alaskan Standard Time";
case "Atlantic/Azores":
return "Azores Standard Time";
case "Asia/Jerusalem":
return "Israel Standard Time";
case "Asia/Amman":
return "Jordan Standard Time";
case "Europe/Tirane":
case "Europe/Warsaw":
return "Central European Standard Time";
case "Europe/Sofia":
case "Europe/Tallinn":
case "Europe/Riga":
case "Europe/Vilnius":
case "Europe/Kiev":
return "FLE Standard Time";
case "Europe/Prague":
case "Europe/Budapest":
case "Europe/Bratislava":
return "Central Europe Standard Time";
default:
Assert.Fail ($"No mapping defined for zone id '{id}'");
return null;
......@@ -905,6 +949,218 @@ namespace MonoTests.System
Assert.AreEqual (new TimeSpan (0, 0, 0), tzi.GetUtcOffset (date));
#endif
}
[Test]
public void Bug_16395 ()
{
// Cuba, Havana (Cuba Standard Time): Jumps ahead at 12:00 AM on 3/8/2020 to 1:00 AM
CheckJumpingIntoDST ("America/Havana",
new DateTime (2020, 3, 8, 0, 0, 0), new DateTime (2020, 3, 8, 0, 30, 0), new DateTime (2020, 3, 8, 1, 0, 0),
new TimeSpan (-5, 0, 0), new TimeSpan (-4, 0, 0));
// US, Kansas City, MO (US Central Time): Jumps ahead at 2:00 AM on 3/8/2020 to 3:00 AM
CheckJumpingIntoDST ("America/Chicago",
new DateTime (2020, 3, 8, 2, 0, 0), new DateTime (2020, 3, 8, 2, 30, 0), new DateTime (2020, 3, 8, 3, 0, 0),
new TimeSpan (-6, 0, 0), new TimeSpan (-5, 0, 0));
// Anchorage, AK (Alaska Time): Jumps ahead at 2:00 AM on 3/8/2020 to 3:00 AM
CheckJumpingIntoDST ("America/Anchorage",
new DateTime (2020, 3, 8, 2, 0, 0), new DateTime (2020, 3, 8, 2, 30, 0), new DateTime (2020, 3, 8, 3, 0, 0),
new TimeSpan (-9, 0, 0), new TimeSpan (-8, 0, 0));
// Azores ST (Ponta Delgada, Portugal): Jumps ahead at 12:00 AM on 3/29/2020 to 1:00 AM
CheckJumpingIntoDST ("Atlantic/Azores",
new DateTime (2020, 3, 29, 0, 0, 0), new DateTime (2020, 3, 29, 0, 30, 0), new DateTime (2020, 3, 29, 1, 0, 0),
new TimeSpan (-1, 0, 0), new TimeSpan (0, 0, 0));
// Iran, Tehran (Iran ST): Jumps ahead at 12:00 AM on 3/21/2020 to 1:00 AM
CheckJumpingIntoDST ("Asia/Tehran",
new DateTime (2020, 3, 21, 0, 0, 0), new DateTime (2020, 3, 21, 0, 30, 0), new DateTime (2020, 3, 21, 1, 0, 0),
new TimeSpan (3, 30, 0), new TimeSpan (4, 30, 0));
// Israel, Jerusalem (Israel ST): Jumps ahead at 2:00 AM on 3/27/2020 to 3:00 AM
CheckJumpingIntoDST ("Asia/Jerusalem",
new DateTime (2020, 3, 27, 2, 0, 0), new DateTime (2020, 3, 27, 2, 30, 0), new DateTime (2020, 3, 27, 3, 0, 0),
new TimeSpan (2, 0, 0), new TimeSpan (3, 0, 0));
// Jordan, Amman (Eastern European ST): Jumps ahead at 12:00 AM on 3/27/2020 to 1:00 AM
CheckJumpingIntoDST ("Asia/Amman",
new DateTime (2020, 3, 27, 0, 0, 0), new DateTime (2020, 3, 27, 0, 30, 0), new DateTime (2020, 3, 27, 1, 0, 0),
new TimeSpan (2, 0, 0), new TimeSpan (3, 0, 0));
// Albania, Tirana (Central European ST): Jumps ahead at 2:00 AM on 3/29/2020 to 3:00 AM
CheckJumpingIntoDST ("Europe/Tirane",
new DateTime (2020, 3, 29, 2, 0, 0), new DateTime (2020, 3, 29, 2, 30, 0), new DateTime (2020, 3, 29, 3, 0, 0),
new TimeSpan (1, 0, 0), new TimeSpan (2, 0, 0));
// Austria, Vienna (Central European ST): Jumps ahead at 2:00 AM on 3/29/2020 to 3:00 AM
CheckJumpingIntoDST ("Europe/Vienna",
new DateTime (2020, 3, 29, 2, 0, 0), new DateTime (2020, 3, 29, 2, 30, 0), new DateTime (2020, 3, 29, 3, 0, 0),
new TimeSpan (1, 0, 0), new TimeSpan (2, 0, 0));
// Belgium, Brussels (Central European ST): Jumps ahead at 2:00 AM on 3/29/2020 to 3:00 AM
CheckJumpingIntoDST ("Europe/Brussels",
new DateTime (2020, 3, 29, 2, 0, 0), new DateTime (2020, 3, 29, 2, 30, 0), new DateTime (2020, 3, 29, 3, 0, 0),
new TimeSpan (1, 0, 0), new TimeSpan (2, 0, 0));
// Bulgaria, Sofia (Eastern European ST): Jumps ahead at 3:00 AM on 3/29/2020 to 4:00 AM
CheckJumpingIntoDST ("Europe/Sofia",
new DateTime (2020, 3, 29, 3, 0, 0), new DateTime (2020, 3, 29, 3, 30, 0), new DateTime (2020, 3, 29, 4, 0, 0),
new TimeSpan (2, 0, 0), new TimeSpan (3, 0, 0));
// Czechia, Prague (Central European ST): Jumps ahead at 2:00 AM on 3/29/2020 to 3:00 AM
CheckJumpingIntoDST ("Europe/Prague",
new DateTime (2020, 3, 29, 2, 0, 0), new DateTime (2020, 3, 29, 2, 30, 0), new DateTime (2020, 3, 29, 3, 0, 0),
new TimeSpan (1, 0, 0), new TimeSpan (2, 0, 0));
// Denmark, Copenhagen (Central European ST): Jumps ahead at 2:00 AM on 3/29/2020 to 3:00 AM
CheckJumpingIntoDST ("Europe/Copenhagen",
new DateTime (2020, 3, 29, 2, 0, 0), new DateTime (2020, 3, 29, 2, 30, 0), new DateTime (2020, 3, 29, 3, 0, 0),
new TimeSpan (1, 0, 0), new TimeSpan (2, 0, 0));
// Estonia, Tallinn (Eastern European ST): Jumps ahead at 3:00 AM on 3/29/2020 to 4:00 AM
CheckJumpingIntoDST ("Europe/Tallinn",
new DateTime (2020, 3, 29, 3, 0, 0), new DateTime (2020, 3, 29, 3, 30, 0), new DateTime (2020, 3, 29, 4, 0, 0),
new TimeSpan (2, 0, 0), new TimeSpan (3, 0, 0));
// France, Paris (Central European ST): Jumps ahead at 2:00 AM on 3/29/2020 to 3:00 AM
CheckJumpingIntoDST ("Europe/Paris",
new DateTime (2020, 3, 29, 2, 0, 0), new DateTime (2020, 3, 29, 2, 30, 0), new DateTime (2020, 3, 29, 3, 0, 0),
new TimeSpan (1, 0, 0), new TimeSpan (2, 0, 0));
// Germany, Berlin (Central European ST): Jumps ahead at 2:00 AM on 3/29/2020 to 3:00 AM
CheckJumpingIntoDST ("Europe/Berlin",
new DateTime (2020, 3, 29, 2, 0, 0), new DateTime (2020, 3, 29, 2, 30, 0), new DateTime (2020, 3, 29, 3, 0, 0),
new TimeSpan (1, 0, 0), new TimeSpan (2, 0, 0));
// Greece, Athens (Eastern European ST): Jumps ahead at 3:00 AM on 3/29/2020 to 4:00 AM
CheckJumpingIntoDST ("Europe/Athens",
new DateTime (2020, 3, 29, 3, 0, 0), new DateTime (2020, 3, 29, 3, 30, 0), new DateTime (2020, 3, 29, 4, 0, 0),
new TimeSpan (2, 0, 0), new TimeSpan (3, 0, 0));
// Guernsey (UK) Jumps ahead at 1:00 AM on 3/29/2020 to 2:00 AM
CheckJumpingIntoDST ("Europe/Guernsey",
new DateTime (2020, 3, 29, 1, 0, 0), new DateTime (2020, 3, 29, 1, 30, 0), new DateTime (2020, 3, 29, 2, 0, 0),
new TimeSpan (0, 0, 0), new TimeSpan (1, 0, 0));
// Holy See, Vatican City (Central European ST): Jumps ahead at 2:00 AM on 3/29/2020 to 3:00 AM
CheckJumpingIntoDST ("Europe/Vatican",
new DateTime (2020, 3, 29, 2, 0, 0), new DateTime (2020, 3, 29, 2, 30, 0), new DateTime (2020, 3, 29, 3, 0, 0),
new TimeSpan (1, 0, 0), new TimeSpan (2, 0, 0));
// Hungary, Budapest (Central European ST): Jumps ahead at 2:00 AM on 3/29/2020 to 3:00 AM
CheckJumpingIntoDST ("Europe/Budapest",
new DateTime (2020, 3, 29, 2, 0, 0), new DateTime (2020, 3, 29, 2, 30, 0), new DateTime (2020, 3, 29, 3, 0, 0),
new TimeSpan (1, 0, 0), new TimeSpan (2, 0, 0));
// // Ireland, Dublin (Greenwich Mean Time -> Irish Standard Time): Jumps ahead at 1:00 AM on 3/29/2020 to 2:00 AM
// CheckJumpingIntoDST ("Europe/Dublin",
// new DateTime (2020, 3, 29, 1, 0, 0), new DateTime (2020, 3, 29, 1, 30, 0), new DateTime (2020, 3, 29, 2, 0, 0),
// new TimeSpan (0, 0, 0), new TimeSpan (1, 0, 0));
// UK, Douglas, Isle of Man (GMT+1:00): Jumps ahead at 1:00 AM on 3/29/2020 to 2:00 AM
CheckJumpingIntoDST ("Europe/Isle_of_Man",
new DateTime (2020, 3, 29, 1, 0, 0), new DateTime (2020, 3, 29, 1, 30, 0), new DateTime (2020, 3, 29, 2, 0, 0),
new TimeSpan (0, 0, 0), new TimeSpan (1, 0, 0));
// Italy, Rome (Central European ST): Jumps ahead at 2:00 AM on 3/29/2020 to 3:00 AM
CheckJumpingIntoDST ("Europe/Rome",
new DateTime (2020, 3, 29, 2, 0, 0), new DateTime (2020, 3, 29, 2, 30, 0), new DateTime (2020, 3, 29, 3, 0, 0),
new TimeSpan (1, 0, 0), new TimeSpan (2, 0, 0));
// Jersey (UK): Jumps ahead at 1:00 AM on 3/29/2020 to 2:00 AM
CheckJumpingIntoDST ("Europe/Jersey",
new DateTime (2020, 3, 29, 1, 0, 0), new DateTime (2020, 3, 29, 1, 30, 0), new DateTime (2020, 3, 29, 2, 0, 0),
new TimeSpan (0, 0, 0), new TimeSpan (1, 0, 0));
// Latvia, Riga (Eastern European ST): Jumps ahead at 3:00 AM on 3/29/2020 to 4:00 AM
CheckJumpingIntoDST ("Europe/Riga",
new DateTime (2020, 3, 29, 3, 0, 0), new DateTime (2020, 3, 29, 3, 30, 0), new DateTime (2020, 3, 29, 4, 0, 0),
new TimeSpan (2, 0, 0), new TimeSpan (3, 0, 0));
// Lithuania, Vilnius (Eastern European ST): Jumps ahead at 3:00 AM on 3/29/2020 to 4:00 AM
CheckJumpingIntoDST ("Europe/Vilnius",
new DateTime (2020, 3, 29, 3, 0, 0), new DateTime (2020, 3, 29, 3, 30, 0), new DateTime (2020, 3, 29, 4, 0, 0),
new TimeSpan (2, 0, 0), new TimeSpan (3, 0, 0));
// Luxembourg, Luxembourg (Central European ST): Jumps ahead at 2:00 AM on 3/29/2020 to 3:00 AM
CheckJumpingIntoDST ("Europe/Luxembourg",
new DateTime (2020, 3, 29, 2, 0, 0), new DateTime (2020, 3, 29, 2, 30, 0), new DateTime (2020, 3, 29, 3, 0, 0),
new TimeSpan (1, 0, 0), new TimeSpan (2, 0, 0));
// Malta, Valletta (Central European ST): Jumps ahead at 2:00 AM on 3/29/2020 to 3:00 AM
CheckJumpingIntoDST ("Europe/Malta",
new DateTime (2020, 3, 29, 2, 0, 0), new DateTime (2020, 3, 29, 2, 30, 0), new DateTime (2020, 3, 29, 3, 0, 0),
new TimeSpan (1, 0, 0), new TimeSpan (2, 0, 0));
// Moldova, Chişinău (Eastern European ST): Jumps ahead at 2:00 AM on 3/29/2020 to 3:00 AM
CheckJumpingIntoDST ("Europe/Chisinau",
new DateTime (2020, 3, 29, 2, 0, 0), new DateTime (2020, 3, 29, 2, 30, 0), new DateTime (2020, 3, 29, 3, 0, 0),
new TimeSpan (2, 0, 0), new TimeSpan (3, 0, 0));
// Monaco, Monaco (Central European ST): Jumps ahead at 2:00 AM on 3/29/2020 to 3:00 AM
CheckJumpingIntoDST ("Europe/Monaco",
new DateTime (2020, 3, 29, 2, 0, 0), new DateTime (2020, 3, 29, 2, 30, 0), new DateTime (2020, 3, 29, 3, 0, 0),
new TimeSpan (1, 0, 0), new TimeSpan (2, 0, 0));
// Netherlands, Amsterdam (Central European ST): Jumps ahead at 2:00 AM on 3/29/2020 to 3:00 AM
CheckJumpingIntoDST ("Europe/Amsterdam",
new DateTime (2020, 3, 29, 2, 0, 0), new DateTime (2020, 3, 29, 2, 30, 0), new DateTime (2020, 3, 29, 3, 0, 0),
new TimeSpan (1, 0, 0), new TimeSpan (2, 0, 0));
// Norway, Oslo (Central European ST): Jumps ahead at 2:00 AM on 3/29/2020 to 3:00 AM
CheckJumpingIntoDST ("Europe/Oslo",
new DateTime (2020, 3, 29, 2, 0, 0), new DateTime (2020, 3, 29, 2, 30, 0), new DateTime (2020, 3, 29, 3, 0, 0),
new TimeSpan (1, 0, 0), new TimeSpan (2, 0, 0));
// Poland, Warsaw (Central European ST): Jumps ahead at 2:00 AM on 3/29/2020 to 3:00 AM
CheckJumpingIntoDST ("Europe/Warsaw",
new DateTime (2020, 3, 29, 2, 0, 0), new DateTime (2020, 3, 29, 2, 30, 0), new DateTime (2020, 3, 29, 3, 0, 0),
new TimeSpan (1, 0, 0), new TimeSpan (2, 0, 0));
// Portugal, Lisbon (Western European ST): Jumps ahead at 1:00 AM on 3/29/2020 to 2:00 AM
CheckJumpingIntoDST ("Europe/Lisbon",
new DateTime (2020, 3, 29, 1, 0, 0), new DateTime (2020, 3, 29, 1, 30, 0), new DateTime (2020, 3, 29, 2, 0, 0),
new TimeSpan (0, 0, 0), new TimeSpan (1, 0, 0));
// San Marino, San Marino (Central European ST): Jumps ahead at 2:00 AM on 3/29/2020 to 3:00 AM
CheckJumpingIntoDST ("Europe/San_Marino",
new DateTime (2020, 3, 29, 2, 0, 0), new DateTime (2020, 3, 29, 2, 30, 0), new DateTime (2020, 3, 29, 3, 0, 0),
new TimeSpan (1, 0, 0), new TimeSpan (2, 0, 0));
// Slovakia, Bratislava (Central European ST): Jumps ahead at 2:00 AM on 3/29/2020 to 3:00 AM
CheckJumpingIntoDST ("Europe/Bratislava",
new DateTime (2020, 3, 29, 2, 0, 0), new DateTime (2020, 3, 29, 2, 30, 0), new DateTime (2020, 3, 29, 3, 0, 0),
new TimeSpan (1, 0, 0), new TimeSpan (2, 0, 0));
// Spain, Madrid (Central European ST): Jumps ahead at 2:00 AM on 3/29/2020 to 3:00 AM
CheckJumpingIntoDST ("Europe/Madrid",
new DateTime (2020, 3, 29, 2, 0, 0), new DateTime (2020, 3, 29, 2, 30, 0), new DateTime (2020, 3, 29, 3, 0, 0),
new TimeSpan (1, 0, 0), new TimeSpan (2, 0, 0));
// Ukraine, Kiev (Eastern European ST): Jumps ahead at 3:00 AM on 3/29/2020 to 4:00 AM
CheckJumpingIntoDST ("Europe/Kiev",
new DateTime (2020, 3, 29, 3, 0, 0), new DateTime (2020, 3, 29, 3, 30, 0), new DateTime (2020, 3, 29, 4, 0, 0),
new TimeSpan (2, 0, 0), new TimeSpan (3, 0, 0));
// United Kingdom, London (British ST): Jumps ahead at 1:00 AM on 3/29/2020 to 2:00 AM
CheckJumpingIntoDST ("Europe/London",
new DateTime (2020, 3, 29, 1, 0, 0), new DateTime (2020, 3, 29, 1, 30, 0), new DateTime (2020, 3, 29, 2, 0, 0),
new TimeSpan (0, 0, 0), new TimeSpan (1, 0, 0));
}
void CheckJumpingIntoDST (string tzId, DateTime dstDeltaStart, DateTime inDstDelta, DateTime dstDeltaEnd, TimeSpan baseOffset, TimeSpan dstOffset)
{
var tzi = TimeZoneInfo.FindSystemTimeZoneById (MapTimeZoneId (tzId));
Assert.IsFalse (tzi.IsDaylightSavingTime (dstDeltaStart), $"{tzId}: #1");
Assert.AreEqual (baseOffset, tzi.GetUtcOffset (dstDeltaStart), $"{tzId}: #2");
Assert.IsFalse (tzi.IsDaylightSavingTime (inDstDelta), $"{tzId}: #3");
Assert.AreEqual (baseOffset, tzi.GetUtcOffset (inDstDelta), $"{tzId}: #4");
Assert.IsTrue (tzi.IsDaylightSavingTime (dstDeltaEnd), $"{tzId}: #5");
Assert.AreEqual (dstOffset, tzi.GetUtcOffset (dstDeltaEnd), $"{tzId}: #6");
}
}
[TestFixture]
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册