Commit dbdede01 authored by Jungshik Shin's avatar Jungshik Shin Committed by Commit Bot

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: 's avatarDaniel Ehrenberg <littledan@chromium.org>
Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52332}
parent ef01379e
......@@ -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);
......
......@@ -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);
......
......@@ -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);
......
......@@ -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 {}
};
......
......@@ -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);
}
......
......@@ -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.
......
......@@ -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;
......
......@@ -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 &&
......
......@@ -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
......
......@@ -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() {
......
......@@ -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_;
......
......@@ -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);
}
......
......@@ -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'
##############################################################################
......
// 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))
// 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))
// 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))
// 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());
// 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))
// 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))
// 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))
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment