Skip to content

Commit cc71de6

Browse files
committed
[GR-30232] Backport: Fixes of timezone-related inconsistencies.
PullRequest: js/2051
2 parents f242ab9 + 8224ffe commit cc71de6

File tree

11 files changed

+245
-159
lines changed

11 files changed

+245
-159
lines changed

3rd_party_licenses.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ defined by the Mozilla Public License, v. 2.0.
478478

479479
================================================================================
480480

481-
ICU4J 68.2
481+
ICU4J 69.1
482482

483483
COPYRIGHT AND PERMISSION NOTICE (ICU 58 and later)
484484

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ The main focus is on user-observable behavior of the engine.
1010
* Implemented experimental operator overloading support. Use the experimental option `--js.operator-overloading` to enable it and consult [the documentation](docs/user/OperatorOverloading.md).
1111
* Updated RegExp Match Indices proposal with opt-in using the `d` flag. Available in ECMAScript 2022 (`--js.ecmascript-version=2022`). Deprecated `--js.regexp-match-indices` option.
1212
* Nashorn compatibility mode now defaults to compatiblity with ECMAScript version 5, unless another version is explicitly selected.
13+
* `Date.prototype` built-ins use ICU (not JDK) algorithms and data (like timezone data) by now (in order to reduce inconsistencies between `Date` and `Intl.DateTimeFormat`).
14+
* Updated ICU4J library to version 69.1.
1315

1416
## Version 21.1.0
1517
* Updated Node.js to version 14.16.1.

graal-js/mx.graal-js/suite.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,12 @@
6060

6161
"ICU4J" : {
6262
"moduleName" : "com.ibm.icu",
63-
"sha1" : "76893e6000401ace133a65262254be0ebe556d46",
64-
"sourceSha1" : "fae7d45fe56f4ef2a35ef6bd30245052268ffda1",
63+
"sha1" : "ff666ac55986650893aacb9e2e0003538e9799c0",
64+
"sourceSha1" : "3e19ca5465fce86a094c24df0b6c9256e53c8885",
6565
"maven" : {
6666
"groupId" : "com.ibm.icu",
6767
"artifactId" : "icu4j",
68-
"version" : "68.2",
68+
"version" : "69.1",
6969
},
7070
},
7171

graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/builtins/DatePrototypeBuiltins.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -399,7 +399,7 @@ protected String doOperation(Object thisDate) {
399399
if (isNaN.profile(Double.isNaN(t))) {
400400
return JSDate.INVALID_DATE_STRING;
401401
}
402-
return JSDate.formatUTC(JSDate.getJSDateUTCFormat(), t);
402+
return JSDate.format(getContext().getRealm().getJSDateUTCFormat(), t);
403403
} else {
404404
return JSDate.toString(t, getContext().getRealm());
405405
}
@@ -438,7 +438,7 @@ protected String doOperation(Object thisDate) {
438438
if (isNaN.profile(Double.isNaN(t))) {
439439
return JSDate.INVALID_DATE_STRING;
440440
}
441-
return JSDate.formatLocal(JSDate.getJSShortDateFormat(), t, getContext().getRealm());
441+
return JSDate.format(getContext().getRealm().getJSShortDateFormat(), t);
442442
}
443443
}
444444

@@ -454,7 +454,7 @@ protected String doOperation(Object thisDate) {
454454
if (isNaN.profile(Double.isNaN(t))) {
455455
return JSDate.INVALID_DATE_STRING;
456456
}
457-
return JSDate.formatLocal(JSDate.getJSShortTimeFormat(), t, getContext().getRealm());
457+
return JSDate.format(getContext().getRealm().getJSShortTimeFormat(), t);
458458
}
459459
}
460460

