| // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import "package:expect/expect.dart"; |
| |
| // Make sure that date-times close to daylight savings work correctly. |
| // See http://dartbug.com/30550 |
| |
| /// A list of (decomposed) date-times where daylight saving changes |
| /// happen. |
| /// |
| /// This list covers multiple timezones to increase test coverage on |
| /// different machines. |
| final daylightSavingChanges = [ |
| // TZ environment, y, m, d, h, change. |
| ["Europe/Paris", 2017, 03, 26, 02, 60], |
| ["Europe/Paris", 2017, 10, 29, 03, -60], |
| ["Antarctica/Troll", 2017, 03, 19, 01, 120], |
| ["Antarctica/Troll", 2017, 10, 29, 03, -120], |
| ["Australia/Canberra", 2017, 04, 02, 03, -60], |
| ["Australia/Canberra", 2017, 10, 01, 02, 60], |
| ["Australia/Lord_Howe", 2017, 04, 02, 02, -30], |
| ["Australia/Lord_Howe", 2017, 10, 01, 02, 30], |
| ["Atlantic/Bermuda", 2017, 03, 12, 02, 60], // US and Canada. |
| ["Atlantic/Bermuda", 2017, 11, 05, 02, -60], |
| ["America/Campo_Grande", 2017, 02, 19, 00, -60], // Brazil |
| ["America/Campo_Grande", 2017, 10, 15, 00, 60], |
| ["America/Santiago", 2017, 05, 14, 00, -60], |
| ["America/Santiago", 2017, 08, 13, 00, 60], |
| ["Chile/EasterIsland", 2017, 05, 13, 22, -60], |
| ["Chile/EasterIsland", 2017, 08, 12, 22, 60], |
| ["Pacific/Fiji", 2017, 01, 15, 03, -60], |
| ["Pacific/Fiji", 2017, 11, 05, 02, 60], |
| ["America/Scoresbysund", 2017, 03, 26, 00, 60], // Ittoqqortoormiit. |
| ["America/Scoresbysund", 2017, 10, 29, 01, -60], |
| ["Asia/Tehran", 2017, 03, 22, 00, 60], |
| ["Asia/Tehran", 2017, 09, 22, 00, -60], |
| ["Israel", 2017, 03, 24, 02, 60], |
| ["Israel", 2017, 10, 29, 02, -60], |
| ["Asia/Amman", 2017, 03, 31, 00, 60], |
| ["Asia/Amman", 2017, 10, 27, 01, -60], |
| ["Mexico/General", 2017, 04, 02, 02, 60], |
| ["Mexico/General", 2017, 10, 29, 02, -60], |
| ]; |
| |
| void runTests() { |
| // Makes sure we don't go into the wrong direction during a |
| // daylight-savings change (as happened in #30550). |
| for (var test in daylightSavingChanges) { |
| for (int i = 0; i < 2; i++) { |
| var year = test[1] as int; |
| var month = test[2] as int; |
| var day = test[3] as int; |
| var hour = test[4] as int; |
| |
| var minute = i == 0 ? 0 : test[5] as int; |
| // Rather adjust the hours than keeping the minutes. |
| hour += minute ~/ 60; |
| minute = minute.remainder(60) as int; |
| if (hour < 0) { |
| hour += 24; |
| day--; |
| } |
| |
| { |
| // Check that microseconds are taken into account. |
| var dtMillisecond = new DateTime(year, month, day, hour, minute, 0, 1); |
| var dtSecond = new DateTime(year, month, day, hour, minute, 1); |
| Expect.equals(const Duration(milliseconds: 999), |
| dtSecond.difference(dtMillisecond)); |
| |
| dtMillisecond = new DateTime(year, month, day, hour, minute, 0, -1); |
| dtSecond = new DateTime(year, month, day, hour, minute, -1); |
| Expect.equals(const Duration(milliseconds: 999), |
| dtMillisecond.difference(dtSecond)); |
| } |
| |
| var dt1 = new DateTime(year, month, day, hour); |
| var dt2 = new DateTime(year, month, day, hour, 1); |
| |
| // Earlier: |
| int earlierDay = day; |
| int earlierHour = hour - 1; |
| if (earlierHour < 0) { |
| earlierHour = 23; |
| earlierDay--; |
| } |
| var dt3 = new DateTime(year, month, earlierDay, earlierHour, 59); |
| |
| var diff1 = dt2.difference(dt1).inMinutes; |
| var diff2 = dt1.difference(dt3).inMinutes; |
| |
| if (diff1 == 1 && diff2 == 1 && dt1.hour == hour && dt1.minute == 0) { |
| // Regular date-time. |
| continue; |
| } |
| |
| // At most one is at a distance of more than a minute. |
| Expect.isTrue(diff1 == 1 || diff2 == 1); |
| |
| if (diff2 < 0) { |
| // This happens, when we ask for invalid times. |
| // Suppose daylight-saving is at 2:00 and switches to 3:00. If we |
| // ask for 2:59, we get 3:59 (59 minutes after 2:00). |
| Expect.isFalse(dt3.day == earlierDay && dt3.hour == earlierHour); |
| // If that's the case, then removing one minute from dt1 should |
| // not yield a date-time with the earlier values, and it should |
| // be far away from dt3. |
| var dt4 = dt1.add(const Duration(minutes: -1)); |
| Expect.isFalse(dt4.day == earlierDay && dt4.hour == earlierHour); |
| Expect.isTrue(dt4.isBefore(dt1)); |
| Expect.isTrue(dt4.day < earlierDay || |
| (dt4.day == earlierDay && dt4.hour < earlierHour)); |
| continue; |
| } |
| |
| // They must be in the right order. |
| Expect.isTrue(dt1.isBefore(dt2)); |
| Expect.isTrue(dt3.isBefore(dt1)); |
| } |
| } |
| } |
| |
| void main() { |
| // The following code constructs a String with all timezones that are |
| // relevant for this test. |
| // This can be helpful for running tests in multiple timezones. |
| // Typically, one would write something like: |
| // for tz in <contents_of_string>; do TZ=$tz tools/test.py ...; done |
| var result = new StringBuffer(); |
| for (int i = 0; i < daylightSavingChanges.length; i += 2) { |
| if (i != 0) result.write(" "); |
| result.write(daylightSavingChanges[i][0]); |
| } |
| |
| runTests(); |
| } |