Skip to content

Commit a91cf04

Browse files
committed
[GR-28112] Backport: Allow parsing of legacy dates without time-zone offset as being in local time.
PullRequest: js/1816
2 parents 7060e6c + 8b8137b commit a91cf04

File tree

4 files changed

+102
-2
lines changed

4 files changed

+102
-2
lines changed

graal-js/src/com.oracle.truffle.js.parser/src/com/oracle/truffle/js/parser/date/DateParser.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import java.util.HashMap;
5151
import java.util.Locale;
5252

53+
import com.oracle.truffle.js.runtime.JSContext;
5354
import com.oracle.truffle.js.runtime.JSRealm;
5455

5556
// @formatter:off
@@ -679,7 +680,8 @@ private boolean patchResult(final boolean strict) {
679680
if (isSet(HOUR) && !isSet(MINUTE)) {
680681
return false;
681682
}
682-
if (realm.getContext().isOptionV8CompatibilityMode()) {
683+
JSContext context = realm.getContext();
684+
if (context.isOptionV8CompatibilityMode()) {
683685
if (!isSet(YEAR) && !isSet(DAY) && !isSet(MONTH)) {
684686
return false;
685687
}
@@ -688,7 +690,7 @@ private boolean patchResult(final boolean strict) {
688690
// fill in default values for unset fields except timezone
689691
for (int field = YEAR; field <= TIMEZONE; field++) {
690692
if (get(field) == null) {
691-
if (field == TIMEZONE && !dateOnly) {
693+
if (field == TIMEZONE && !isUTCDefaultTimezone(context, dateOnly, strict)) {
692694
// When the UTC offset representation is absent,
693695
// date-only forms are interpreted as a UTC time and
694696
// date-time forms are interpreted as a local time (= empty TIMEZONE).
@@ -737,6 +739,10 @@ private boolean patchResult(final boolean strict) {
737739
return true;
738740
}
739741

742+
private static boolean isUTCDefaultTimezone(JSContext context, boolean dateOnly, boolean strict) {
743+
return (strict || context.getContextOptions().shouldUseUTCForLegacyDates()) && dateOnly;
744+
}
745+
740746
private static void addName(final String str, final int type, final int value) {
741747
final Name name = new Name(str, type, value);
742748
names.put(name.key, name);
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl.
6+
*/
7+
8+
/**
9+
* This test is an extension of
10+
* https://github.com/tc39/test262/blob/main/test/built-ins/Date/parse/without-utc-offset.js
11+
* This extension checks also the desired behaviour for legacy dates
12+
* (that are not covered by the specification).
13+
*/
14+
15+
load('assert.js');
16+
17+
const timezoneOffsetMS = new Date(0).getTimezoneOffset() * 60000;
18+
19+
function check(string, expected) {
20+
assertSame(expected, Date.parse(string));
21+
assertSame(expected, new Date(string).getTime());
22+
}
23+
24+
check('1970-01-01T00:00:00', timezoneOffsetMS);
25+
check('1970-01-01', 0);
26+
check('1970-01', 0);
27+
check('1970', 0);
28+
29+
// Legacy format
30+
check('1-1-1970', 0);
31+
check('1-1-1970 00:00:00', timezoneOffsetMS);
32+
check('1/1/1970', 0);
33+
check('1/1/1970 00:00:00', timezoneOffsetMS);
34+
check('1970-01-1', 0);
35+
check('1970-1-01', 0);
36+
check('1970-1-1', 0);
37+
check('1970-1', 0);
38+
39+
true;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl.
6+
*/
7+
8+
/**
9+
* This test is an extension of
10+
* https://github.com/tc39/test262/blob/main/test/built-ins/Date/parse/without-utc-offset.js
11+
* This extension checks also the desired behaviour for legacy dates
12+
* (that are not covered by the specification).
13+
*
14+
* @option use-utc-for-legacy-dates=false
15+
*/
16+
17+
load('assert.js');
18+
19+
const timezoneOffsetMS = new Date(0).getTimezoneOffset() * 60000;
20+
21+
function check(string, expected) {
22+
assertSame(expected, Date.parse(string));
23+
assertSame(expected, new Date(string).getTime());
24+
}
25+
26+
check('1970-01-01T00:00:00', timezoneOffsetMS);
27+
check('1970-01-01', 0);
28+
check('1970-01', 0);
29+
check('1970', 0);
30+
31+
// Legacy format
32+
check('1-1-1970', timezoneOffsetMS);
33+
check('1-1-1970 00:00:00', timezoneOffsetMS);
34+
check('1/1/1970', timezoneOffsetMS);
35+
check('1/1/1970 00:00:00', timezoneOffsetMS);
36+
check('1970-01-1', timezoneOffsetMS);
37+
check('1970-1-01', timezoneOffsetMS);
38+
check('1970-1-1', timezoneOffsetMS);
39+
check('1970-1', timezoneOffsetMS);
40+
41+
true;

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,11 @@ public Map<String, String> apply(String value) {
487487
protected static final OptionKey<Boolean> TOP_LEVEL_AWAIT = new OptionKey<>(false);
488488
@CompilationFinal private boolean topLevelAwait;
489489

490+
public static final String USE_UTC_FOR_LEGACY_DATES_NAME = JS_OPTION_PREFIX + "use-utc-for-legacy-dates";
491+
@Option(name = USE_UTC_FOR_LEGACY_DATES_NAME, category = OptionCategory.EXPERT, stability = OptionStability.STABLE, help = "Determines what time zone (UTC or local time zone) should be used when UTC offset is absent in a parsed date.") //
492+
public static final OptionKey<Boolean> USE_UTC_FOR_LEGACY_DATES = new OptionKey<>(true);
493+
@CompilationFinal private boolean useUTCForLegacyDates;
494+
490495
JSContextOptions(JSParserOptions parserOptions, OptionValues optionValues) {
491496
this.parserOptions = parserOptions;
492497
this.optionValues = optionValues;
@@ -571,6 +576,7 @@ private void cacheOptions() {
571576
this.maxPrototypeChainLength = readIntegerOption(MAX_PROTOTYPE_CHAIN_LENGTH);
572577
this.asyncStackTraces = readBooleanOption(ASYNC_STACK_TRACES);
573578
this.topLevelAwait = TOP_LEVEL_AWAIT.hasBeenSet(optionValues) ? readBooleanOption(TOP_LEVEL_AWAIT) : getEcmaScriptVersion() >= JSConfig.ECMAScript2022;
579+
this.useUTCForLegacyDates = USE_UTC_FOR_LEGACY_DATES.hasBeenSet(optionValues) ? readBooleanOption(USE_UTC_FOR_LEGACY_DATES) : !v8CompatibilityMode;
574580

575581
this.propertyCacheLimit = readIntegerOption(PROPERTY_CACHE_LIMIT);
576582
this.functionCacheLimit = readIntegerOption(FUNCTION_CACHE_LIMIT);
@@ -925,6 +931,10 @@ public boolean isAsyncStackTraces() {
925931
return asyncStackTraces;
926932
}
927933

934+
public boolean shouldUseUTCForLegacyDates() {
935+
return useUTCForLegacyDates;
936+
}
937+
928938
@Override
929939
public int hashCode() {
930940
int hash = 5;
@@ -973,6 +983,7 @@ public int hashCode() {
973983
hash = 53 * hash + this.propertyCacheLimit;
974984
hash = 53 * hash + this.functionCacheLimit;
975985
hash = 53 * hash + (this.topLevelAwait ? 1 : 0);
986+
hash = 53 * hash + (this.useUTCForLegacyDates ? 1 : 0);
976987
return hash;
977988
}
978989

@@ -1120,6 +1131,9 @@ public boolean equals(Object obj) {
11201131
if (this.topLevelAwait != other.topLevelAwait) {
11211132
return false;
11221133
}
1134+
if (this.useUTCForLegacyDates != other.useUTCForLegacyDates) {
1135+
return false;
1136+
}
11231137
return Objects.equals(this.parserOptions, other.parserOptions);
11241138
}
11251139
}

0 commit comments

Comments
 (0)