@@ -470,7 +470,7 @@ protected String doOperation(Object thisDate) {
470470
if (isNaN.profile(Double.isNaN(t))) {
471471
return JSDate.INVALID_DATE_STRING;
472472
}
473-
return JSDate.formatLocal(JSDate.getJSShortDateLocalFormat(), t, getContext().getRealm());
473+
return JSDate.format(getContext().getRealm().getJSShortDateLocalFormat(), t);
474474
}
475475
}
476476

@@ -506,7 +506,7 @@ protected String doOperation(Object thisDate) {
506506
if (isNaN.profile(Double.isNaN(t))) {
507507
return JSDate.INVALID_DATE_STRING;
508508
}
509-
return JSDate.formatLocal(JSDate.getJSShortTimeLocalFormat(), t, getContext().getRealm());
509+
return JSDate.format(getContext().getRealm().getJSShortTimeLocalFormat(), t);
510510
}
511511
}
512512

@@ -540,7 +540,7 @@ public JSDateToISOStringNode(JSContext context, JSBuiltin builtin) {
540540
protected String doOperation(Object thisDate) {
541541
double t = asDateMillis(thisDate);
542542
checkTimeValid(t);
543-
return JSDate.toISOStringIntl(t);
543+
return JSDate.toISOStringIntl(t, getContext().getRealm());
544544
}
545545
}
546546

graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/nodes/intl/InitializeDateTimeFormatNode.java

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -40,11 +40,9 @@
4040
*/
4141
package com.oracle.truffle.js.nodes.intl;
4242

43-
import java.time.ZoneId;
4443
import java.util.MissingResourceException;
4544

4645
import com.ibm.icu.util.TimeZone;
47-
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
4846
import com.oracle.truffle.api.dsl.Specialization;
4947
import com.oracle.truffle.api.object.DynamicObject;
5048
import com.oracle.truffle.api.profiles.BranchProfile;
@@ -204,27 +202,10 @@ private TimeZone toTimeZone(Object timeZoneValue) {
204202
errorBranch.enter();
205203
throw Errors.createRangeErrorInvalidTimeZone(name);
206204
}
205+
return IntlUtil.getICUTimeZone(tzId);
207206
} else {
208-
tzId = toICUTimeZoneId(context.getRealm().getLocalTimeZoneId());
207+
return context.getRealm().getLocalTimeZone();
209208
}
210-
return getICUTimeZone(tzId);
211209
}
212210

213-
@TruffleBoundary
214-
private static TimeZone getICUTimeZone(String tzId) {
215-
assert tzId != null;
216-
return TimeZone.getTimeZone(tzId);
217-
}
218-
219-
@TruffleBoundary
220-
private static String toICUTimeZoneId(ZoneId zoneId) {
221-
String tzid = zoneId.getId();
222-
char c = tzid.charAt(0);
223-
if (c == '+' || c == '-') {
224-
tzid = "GMT" + tzid;
225-
} else if (c == 'Z' && tzid.length() == 1) {
226-
tzid = "UTC";
227-
}
228-
return tzid;
229-
}
230211
}

graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/runtime/JSRealm.java

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,10 @@
4848
import java.time.DateTimeException;
4949
import java.time.ZoneId;
5050
import java.util.ArrayList;
51+
import java.util.Date;
5152
import java.util.HashMap;
5253
import java.util.List;
54+
import java.util.Locale;
5355
import java.util.Map;
5456
import java.util.Objects;
5557
import java.util.SplittableRandom;
@@ -59,6 +61,14 @@
5961
import org.graalvm.home.HomeFinder;
6062
import org.graalvm.options.OptionValues;
6163

