Reland "Implement a new spec for timezone offset calculation"

This is a reland of dbdede0101
after a webkit layout test (geolocation-api/timestamp.html) was
fixed by
https://chromium-review.googlesource.com/c/chromium/src/+/994343 .

Original change's description:
> Implement a new spec for timezone offset calculation
>
> https://github.com/tc39/ecma262/pull/778 was recently merged
> to Ecma 262.
>
> It changes the way to convert between "local time" and UTC in such
> a way that it'd work for all timezones whether or not there has
> been any change in the timezone offset of the standard time. For
> instance, Europe/Moscow and some parts of US state of Indiana have
> changed the standard (non-DST) timezone offset a few times. The
> previous spec assumes that the the standard timezone offset is
> constant, but the new spec take into account the offset change
> history.
>
> In addition, it specifies a new way to calculate the timezone
> offset during a timezone transition (either in and
> out of DST or timezone offset shift).
>
> During a negative transition (e.g.  fall backward / getting
> out of DST), repeated times are to be interpreted as if the
> offset before the transition is in effect.
>
> During a positive transition (e.g. spring forward / getting
> into DST), skipped times are to be treated similarly. That
> is, they are to be interpreted as if the offset before the
> transition is in effect.
>
> With icu-timezone-data, v8 is compliant to the new spec for the
> past and the future as well as now whether or not the standard
> timezone offset of a given timezone has changed over time
> (e.g. Europe/Moscow, Pacific/Apia). With icu-timezone-data,
> Australia/Lord_Howe (30 minute DST change) also works per spec.
>
> Without icu-timezone-data, it works only for timezones of which
> the standard timezone offset is the same as the current offset
> (e.g. most North American timezones other than parts of Indiana)
> and of which the DST shift is an hour. For instance, it doesn't work
> for Europe/Moscow in 2010 when the standard timezone offset was
> +4h because the current (2018) standard timezone offset is +3h. Neither
> does it for Lord Howe in Australia with the DST shift of 0.5 hr.
>
> This CL used to require one of the two ICU CLs below, but not
> any more.
>
>   https://chromium-review.googlesource.com/c/chromium/deps/icu/+/572652
>   https://chromium-review.googlesource.com/851265  (a proposed CL to the
>   upstream ICU).
>
> Bug: v8:3547,chromium:417640,v8:5714
> Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng
> Change-Id: Ib162295da5bee31b2390bd0918157014aebd3e33
> Reviewed-on: https://chromium-review.googlesource.com/572148
> Commit-Queue: Jungshik Shin <jshin@chromium.org>
> Reviewed-by: Daniel Ehrenberg <littledan@chromium.org>
> Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#52332}

Bug: v8:3547, chromium:417640, v8:5714
Change-Id: I47536c111143f75e3cfeecf5d9761c43a98a10f5
Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng;master.tryserver.blink:linux_trusty_blink_rel
Reviewed-on: https://chromium-review.googlesource.com/995971
Commit-Queue: Jungshik Shin <jshin@chromium.org>
Reviewed-by: Clemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52372}
This commit is contained in:
Jungshik Shin 2018-04-03 10:18:38 -07:00 committed by Commit Bot
parent 539a24432b
commit 1d3a87bd1c
21 changed files with 435 additions and 88 deletions

View File

