mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 11:03:19 +00:00
Fix datetime for daylight-saving transitions.
Change-Id: I0ec35937a76ada32991b79bcdac57912731db489 Reviewed-on: https://dart-review.googlesource.com/14380 Reviewed-by: Florian Loitsch <floitsch@google.com>
This commit is contained in:
parent
9a0e962e67
commit
88f58757b1
|
@ -297,16 +297,28 @@ class DateTime {
|
|||
}
|
||||
|
||||
if (!isUtc) {
|
||||
// Note that we need to remove the local timezone adjustment before
|
||||
// asking for the correct zone offset.
|
||||
// Note that we can't literally follow the ECMAScript spec (which this
|
||||
// code is based on), because it leads to incorrect computations at
|
||||
// the DST transition points.
|
||||
//
|
||||
// See V8's comment here:
|
||||
// https://github.com/v8/v8/blob/089dd7d2447d6eaf57c8ba6d8f37957f3a269777/src/date.h#L118
|
||||
|
||||
// We need to remove the local timezone adjustment before asking for the
|
||||
// correct zone offset.
|
||||
int adjustment = _localTimeZoneAdjustmentInSeconds() *
|
||||
Duration.MICROSECONDS_PER_SECOND;
|
||||
// The adjustment is independent of the actual date and of the daylight
|
||||
// saving time. It is positive east of the Prime Meridian and negative
|
||||
// west of it, e.g. -28800 sec for America/Los_Angeles timezone.
|
||||
|
||||
// We remove one hour to ensure that we have the correct offset at
|
||||
// DST transitioning points. This is a temporary solution and only
|
||||
// correct in timezones that shift for exactly one hour.
|
||||
adjustment += Duration.MICROSECONDS_PER_HOUR;
|
||||
int zoneOffset =
|
||||
_timeZoneOffsetInSeconds(microsecondsSinceEpoch - adjustment);
|
||||
|
||||
// The zoneOffset depends on the actual date and reflects any daylight
|
||||
// saving time and/or historical deviation relative to UTC time.
|
||||
// It is positive east of the Prime Meridian and negative west of it,
|
||||
|
|
|
@ -419,6 +419,7 @@ iterable_to_set_test: RuntimeError # is-checks do not implement strong mode type
|
|||
regexp/no-extensions_test: RuntimeError
|
||||
regexp/lookahead_test: RuntimeError
|
||||
regexp/overflow_test: RuntimeError
|
||||
date_time11_test: RuntimeError, OK # Bug in Safari.
|
||||
|
||||
[ $runtime == safari || $runtime == safarimobilesim ]
|
||||
string_trimlr_test/02: RuntimeError # Uses Unicode 6.2.0 or earlier.
|
||||
|
|
138
tests/corelib_2/date_time11_test.dart
Normal file
138
tests/corelib_2/date_time11_test.dart
Normal file
|
@ -0,0 +1,138 @@
|
|||
// 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++) {
|
||||
int year = test[1];
|
||||
int month = test[2];
|
||||
int day = test[3];
|
||||
int hour = test[4];
|
||||
|
||||
int minute = i == 0 ? 0 : test[5];
|
||||
// Rather adjust the hours than keeping the minutes.
|
||||
hour += minute ~/ 60;
|
||||
minute = minute.remainder(60);
|
||||
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(List<String> args) {
|
||||
// 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();
|
||||
}
|
Loading…
Reference in a new issue