64+
import com.ibm.icu.text.DateFormat;
65+
import com.ibm.icu.text.SimpleDateFormat;
66+
import com.ibm.icu.text.TimeZoneFormat;
67+
import com.ibm.icu.text.TimeZoneNames;
68+
import com.ibm.icu.util.Calendar;
69+
import com.ibm.icu.util.GregorianCalendar;
70+
import com.ibm.icu.util.TimeZone;
71+
import com.ibm.icu.util.ULocale;
6272
import com.oracle.truffle.api.CompilerAsserts;
6373
import com.oracle.truffle.api.CompilerDirectives;
6474
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
@@ -168,6 +178,7 @@
168178
import com.oracle.truffle.js.runtime.objects.PropertyDescriptor;
169179
import com.oracle.truffle.js.runtime.objects.PropertyProxy;
170180
import com.oracle.truffle.js.runtime.objects.Undefined;
181+
import com.oracle.truffle.js.runtime.util.IntlUtil;
171182
import com.oracle.truffle.js.runtime.util.PrintWriterWrapper;
172183
import com.oracle.truffle.js.runtime.util.SimpleArrayList;
173184
import com.oracle.truffle.js.runtime.util.TRegexUtil;
@@ -382,6 +393,17 @@ public class JSRealm {
382393
* Local time zone ID. Initialized lazily.
383394
*/
384395
@CompilationFinal private ZoneId localTimeZoneId;
396+
@CompilationFinal private TimeZone localTimeZone;
397+
398+
@CompilationFinal private DateFormat jsDateFormat;
399+
@CompilationFinal private DateFormat jsDateFormatBeforeYear0;
400+
@CompilationFinal private DateFormat jsDateFormatAfterYear9999;
401+
@CompilationFinal private DateFormat jsDateFormatISO;
402+
@CompilationFinal private DateFormat jsShortDateFormat;
403+
@CompilationFinal private DateFormat jsShortDateLocalFormat;
404+
@CompilationFinal private DateFormat jsShortTimeFormat;
405+
@CompilationFinal private DateFormat jsShortTimeLocalFormat;
406+
@CompilationFinal private DateFormat jsDateToStringFormat;
385407

386408
public static final long NANOSECONDS_PER_MILLISECOND = 1000000;
387409
private SplittableRandom random;
@@ -2257,6 +2279,17 @@ public void setAgent(JSAgent newAgent) {
22572279
this.agent = newAgent;
22582280
}
22592281

2282+
public TimeZone getLocalTimeZone() {
2283+
TimeZone timeZone = localTimeZone;
2284+
if (CompilerDirectives.injectBranchProbability(CompilerDirectives.SLOWPATH_PROBABILITY, timeZone == null)) {
2285+
if (CompilerDirectives.isPartialEvaluationConstant(timeZone)) {
2286+
CompilerDirectives.transferToInterpreterAndInvalidate();
2287+
}
2288+
timeZone = IntlUtil.getICUTimeZone(getLocalTimeZoneId());
2289+
}
2290+
return timeZone;
2291+
}
2292+
22602293
public ZoneId getLocalTimeZoneId() {
22612294
ZoneId id = localTimeZoneId;
22622295
if (CompilerDirectives.injectBranchProbability(CompilerDirectives.SLOWPATH_PROBABILITY, id == null)) {
@@ -2453,4 +2486,99 @@ public DynamicObject getForeignIterablePrototype() {
24532486
return foreignIterablePrototype;
24542487
}
24552488

2489+
public DateFormat getJSDateFormat(double time) {
2490+
long milliseconds = (long) time;
2491+
if (milliseconds < -62167219200000L) {
2492+
if (jsDateFormatBeforeYear0 == null) {
2493+
CompilerDirectives.transferToInterpreterAndInvalidate();
2494+
jsDateFormatBeforeYear0 = createDateFormat("uuuuuu-MM-dd'T'HH:mm:ss.SSS'Z'", false);
2495+
}
2496+
return jsDateFormatBeforeYear0;
2497+
} else if (milliseconds >= 253402300800000L) {
2498+
if (jsDateFormatAfterYear9999 == null) {
2499+
CompilerDirectives.transferToInterpreterAndInvalidate();
2500+
jsDateFormatAfterYear9999 = createDateFormat("+uuuuuu-MM-dd'T'HH:mm:ss.SSS'Z'", false);
2501+
}
2502+
return jsDateFormatAfterYear9999;
2503+
} else {
2504+
if (jsDateFormat == null) {
2505+
// UTC
2506+
CompilerDirectives.transferToInterpreterAndInvalidate();
2507+
jsDateFormat = createDateFormat("uuuu-MM-dd'T'HH:mm:ss.SSS'Z'", false);
2508+
}
2509+
return jsDateFormat;
2510+
}
2511+
}
2512+
2513+
public DateFormat getJSDateUTCFormat() {
2514+
if (jsDateFormatISO == null) {
2515+
CompilerDirectives.transferToInterpreterAndInvalidate();
2516+
jsDateFormatISO = createDateFormat("EEE, dd MMM uuuu HH:mm:ss 'GMT'", false);
2517+
}
2518+
return jsDateFormatISO;
2519+
}
2520+
2521+
public DateFormat getJSShortDateFormat() {
2522+
if (jsShortDateFormat == null) {
2523+
CompilerDirectives.transferToInterpreterAndInvalidate();
2524+
// no UTC
2525+
jsShortDateFormat = createDateFormat("EEE MMM dd uuuu", true);
2526+
}
2527+
return jsShortDateFormat;
2528+
}
2529+
2530+
public DateFormat getJSShortDateLocalFormat() {
2531+
if (jsShortDateLocalFormat == null) {
2532+
CompilerDirectives.transferToInterpreterAndInvalidate();
2533+
// no UTC
2534+
jsShortDateLocalFormat = createDateFormat("uuuu-MM-dd", true);
2535+
}
2536+
return jsShortDateLocalFormat;
2537+
}
2538+
2539+
public DateFormat getJSShortTimeFormat() {
2540+
if (jsShortTimeFormat == null) {
2541+
CompilerDirectives.transferToInterpreterAndInvalidate();
2542+
// no UTC
2543+
jsShortTimeFormat = createDateFormat("HH:mm:ss 'GMT'xx (z)", true);
2544+
}
2545+
return jsShortTimeFormat;
2546+
}
2547+
2548+
public DateFormat getJSShortTimeLocalFormat() {
2549+
if (jsShortTimeLocalFormat == null) {
2550+
CompilerDirectives.transferToInterpreterAndInvalidate();
2551+
// no UTC
2552+
jsShortTimeLocalFormat = createDateFormat("HH:mm:ss", true);
2553+
}
2554+
return jsShortTimeLocalFormat;
2555+
}
2556+
2557+
public DateFormat getDateToStringFormat() {
2558+
if (jsDateToStringFormat == null) {
2559+
CompilerDirectives.transferToInterpreterAndInvalidate();
2560+
jsDateToStringFormat = createDateFormat("EEE MMM dd uuuu HH:mm:ss 'GMT'xx (z)", true);
2561+
}
2562+
return jsDateToStringFormat;
2563+
}
2564+
2565+
@TruffleBoundary
2566+
private DateFormat createDateFormat(String pattern, boolean local) {
2567+
SimpleDateFormat format = new SimpleDateFormat(pattern, Locale.US);
2568+
format.setTimeZone(local ? getLocalTimeZone() : TimeZone.GMT_ZONE);
2569+
2570+
TimeZoneFormat tzFormat = format.getTimeZoneFormat().cloneAsThawed();
2571+
tzFormat.setTimeZoneNames(TimeZoneNames.getTZDBInstance(ULocale.US));
2572+
format.setTimeZoneFormat(tzFormat);
2573+
2574+
Calendar calendar = format.getCalendar();
2575+
if (calendar instanceof GregorianCalendar) {
2576+
// Ensure that Gregorian calendar is used for all dates.
2577+
// GregorianCalendar used by SimpleDateFormat is using
2578+
// Julian calendar for dates before 1582 otherwise.
2579+
((GregorianCalendar) calendar).setGregorianChange(new Date(Long.MIN_VALUE));
2580+
}
2581+
return format;
2582+
}
2583+
24562584
}

0 commit comments

Comments
 (0)