@ -39,21 +39,21 @@ namespace base {
class AIXTimezoneCache : public PosixTimezoneCache {
const char* LocalTimezone(double time) override;
double LocalTimeOffset() override;
double LocalTimeOffset(double time_ms, bool is_utc) override;
~AIXTimezoneCache() override {}
};
const char* AIXTimezoneCache::LocalTimezone(double time) {
if (std::isnan(time)) return "";
time_t tv = static_cast<time_t>(floor(time / msPerSecond));
const char* AIXTimezoneCache::LocalTimezone(double time_ms) {
if (std::isnan(time_ms)) return "";
time_t tv = static_cast<time_t>(floor(time_ms / msPerSecond));
struct tm tm;
struct tm* t = localtime_r(&tv, &tm);
if (nullptr == t) return "";
return tzname[0]; // The location of the timezone string on AIX.
}
double AIXTimezoneCache::LocalTimeOffset() {
double AIXTimezoneCache::LocalTimeOffset(double time_ms, bool is_utc) {
// On AIX, struct tm does not contain a tm_gmtoff field.
time_t utc = time(nullptr);
DCHECK_NE(utc, -1);

View File

@ -66,7 +66,7 @@ uint8_t* RandomizedVirtualAlloc(size_t size, DWORD flags, DWORD protect,
class CygwinTimezoneCache : public PosixTimezoneCache {
const char* LocalTimezone(double time) override;
double LocalTimeOffset() override;
double LocalTimeOffset(double time_ms, bool is_utc) override;
~CygwinTimezoneCache() override {}
};
@ -80,7 +80,7 @@ const char* CygwinTimezoneCache::LocalTimezone(double time) {
return tzname[0]; // The location of the timezone string on Cygwin.
}
double CygwinTimezoneCache::LocalTimeOffset() {
double LocalTimeOffset(double time_ms, bool is_utc) {
// On Cygwin, struct tm does not contain a tm_gmtoff field.
time_t utc = time(nullptr);
DCHECK_NE(utc, -1);

View File

@ -18,7 +18,9 @@ const char* PosixDefaultTimezoneCache::LocalTimezone(double time) {
return t->tm_zone;
}
double PosixDefaultTimezoneCache::LocalTimeOffset() {
double PosixDefaultTimezoneCache::LocalTimeOffset(double time_ms, bool is_utc) {
// Preserve the old behavior for non-ICU implementation by ignoring both
// time_ms and is_utc.
time_t tv = time(nullptr);
struct tm tm;
struct tm* t = localtime_r(&tv, &tm);

View File

@ -13,7 +13,7 @@ namespace base {
class PosixDefaultTimezoneCache : public PosixTimezoneCache {
public:
const char* LocalTimezone(double time_ms) override;
double LocalTimeOffset() override;
double LocalTimeOffset(double time_ms, bool is_utc) override;
~PosixDefaultTimezoneCache() override {}
};

View File

@ -37,8 +37,7 @@ namespace base {
class SolarisTimezoneCache : public PosixTimezoneCache {
const char* LocalTimezone(double time) override;
double LocalTimeOffset() override;
double LocalTimeOffset(double time, bool is_utc) override;
~SolarisTimezoneCache() override {}
};
@ -51,7 +50,7 @@ const char* SolarisTimezoneCache::LocalTimezone(double time) {
return tzname[0]; // The location of the timezone string on Solaris.
}
double SolarisTimezoneCache::LocalTimeOffset() {
double SolarisTimezoneCache::LocalTimeOffset(double time, bool is_utc) {
tzset();
return -static_cast<double>(timezone * msPerSecond);
}

View File

@ -115,7 +115,7 @@ class WindowsTimezoneCache : public TimezoneCache {
const char* LocalTimezone(double time) override;
double LocalTimeOffset() override;
double LocalTimeOffset(double time, bool is_utc) override;
double DaylightSavingsOffset(double time) override;
@ -466,7 +466,9 @@ const char* WindowsTimezoneCache::LocalTimezone(double time) {
// Returns the local time offset in milliseconds east of UTC without
// taking daylight savings time into account.
double WindowsTimezoneCache::LocalTimeOffset() {
double WindowsTimezoneCache::LocalTimeOffset(double time_ms, bool is_utc) {
// Ignore is_utc and time_ms for now. That way, the behavior wouldn't
// change with icu_timezone_data disabled.
// Use current time, rounded to the millisecond.
Win32Time t(OS::TimeCurrentMillis());
// Time::LocalOffset inlcudes any daylight savings offset, so subtract it.

View File

@ -20,10 +20,8 @@ class TimezoneCache {
// ES #sec-local-time-zone-adjustment
// Local Time Zone Adjustment
//
// TODO(littledan): Make more accurate with another parameter along the
// lines of this spec change:
// https://github.com/tc39/ecma262/pull/778
virtual double LocalTimeOffset() = 0;
virtual double LocalTimeOffset(double time_ms, bool is_utc) = 0;
// Called when the local timezone changes
virtual void Clear() = 0;

View File

@ -52,8 +52,14 @@ void DateCache::ResetDateCache() {
dst_usage_counter_ = 0;
before_ = &dst_[0];
after_ = &dst_[1];
local_offset_ms_ = kInvalidLocalOffsetInMs;
ymd_valid_ = false;
#ifdef V8_INTL_SUPPORT
if (!FLAG_icu_timezone_data) {
#endif
local_offset_ms_ = kInvalidLocalOffsetInMs;
#ifdef V8_INTL_SUPPORT
}
#endif
tz_cache_->Clear();
tz_name_ = nullptr;
dst_tz_name_ = nullptr;
@ -206,6 +212,70 @@ void DateCache::BreakDownTime(int64_t time_ms, int* year, int* month, int* day,
*ms = time_in_day_ms % 1000;
}
// Implements LocalTimeZonedjustment(t, isUTC)
// ECMA 262 - ES#sec-local-time-zone-adjustment
int DateCache::GetLocalOffsetFromOS(int64_t time_ms, bool is_utc) {
double offset;
#ifdef V8_INTL_SUPPORT
if (FLAG_icu_timezone_data) {
offset = tz_cache_->LocalTimeOffset(static_cast<double>(time_ms), is_utc);
} else {
#endif
// When ICU timezone data is not used, we need to compute the timezone
// offset for a given local time.
//
// The following shows that using DST for (t - LocalTZA - hour) produces
// correct conversion where LocalTZA is the timezone offset in winter (no
// DST) and the timezone offset is assumed to have no historical change.
// Note that it does not work for the past and the future if LocalTZA (no
// DST) is different from the current LocalTZA (no DST). For instance,
// this will break for Europe/Moscow in 2012 ~ 2013 because LocalTZA was
// 4h instead of the current 3h (as of 2018).
//
// Consider transition to DST at local time L1.
// Let L0 = L1 - hour, L2 = L1 + hour,
// U1 = UTC time that corresponds to L1,
// U0 = U1 - hour.
// Transitioning to DST moves local clock one hour forward L1 => L2, so
// U0 = UTC time that corresponds to L0 = L0 - LocalTZA,
// U1 = UTC time that corresponds to L1 = L1 - LocalTZA,
// U1 = UTC time that corresponds to L2 = L2 - LocalTZA - hour.
// Note that DST(U0 - hour) = 0, DST(U0) = 0, DST(U1) = 1.
// U0 = L0 - LocalTZA - DST(L0 - LocalTZA - hour),
// U1 = L1 - LocalTZA - DST(L1 - LocalTZA - hour),
// U1 = L2 - LocalTZA - DST(L2 - LocalTZA - hour).
//
// Consider transition from DST at local time L1.
// Let L0 = L1 - hour,
// U1 = UTC time that corresponds to L1,
// U0 = U1 - hour, U2 = U1 + hour.
// Transitioning from DST moves local clock one hour back L1 => L0, so
// U0 = UTC time that corresponds to L0 (before transition)
// = L0 - LocalTZA - hour.
// U1 = UTC time that corresponds to L0 (after transition)
// = L0 - LocalTZA = L1 - LocalTZA - hour
// U2 = UTC time that corresponds to L1 = L1 - LocalTZA.
// Note that DST(U0) = 1, DST(U1) = 0, DST(U2) = 0.
// U0 = L0 - LocalTZA - DST(L0 - LocalTZA - hour) = L0 - LocalTZA - DST(U0).
// U2 = L1 - LocalTZA - DST(L1 - LocalTZA - hour) = L1 - LocalTZA - DST(U1).
// It is impossible to get U1 from local time.
if (local_offset_ms_ == kInvalidLocalOffsetInMs) {
// This gets the constant LocalTZA (arguments are ignored).
local_offset_ms_ =
tz_cache_->LocalTimeOffset(static_cast<double>(time_ms), is_utc);
}
offset = local_offset_ms_;
if (!is_utc) {
const int kMsPerHour = 3600 * 1000;
time_ms -= (offset + kMsPerHour);
}
offset += DaylightSavingsOffsetInMs(time_ms);
#ifdef V8_INTL_SUPPORT
}
#endif
DCHECK_LT(offset, kInvalidLocalOffsetInMs);
return static_cast<int>(offset);
}
void DateCache::ExtendTheAfterSegment(int time_sec, int offset_ms) {
if (after_->offset_ms == offset_ms &&

View File

@ -75,13 +75,9 @@ class DateCache {
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
// ECMA 262 - 15.9.1.7.
int LocalOffsetInMs() {
if (local_offset_ms_ == kInvalidLocalOffsetInMs) {
local_offset_ms_ = GetLocalOffsetFromOS();
}
return local_offset_ms_;
// ECMA 262 - ES#sec-local-time-zone-adjustment
int LocalOffsetInMs(int64_t time, bool is_utc) {
return GetLocalOffsetFromOS(time, is_utc);
}
@ -103,53 +99,16 @@ class DateCache {
return static_cast<int>((time_ms - local_ms) / kMsPerMin);
}
// ECMA 262 - 15.9.1.9
// LocalTime(t) = t + LocalTZA + DaylightSavingTA(t)
// ECMA 262 - ES#sec-localtime-t
// LocalTime(t) = t + LocalTZA(t, true)
int64_t ToLocal(int64_t time_ms) {
return time_ms + LocalOffsetInMs() + DaylightSavingsOffsetInMs(time_ms);
return time_ms + LocalOffsetInMs(time_ms, true);
}
// ECMA 262 - 15.9.1.9
// UTC(t) = t - LocalTZA - DaylightSavingTA(t - LocalTZA)
// ECMA 262 - ES#sec-utc-t
// UTC(t) = t - LocalTZA(t, false)
int64_t ToUTC(int64_t time_ms) {
// We need to compute UTC time that corresponds to the given local time.
// Literally following spec here leads to incorrect time computation at
// the points were we transition to and from DST.
//
// The following shows that using DST for (t - LocalTZA - hour) produces
// correct conversion.
//
// Consider transition to DST at local time L1.
// Let L0 = L1 - hour, L2 = L1 + hour,
// U1 = UTC time that corresponds to L1,
// U0 = U1 - hour.
// Transitioning to DST moves local clock one hour forward L1 => L2, so
// U0 = UTC time that corresponds to L0 = L0 - LocalTZA,
// U1 = UTC time that corresponds to L1 = L1 - LocalTZA,
// U1 = UTC time that corresponds to L2 = L2 - LocalTZA - hour.
// Note that DST(U0 - hour) = 0, DST(U0) = 0, DST(U1) = 1.
// U0 = L0 - LocalTZA - DST(L0 - LocalTZA - hour),
// U1 = L1 - LocalTZA - DST(L1 - LocalTZA - hour),
// U1 = L2 - LocalTZA - DST(L2 - LocalTZA - hour).
//
// Consider transition from DST at local time L1.
// Let L0 = L1 - hour,
// U1 = UTC time that corresponds to L1,
// U0 = U1 - hour, U2 = U1 + hour.
// Transitioning from DST moves local clock one hour back L1 => L0, so
// U0 = UTC time that corresponds to L0 (before transition)
// = L0 - LocalTZA - hour.
// U1 = UTC time that corresponds to L0 (after transition)
// = L0 - LocalTZA = L1 - LocalTZA - hour
// U2 = UTC time that corresponds to L1 = L1 - LocalTZA.
// Note that DST(U0) = 1, DST(U1) = 0, DST(U2) = 0.
// U0 = L0 - LocalTZA - DST(L0 - LocalTZA - hour) = L0 - LocalTZA - DST(U0).
// U2 = L1 - LocalTZA - DST(L1 - LocalTZA - hour) = L1 - LocalTZA - DST(U1).
// It is impossible to get U1 from local time.
const int kMsPerHour = 3600 * 1000;
time_ms -= LocalOffsetInMs();
return time_ms - DaylightSavingsOffsetInMs(time_ms - kMsPerHour);
return time_ms - LocalOffsetInMs(time_ms, false);
}
@ -208,11 +167,7 @@ class DateCache {
return static_cast<int>(tz_cache_->DaylightSavingsOffset(time_ms));
}
virtual int GetLocalOffsetFromOS() {
double offset = tz_cache_->LocalTimeOffset();
DCHECK_LT(offset, kInvalidLocalOffsetInMs);
return static_cast<int>(offset);
}
virtual int GetLocalOffsetFromOS(int64_t time_ms, bool is_utc);
private:
// The implementation relies on the fact that no time zones have

View File

@ -14,6 +14,7 @@
#include "src/isolate.h"
#include "src/objects-inl.h"
#include "src/string-case.h"
#include "unicode/basictz.h"
#include "unicode/calendar.h"
#include "unicode/gregocal.h"
#include "unicode/timezone.h"
@ -373,23 +374,41 @@ icu::TimeZone* ICUTimezoneCache::GetTimeZone() {
return timezone_;
}
bool ICUTimezoneCache::GetOffsets(double time_ms, int32_t* raw_offset,
int32_t* dst_offset) {
bool ICUTimezoneCache::GetOffsets(double time_ms, bool is_utc,
int32_t* raw_offset, int32_t* dst_offset) {
UErrorCode status = U_ZERO_ERROR;
GetTimeZone()->getOffset(time_ms, false, *raw_offset, *dst_offset, status);
// TODO(jshin): ICU TimeZone class handles skipped time differently from
// Ecma 262 (https://github.com/tc39/ecma262/pull/778) and icu::TimeZone
// class does not expose the necessary API. Fixing
// http://bugs.icu-project.org/trac/ticket/13268 would make it easy to
// implement the proposed spec change. A proposed fix for ICU is
// https://chromium-review.googlesource.com/851265 .
// In the meantime, use an internal (still public) API of icu::BasicTimeZone.
// Once it's accepted by the upstream, get rid of cast. Note that casting
// TimeZone to BasicTimeZone is safe because we know that icu::TimeZone used
// here is a BasicTimeZone.
if (is_utc) {
GetTimeZone()->getOffset(time_ms, false, *raw_offset, *dst_offset, status);
} else {
static_cast<const icu::BasicTimeZone*>(GetTimeZone())
->getOffsetFromLocal(time_ms, icu::BasicTimeZone::kFormer,
icu::BasicTimeZone::kFormer, *raw_offset,
*dst_offset, status);
}
return U_SUCCESS(status);
}
double ICUTimezoneCache::DaylightSavingsOffset(double time_ms) {
int32_t raw_offset, dst_offset;
if (!GetOffsets(time_ms, &raw_offset, &dst_offset)) return 0;
if (!GetOffsets(time_ms, true, &raw_offset, &dst_offset)) return 0;
return dst_offset;
}
double ICUTimezoneCache::LocalTimeOffset() {
double ICUTimezoneCache::LocalTimeOffset(double time_ms, bool is_utc) {
int32_t raw_offset, dst_offset;
if (!GetOffsets(icu::Calendar::getNow(), &raw_offset, &dst_offset)) return 0;
return raw_offset;
if (!GetOffsets(time_ms, is_utc, &raw_offset, &dst_offset)) return 0;
return raw_offset + dst_offset;
}
void ICUTimezoneCache::Clear() {

View File

@ -48,14 +48,15 @@ class ICUTimezoneCache : public base::TimezoneCache {
double DaylightSavingsOffset(double time_ms) override;
double LocalTimeOffset() override;
double LocalTimeOffset(double time_ms, bool is_utc) override;
void Clear() override;
private:
icu::TimeZone* GetTimeZone();
bool GetOffsets(double time_ms, int32_t* raw_offset, int32_t* dst_offset);
bool GetOffsets(double time_ms, bool is_utc, int32_t* raw_offset,
int32_t* dst_offset);
icu::TimeZone* timezone_;

View File

@ -53,9 +53,8 @@ class DateCacheMock: public DateCache {
return rule == nullptr ? 0 : rule->offset_sec * 1000;
}
virtual int GetLocalOffsetFromOS() {
return local_offset_;
virtual int GetLocalOffsetFromOS(int64_t time_sec, bool is_utc) {
return local_offset_ + GetDaylightSavingsOffsetFromOS(time_sec);
}
private:
@ -113,8 +112,7 @@ static void CheckDST(int64_t time) {
Isolate* isolate = CcTest::i_isolate();
DateCache* date_cache = isolate->date_cache();
int64_t actual = date_cache->ToLocal(time);
int64_t expected = time + date_cache->GetLocalOffsetFromOS() +
date_cache->GetDaylightSavingsOffsetFromOS(time / 1000);
int64_t expected = time + date_cache->GetLocalOffsetFromOS(time, true);
CHECK_EQ(actual, expected);
}

View File

@ -181,6 +181,11 @@
# noi18n cannot turn on ICU backend for Date
'icu-date-to-string': [PASS, ['no_i18n == True', SKIP]],
'icu-date-lord-howe': [PASS, ['no_i18n == True', SKIP]],
'tzoffset-transition-apia': [PASS, ['no_i18n == True', SKIP]],
'tzoffset-transition-lord-howe': [PASS, ['no_i18n == True', SKIP]],
'tzoffset-transition-moscow': [PASS, ['no_i18n == True', SKIP]],
'tzoffset-transition-new-york': [PASS, ['no_i18n == True', SKIP]],
'tzoffset-seoul': [PASS, ['no_i18n == True', SKIP]],
# TODO(bmeurer): Flaky timeouts (sometimes <1s, sometimes >3m).
'unicodelctest': [PASS, NO_VARIANTS],
@ -609,6 +614,13 @@
'icu-date-to-string': [SKIP],
'icu-date-lord-howe': [SKIP],
'regress/regress-6288': [SKIP],
'tzoffset-transition-apia': [SKIP],
'tzoffset-transition-lord-howe': [SKIP],
'tzoffset-transition-moscow': [SKIP],
'tzoffset-transition-new-york': [SKIP],
'tzoffset-transition-new-york-noi18n': [SKIP],
'tzoffset-seoul': [SKIP],
'tzoffset-seoul-noi18n': [SKIP],
}], # 'system == windows'
##############################################################################

View File

@ -0,0 +1,20 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --no-icu-timezone-data
// Environment Variables: TZ=Asia/Seoul
// Seoul has DST (UTC+10) in 1987 and 1988.
assertEquals(new Date(Date.UTC(1986, 5, 22, 3)),
new Date(1986, 5, 22, 12))
assertEquals(new Date(Date.UTC(1987, 5, 22, 2)),
new Date(1987, 5, 22, 12))
assertEquals(new Date(Date.UTC(1987, 11, 22, 3)),
new Date(1987, 11, 22, 12))
assertEquals(new Date(Date.UTC(1988, 5, 22, 2)),
new Date(1988, 5, 22, 12))
assertEquals(new Date(Date.UTC(1988, 11, 22, 3)),
new Date(1988, 11, 22, 12))
assertEquals(new Date(Date.UTC(1989, 5, 22, 3)),
new Date(1989, 5, 22, 12))

View File

@ -0,0 +1,20 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --icu-timezone-data
// Environment Variables: TZ=Asia/Seoul
// Seoul has DST (UTC+10) in 1987 and 1988.
assertEquals(new Date(Date.UTC(1986, 5, 22, 3)),
new Date(1986, 5, 22, 12))
assertEquals(new Date(Date.UTC(1987, 5, 22, 2)),
new Date(1987, 5, 22, 12))
assertEquals(new Date(Date.UTC(1987, 11, 22, 3)),
new Date(1987, 11, 22, 12))
assertEquals(new Date(Date.UTC(1988, 5, 22, 2)),
new Date(1988, 5, 22, 12))
assertEquals(new Date(Date.UTC(1988, 11, 22, 3)),
new Date(1988, 11, 22, 12))
assertEquals(new Date(Date.UTC(1989, 5, 22, 3)),
new Date(1989, 5, 22, 12))

View File

@ -0,0 +1,48 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --icu-timezone-data
// Environment Variables: TZ=Pacific/Apia
// https://www.timeanddate.com/time/zone/samoa/apia
// 2011-09-24T03:00 : UTC-11 => UTC-10
assertEquals(new Date(Date.UTC(2011, 8, 24, 13, 59)),
new Date(2011, 8, 24, 2, 59))
assertEquals(new Date(Date.UTC(2011, 8, 24, 14, 30)),
new Date(2011, 8, 24, 3, 30));
assertEquals(new Date(Date.UTC(2011, 8, 24, 14)),
new Date(2011, 8, 24, 4));
assertEquals(new Date(Date.UTC(2011, 8, 24, 14, 30)),
new Date(2011, 8, 24, 4, 30));
assertEquals((new Date(2011, 8, 24, 4, 30)).getTimezoneOffset(),
(new Date(2011, 8, 24, 3, 30)).getTimezoneOffset());
// 2011-12-30T00:00 : UTC-10 => UTC+14
// A whole day(2011-12-30; 24 hours) is skipped, but the skipped
// time is to be interpreted with an offset before the transition.
assertEquals(new Date(Date.UTC(2011, 11, 30, 9, 59)),
new Date(2011, 11, 29, 23, 59));
for (var h = 0; h < 24; ++h) {
assertEquals(new Date(Date.UTC(2011, 11, 30, h + 10)),
new Date(2011, 11, 30, h));
assertEquals(new Date(Date.UTC(2011, 11, 30, h + 10, 30)),
new Date(2011, 11, 30, h, 30));
assertEquals(new Date(Date.UTC(2011, 11, 30, h + 10)),
new Date(2011, 11, 31, h));
assertEquals(new Date(Date.UTC(2011, 11, 30, h + 10, 30)),
new Date(2011, 11, 31, h, 30));
}
assertEquals(new Date(Date.UTC(2011, 11, 31, 10)),
new Date(2012, 0, 1, 0));
// 2012-04-01T0400: UTC+14 => UTC+13
assertEquals(new Date(Date.UTC(2012, 2, 31, 13)),
new Date(2012, 3, 1, 3));
assertEquals(new Date(Date.UTC(2012, 2, 31, 13, 30)),
new Date(2012, 3, 1, 3, 30));
assertEquals(new Date(Date.UTC(2012, 2, 31, 13, 59)),
new Date(2012, 3, 1, 3, 59))
assertEquals(new Date(Date.UTC(2012, 2, 31, 15)),
new Date(2012, 3, 1, 4))

View File

@ -0,0 +1,35 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --icu-timezone-data
// Environment Variables: TZ=Australia/Lord_Howe
// 2017-04-02T02:00 : UTC+11 => UTC+1030
assertEquals(new Date(Date.UTC(2017, 3, 1, 14, 29)),
new Date(2017, 3, 2, 1, 29));
assertEquals(new Date(Date.UTC(2017, 3, 1, 14, 30)),
new Date(2017, 3, 2, 1, 30));
assertEquals(new Date(Date.UTC(2017, 3, 1, 14, 45)),
new Date(2017, 3, 2, 1, 45));
assertEquals(new Date(Date.UTC(2017, 3, 1, 14, 59)),
new Date(2017, 3, 2, 1, 59));
assertEquals(new Date(Date.UTC(2017, 3, 1, 15, 30)),
new Date(2017, 3, 2, 2));
assertEquals(new Date(Date.UTC(2017, 3, 1, 15, 31)),
new Date(2017, 3, 2, 2, 1));
// 2017-10-07T02:00 : UTC+1030 => UTC+11
assertEquals(new Date(Date.UTC(2017, 8, 30, 15, 29)),
new Date(2017, 9, 1, 1, 59))
assertEquals(new Date(Date.UTC(2017, 8, 30, 15, 30)),
new Date(2017, 9, 1, 2));
assertEquals(new Date(Date.UTC(2017, 8, 30, 15, 45)),
new Date(2017, 9, 1, 2, 15));
assertEquals(new Date(Date.UTC(2017, 8, 30, 15, 30)),
new Date(2017, 9, 1, 2, 30));
assertEquals(new Date(Date.UTC(2017, 8, 30, 15, 45)),
new Date(2017, 9, 1, 2, 45));
assertEquals((new Date(2017, 9, 1, 2, 45)).getTimezoneOffset(),
(new Date(2017, 9, 1, 2, 15)).getTimezoneOffset());

View File

@ -0,0 +1,85 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --icu-timezone-data
// Environment Variables: TZ=Europe/Moscow
// https://www.timeanddate.com/time/zone/russia/moscow
// 2010-03-28T02:00 : UTC+3 => UTC+4
assertEquals(new Date(Date.UTC(2010, 2, 27, 22, 59)),
new Date(2010, 2, 28, 1, 59));
assertEquals(new Date(Date.UTC(2010, 2, 27, 23)),
new Date(2010, 2, 28, 2));
assertEquals(new Date(Date.UTC(2010, 2, 27, 23, 30)),
new Date(2010, 2, 28, 2, 30));
assertEquals(new Date(Date.UTC(2010, 2, 27, 23)),
new Date(2010, 2, 28, 3));
assertEquals(new Date(Date.UTC(2010, 2, 27, 23, 30)),
new Date(2010, 2, 28, 3, 30));
assertEquals((new Date(2010, 2, 28, 3, 30)).getTimezoneOffset(),
(new Date(2010, 2, 28, 2, 30)).getTimezoneOffset());
// 2010-10-31T03:00 : UTC+4 => UTC+3
assertEquals(new Date(Date.UTC(2010, 9, 30, 21, 59)),
new Date(2010, 9, 31, 1, 59));
assertEquals(new Date(Date.UTC(2010, 9, 30, 22)),
new Date(2010, 9, 31, 2));
assertEquals(new Date(Date.UTC(2010, 9, 30, 22, 30)),
new Date(2010, 9, 31, 2, 30));
assertEquals(new Date(Date.UTC(2010, 9, 30, 22, 59)),
new Date(2010, 9, 31, 2, 59))
assertEquals(new Date(Date.UTC(2010, 9, 31, 0)),
new Date(2010, 9, 31, 3))
assertEquals(new Date(Date.UTC(2010, 9, 31, 0, 30)),
new Date(2010, 9, 31, 3, 30))
// 2011-03-27T02:00 : UTC+3 => UTC+4
assertEquals(new Date(Date.UTC(2011, 2, 26, 22, 59)),
new Date(2011, 2, 27, 1, 59))
assertEquals(new Date(Date.UTC(2011, 2, 26, 23)),
new Date(2011, 2, 27, 2));
assertEquals(new Date(Date.UTC(2011, 2, 26, 23, 30)),
new Date(2011, 2, 27, 2, 30));
assertEquals(new Date(Date.UTC(2011, 2, 26, 23)),
new Date(2011, 2, 27, 3));
assertEquals(new Date(Date.UTC(2011, 2, 26, 23, 30)),
new Date(2011, 2, 27, 3, 30));
assertEquals((new Date(2011, 2, 27, 3, 30)).getTimezoneOffset(),
(new Date(2011, 2, 27, 2, 30)).getTimezoneOffset());
// No daylight saving time in 2012, 2013: UTC+4 year-round
assertEquals(new Date(Date.UTC(2012, 5, 21, 0)),
new Date(2012, 5, 21, 4))
assertEquals(new Date(Date.UTC(2012, 11, 21, 0)),
new Date(2012, 11, 21, 4))
assertEquals(new Date(Date.UTC(2013, 5, 21, 0)),
new Date(2013, 5, 21, 4))
assertEquals(new Date(Date.UTC(2013, 11, 21, 0)),
new Date(2013, 11, 21, 4))
// 2014-10-26T0200: UTC+4 => UTC+3 (year-round)
assertEquals(new Date(Date.UTC(2014, 9, 25, 20, 59)),
new Date(2014, 9, 26, 0, 59));
assertEquals(new Date(Date.UTC(2014, 9, 25, 21)),
new Date(2014, 9, 26, 1));
assertEquals(new Date(Date.UTC(2014, 9, 25, 21, 30)),
new Date(2014, 9, 26, 1, 30));
assertEquals(new Date(Date.UTC(2014, 9, 25, 21, 59)),
new Date(2014, 9, 26, 1, 59))
assertEquals(new Date(Date.UTC(2014, 9, 25, 23)),
new Date(2014, 9, 26, 2))
assertEquals(new Date(Date.UTC(2014, 9, 25, 23, 1)),
new Date(2014, 9, 26, 2, 1))
assertEquals(new Date(Date.UTC(2014, 11, 21, 0)),
new Date(2014, 11, 21, 3))
assertEquals(new Date(Date.UTC(2015, 5, 21, 0)),
new Date(2015, 5, 21, 3))
assertEquals(new Date(Date.UTC(2015, 11, 21, 0)),
new Date(2015, 11, 21, 3))
assertEquals(new Date(Date.UTC(2016, 5, 21, 0)),
new Date(2016, 5, 21, 3))
assertEquals(new Date(Date.UTC(2015, 11, 21, 0)),
new Date(2015, 11, 21, 3))

View File

@ -0,0 +1,34 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --no-icu-timezone-data
// Environment Variables: TZ=America/New_York
// 2017-03-12T02:00 : UTC-5 => UTC-4
assertEquals(new Date(Date.UTC(2017, 2, 12, 6, 59)),
new Date(2017, 2, 12, 1, 59))
assertEquals(new Date(Date.UTC(2017, 2, 12, 7)),
new Date(2017, 2, 12, 2));
assertEquals(new Date(Date.UTC(2017, 2, 12, 7, 30)),
new Date(2017, 2, 12, 2, 30));
assertEquals(new Date(Date.UTC(2017, 2, 12, 7)),
new Date(2017, 2, 12, 3));
assertEquals(new Date(Date.UTC(2017, 2, 12, 7, 30)),
new Date(2017, 2, 12, 3, 30));
assertEquals((new Date(2017, 2, 12, 3, 30)).getTimezoneOffset(),
(new Date(2017, 2, 12, 2, 30)).getTimezoneOffset());
// 2017-11-05T02:00 : UTC-4 => UTC-5
assertEquals(new Date(Date.UTC(2017, 10, 5, 4, 59)),
new Date(2017, 10, 5, 0, 59));
assertEquals(new Date(Date.UTC(2017, 10, 5, 5)),
new Date(2017, 10, 5, 1));
assertEquals(new Date(Date.UTC(2017, 10, 5, 5, 30)),
new Date(2017, 10, 5, 1, 30));
assertEquals(new Date(Date.UTC(2017, 10, 5, 5, 59)),
new Date(2017, 10, 5, 1, 59));
assertEquals(new Date(Date.UTC(2017, 10, 5, 7)),
new Date(2017, 10, 5, 2))
assertEquals(new Date(Date.UTC(2017, 10, 5, 8)),
new Date(2017, 10, 5, 3))

View File

@ -0,0 +1,34 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --icu-timezone-data
// Environment Variables: TZ=America/New_York
// 2017-03-12T02:00 : UTC-5 => UTC-4
assertEquals(new Date(Date.UTC(2017, 2, 12, 6, 59)),
new Date(2017, 2, 12, 1, 59))
assertEquals(new Date(Date.UTC(2017, 2, 12, 7)),
new Date(2017, 2, 12, 2));
assertEquals(new Date(Date.UTC(2017, 2, 12, 7, 30)),
new Date(2017, 2, 12, 2, 30));
assertEquals(new Date(Date.UTC(2017, 2, 12, 7)),
new Date(2017, 2, 12, 3));
assertEquals(new Date(Date.UTC(2017, 2, 12, 7, 30)),
new Date(2017, 2, 12, 3, 30));
assertEquals((new Date(2017, 2, 12, 3, 30)).getTimezoneOffset(),
(new Date(2017, 2, 12, 2, 30)).getTimezoneOffset());
// 2017-11-05T02:00 : UTC-4 => UTC-5
assertEquals(new Date(Date.UTC(2017, 10, 5, 4, 59)),
new Date(2017, 10, 5, 0, 59));
assertEquals(new Date(Date.UTC(2017, 10, 5, 5)),
new Date(2017, 10, 5, 1));
assertEquals(new Date(Date.UTC(2017, 10, 5, 5, 30)),
new Date(2017, 10, 5, 1, 30));
assertEquals(new Date(Date.UTC(2017, 10, 5, 5, 59)),
new Date(2017, 10, 5, 1, 59));
assertEquals(new Date(Date.UTC(2017, 10, 5, 7)),
new Date(2017, 10, 5, 2))
assertEquals(new Date(Date.UTC(2017, 10, 5, 8)),
new Date(2017, 10, 5, 3))

View File

@ -225,7 +225,22 @@
# 1050186: Arm/MIPS vm is broken; probably unrelated to dates
'ecma/Array/15.4.4.5-3': [PASS, ['arch == arm or arch == mipsel or arch == mips', FAIL]],
'ecma/Date/15.9.5.22-2': [PASS, ['arch == arm or arch == mipsel or arch == mips', FAIL]],
# These 4 tests made an incorrect assumption that the timezone offset of any
# given timezone has not changed over time. With icu-timezone-data enabled
# and https://github.com/tc39/ecma262/pull/778 implemented, the historical
# timezone offset is handled properly and these tests are broken in i18n
# build. It's non-trivial to fix them because they strive to work in *any*
# timezone.
# TODO(jshin): Add equivalent tests with a specific timezone using TZ variable
# on Linux to mjsunit.
'ecma/Date/15.9.5.16': [PASS, ['no_i18n == False', FAIL]],
'ecma/Date/15.9.5.18': [PASS, ['no_i18n == False', FAIL]],
'ecma/Date/15.9.5.22-1': [PASS, ['no_i18n == False', FAIL]],
# 1050186: Arm/MIPS vm is broken; probably unrelated to dates
'ecma/Date/15.9.5.22-2': [PASS, ['no_i18n == False or arch == arm or arch == mipsel or arch == mips', FAIL]],
# Flaky test that fails due to what appears to be a bug in the test.
# Occurs depending on current time