From 3a4f05b7c4c6b638b4da81501b9188a747260a9b Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Sat, 15 Jun 2024 11:59:08 +0200 Subject: [PATCH] - use DateTimeFormatter instead of SimpleDateFormat - Date.prototype.toLocaleString now handles locales argument - make the formatter use Context.timezone instead of system default - ES6: use FormatStyle.SHORT for the formatter to make it behave more like real browsers when Intl.DateTimeFormat is not supported --- .../org/mozilla/javascript/NativeDate.java | 87 +++- .../javascript/tests/es6/NativeDateTest.java | 470 ++++++++++++++++-- 2 files changed, 495 insertions(+), 62 deletions(-) diff --git a/rhino-runtime/src/main/java/org/mozilla/javascript/NativeDate.java b/rhino-runtime/src/main/java/org/mozilla/javascript/NativeDate.java index c28b216e2d..e3eda03fea 100644 --- a/rhino-runtime/src/main/java/org/mozilla/javascript/NativeDate.java +++ b/rhino-runtime/src/main/java/org/mozilla/javascript/NativeDate.java @@ -6,9 +6,11 @@ package org.mozilla.javascript; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; +import java.util.*; /** * This class implements the Date native object. See ECMA 15.9. @@ -21,7 +23,6 @@ final class NativeDate extends IdScriptableObject { private static final long serialVersionUID = -8307438915861678966L; private static final Object DATE_TAG = "Date"; - private static final String js_NaN_date_str = "Invalid Date"; static void init(Scriptable scope, boolean sealed) { @@ -336,7 +337,7 @@ public Object execIdCall( case Id_toLocaleTimeString: case Id_toLocaleDateString: if (!Double.isNaN(t)) { - return toLocale_helper(t, id); + return toLocale_helper(cx, t, id, args); } return js_NaN_date_str; @@ -1348,10 +1349,8 @@ private static String date_format(Context cx, double t, int methodId) { t = MakeDate(day, TimeWithinDay(t)); } result.append(" ("); - Date date = new Date((long) t); - synchronized (timeZoneFormatter) { - result.append(timeZoneFormatter.format(date)); - } + final ZoneId zoneid = cx.getTimeZone().toZoneId(); + result.append(timeZoneFormatter.format(Instant.ofEpochMilli((long) t).atZone(zoneid))); result.append(')'); } return result.toString(); @@ -1399,25 +1398,57 @@ private static Object jsConstructor(Context cx, Object[] args) { return obj; } - private static String toLocale_helper(double t, int methodId) { - DateFormat formatter; + private static String toLocale_helper(Context cx, double t, int methodId, Object[] args) { + DateTimeFormatter formatter; switch (methodId) { case Id_toLocaleString: - formatter = localeDateTimeFormatter; + formatter = + cx.getLanguageVersion() >= Context.VERSION_ES6 + ? localeDateTimeFormatterES6 + : localeDateTimeFormatter; break; case Id_toLocaleTimeString: - formatter = localeTimeFormatter; + formatter = + cx.getLanguageVersion() >= Context.VERSION_ES6 + ? localeTimeFormatterES6 + : localeTimeFormatter; break; case Id_toLocaleDateString: - formatter = localeDateFormatter; + formatter = + cx.getLanguageVersion() >= Context.VERSION_ES6 + ? localeDateFormatterES6 + : localeDateFormatter; break; default: throw new AssertionError(); // unreachable } - synchronized (formatter) { - return formatter.format(new Date((long) t)); + final List languageTags = new ArrayList<>(); + if (args.length != 0) { + // we use the 'locales' argument but ignore the second 'options' argument as per spec of + // an + // implementation that has no Intl.DateTimeFormat support + if (args[0] instanceof NativeArray) { + final NativeArray array = (NativeArray) args[0]; + for (Object languageTag : array) { + languageTags.add(Context.toString(languageTag)); + } + } else { + languageTags.add(Context.toString(args[0])); + } } + + final List availableLocales = Arrays.asList(Locale.getAvailableLocales()); + for (String languageTag : languageTags) { + Locale locale = Locale.forLanguageTag(languageTag); + if (availableLocales.contains(locale)) { + formatter = formatter.withLocale(locale); + break; + } + } + + final ZoneId zoneid = cx.getTimeZone().toZoneId(); + return formatter.format(Instant.ofEpochMilli((long) t).atZone(zoneid)); } private static String js_toUTCString(double date) { @@ -1916,12 +1947,22 @@ protected int findPrototypeId(String s) { private static final int Id_toGMTString = Id_toUTCString; // Alias, see Ecma B.2.6 - // not thread safe - private static final DateFormat timeZoneFormatter = new SimpleDateFormat("zzz"); - private static final DateFormat localeDateTimeFormatter = - new SimpleDateFormat("MMMM d, yyyy h:mm:ss a z"); - private static final DateFormat localeDateFormatter = new SimpleDateFormat("MMMM d, yyyy"); - private static final DateFormat localeTimeFormatter = new SimpleDateFormat("h:mm:ss a z"); - + private static final DateTimeFormatter timeZoneFormatter = DateTimeFormatter.ofPattern("zzz"); + + private static final DateTimeFormatter localeDateTimeFormatter = + DateTimeFormatter.ofPattern("MMMM d, yyyy h:mm:ss a z"); + private static final DateTimeFormatter localeDateFormatter = + DateTimeFormatter.ofPattern("MMMM d, yyyy"); + private static final DateTimeFormatter localeTimeFormatter = + DateTimeFormatter.ofPattern("h:mm:ss a z"); + + // use FormatStyle.SHORT for these as per spec of an implementation that has no + // Intl.DateTimeFormat support + private static final DateTimeFormatter localeDateTimeFormatterES6 = + DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT); + private static final DateTimeFormatter localeDateFormatterES6 = + DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT); + private static final DateTimeFormatter localeTimeFormatterES6 = + DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT); private double date; } diff --git a/tests/src/test/java/org/mozilla/javascript/tests/es6/NativeDateTest.java b/tests/src/test/java/org/mozilla/javascript/tests/es6/NativeDateTest.java index f84266939a..5f3dc6029b 100644 --- a/tests/src/test/java/org/mozilla/javascript/tests/es6/NativeDateTest.java +++ b/tests/src/test/java/org/mozilla/javascript/tests/es6/NativeDateTest.java @@ -19,9 +19,120 @@ public class NativeDateTest { @Test - public void ctorDateTime() { - String js = "new Date('2021-12-18T22:23').toISOString()"; + public void ctorDateTimeStringGMT() { + ctorDateTimeString( + "2021-12-18T22:23:00.000Z", + "new Date('2021-12-18T22:23:00.000+00:00').toISOString()"); + ctorDateTimeString( + "2021-12-18T22:23:00.000Z", + "new Date('Sat, 18 Dec 2021 22:23:00 GMT').toISOString()"); + } + + @Test + public void ctorDateTimeStringUTC() { + ctorDateTimeString( + "2021-12-18T22:23:00.000Z", + "new Date('2021-12-18T22:23:00.000+00:00').toISOString()"); + ctorDateTimeString( + "2021-12-18T22:23:00.000Z", + "new Date('Sat, 18 Dec 2021 22:23:00 UTC').toISOString()"); + } + + @Test + public void ctorDateTimeStringUT() { + ctorDateTimeString( + "2021-12-18T22:23:00.000Z", + "new Date('Sat, 18 Dec 2021 22:23:00 UT').toISOString()"); + } + + @Test + public void ctorDateTimeStringEST() { + ctorDateTimeString( + "2021-12-18T12:23:00.000Z", + "new Date('2021-12-18T17:23:00.000+05:00').toISOString()"); + ctorDateTimeString( + "2021-12-19T03:23:00.000Z", + "new Date('Sat, 18 Dec 2021 22:23:00 EST').toISOString()"); + } + + @Test + public void ctorDateTimeStringEDT() { + ctorDateTimeString( + "2021-12-19T02:23:00.000Z", + "new Date('Sat, 18 Dec 2021 22:23:00 EDT').toISOString()"); + } + @Test + public void ctorDateTimeStringCST() { + ctorDateTimeString( + "2021-12-19T04:23:00.000Z", + "new Date('Sat, 18 Dec 2021 22:23:00 CST').toISOString()"); + } + + @Test + public void ctorDateTimeStringCDT() { + ctorDateTimeString( + "2021-12-19T03:23:00.000Z", + "new Date('Sat, 18 Dec 2021 22:23:00 CDT').toISOString()"); + } + + @Test + public void ctorDateTimeStringMST() { + ctorDateTimeString( + "2021-12-19T05:23:00.000Z", + "new Date('Sat, 18 Dec 2021 22:23:00 MST').toISOString()"); + } + + @Test + public void ctorDateTimeStringMDT() { + ctorDateTimeString( + "2021-12-19T04:23:00.000Z", + "new Date('Sat, 18 Dec 2021 22:23:00 MDT').toISOString()"); + } + + @Test + public void ctorDateTimeStringPST() { + ctorDateTimeString( + "2021-12-19T06:23:00.000Z", + "new Date('Sat, 18 Dec 2021 22:23:00 PST').toISOString()"); + } + + @Test + public void ctorDateTimeStringPDT() { + ctorDateTimeString( + "2021-12-19T05:23:00.000Z", + "new Date('Sat, 18 Dec 2021 22:23:00 PDT').toISOString()"); + } + + @Test + public void ctorDateTimeStringBerlin() { + ctorDateTimeString( + "2021-12-18T18:23:00.000Z", + "new Date('2021-12-18T17:23:00.000-01:00').toISOString()"); + } + + @Test + public void ctorDateTimeStringTokyo() { + ctorDateTimeString( + "2021-12-19T02:23:00.000Z", + "new Date('2021-12-18T17:23:00.000-09:00').toISOString()"); + } + + @Test + public void ctorDateTimeStringJST() { + ctorDateTimeString( + "2021-12-19T02:23:00.000Z", + "new Date('2021-12-18T17:23:00.000-09:00').toISOString()"); + } + + @Test + public void ctorDateTimeStringNewYork() { + ctorDateTimeString( + "2021-12-18T12:23:00.000Z", + "new Date('2021-12-18T17:23:00.000+05:00').toISOString()"); + } + + private static void ctorDateTimeString(final String expected, final String js) { Utils.runWithAllOptimizationLevels( cx -> { final Scriptable scope = cx.initStandardObjects(); @@ -29,14 +140,34 @@ public void ctorDateTime() { cx.setTimeZone(TimeZone.getTimeZone("GMT")); final Object res = cx.evaluateString(scope, js, "test.js", 0, null); - assertEquals("2021-12-18T22:23:00.000Z", res); + assertEquals(expected, res); return null; }); } + @Test + public void ctorDateTimeGMT() { + ctorDateTime("2021-12-18T22:23:00.000Z", "GMT"); + } + + @Test + public void ctorDateTimeUTC() { + ctorDateTime("2021-12-18T22:23:00.000Z", "UTC"); + } + + @Test + public void ctorDateTimeEST() { + ctorDateTime("2021-12-19T03:23:00.000Z", "EST"); + } + @Test public void ctorDateTimeBerlin() { - String js = "new Date('2021-12-18T22:23').toISOString()"; + ctorDateTime("2021-12-18T21:23:00.000Z", "Europe/Berlin"); + } + + @Test + public void ctorDateTimeBerlinDaylightSavingTime() { + String js = "new Date('2021-07-18T22:23').toISOString()"; Utils.runWithAllOptimizationLevels( cx -> { @@ -45,135 +176,396 @@ public void ctorDateTimeBerlin() { cx.setTimeZone(TimeZone.getTimeZone("Europe/Berlin")); final Object res = cx.evaluateString(scope, js, "test.js", 0, null); - assertEquals("2021-12-18T21:23:00.000Z", res); + assertEquals("2021-07-18T20:23:00.000Z", res); return null; }); } @Test - public void ctorDateTimeBerlinDaylightSavingTime() { - String js = "new Date('2021-07-18T22:23').toISOString()"; + public void ctorDateTimeTokyo() { + ctorDateTime("2021-12-18T13:23:00.000Z", "Asia/Tokyo"); + } + + @Test + public void ctorDateTimeJST() { + ctorDateTime("2021-12-18T13:23:00.000Z", "JST"); + } + @Test + public void ctorDateTimeNewYork() { + ctorDateTime("2021-12-19T03:23:00.000Z", "America/New_York"); + } + + private static void ctorDateTime(final String expected, final String tz) { + final String js = "new Date('2021-12-18T22:23').toISOString()"; Utils.runWithAllOptimizationLevels( cx -> { final Scriptable scope = cx.initStandardObjects(); cx.setLanguageVersion(Context.VERSION_ES6); - cx.setTimeZone(TimeZone.getTimeZone("Europe/Berlin")); + cx.setTimeZone(TimeZone.getTimeZone(tz)); final Object res = cx.evaluateString(scope, js, "test.js", 0, null); - assertEquals("2021-07-18T20:23:00.000Z", res); + assertEquals(expected, res); return null; }); } @Test - public void ctorDateTimeNewYork() { - String js = "new Date('2021-12-18T22:23').toISOString()"; + public void ctorDateGMT() { + ctorDate("2021-12-18T00:00:00.000Z", "GMT"); + } + + @Test + public void ctorDateUTC() { + ctorDate("2021-12-18T00:00:00.000Z", "UTC"); + } + + @Test + public void ctorDateEST() { + ctorDate("2021-12-18T00:00:00.000Z", "EST"); + } + + @Test + public void ctorDateBerlin() { + ctorDate("2021-12-18T00:00:00.000Z", "Europe/Berlin"); + } + + @Test + public void ctorDateNewYork() { + ctorDate("2021-12-18T00:00:00.000Z", "America/New_York"); + } + + @Test + public void ctorDateTokyo() { + ctorDate("2021-12-18T00:00:00.000Z", "Asia/Tokyo"); + } + @Test + public void ctorDateJST() { + ctorDate("2021-12-18T00:00:00.000Z", "JST"); + } + + private static void ctorDate(final String expected, final String tz) { + final String js = "new Date('2021-12-18').toISOString()"; Utils.runWithAllOptimizationLevels( cx -> { final Scriptable scope = cx.initStandardObjects(); cx.setLanguageVersion(Context.VERSION_ES6); - cx.setTimeZone(TimeZone.getTimeZone("America/New_York")); + cx.setTimeZone(TimeZone.getTimeZone(tz)); final Object res = cx.evaluateString(scope, js, "test.js", 0, null); - assertEquals("2021-12-19T03:23:00.000Z", res); + assertEquals(expected, res); return null; }); } @Test - public void ctorDate() { - String js = "new Date('2021-12-18').toISOString()"; + public void ctorInt() { + final String js = "new Date(951782399000).toISOString()"; + Utils.runWithAllOptimizationLevels( + cx -> { + final Scriptable scope = cx.initStandardObjects(); + final Object res = cx.evaluateString(scope, js, "test.js", 0, null); + assertEquals("2000-02-28T23:59:59.000Z", res); + return null; + }); + } + + @Test + public void ctorDouble() { + final String js = "new Date(208e10).toISOString()"; Utils.runWithAllOptimizationLevels( cx -> { final Scriptable scope = cx.initStandardObjects(); - cx.setLanguageVersion(Context.VERSION_ES6); - cx.setTimeZone(TimeZone.getTimeZone("GMT")); final Object res = cx.evaluateString(scope, js, "test.js", 0, null); - assertEquals("2021-12-18T00:00:00.000Z", res); + assertEquals("2035-11-30T01:46:40.000Z", res); return null; }); } @Test - public void ctorDateBerlin() { - String js = "new Date('2021-12-18').toISOString()"; + public void toLocaleEnUs() { + // real browser toLocale("12/18/2021, 10:23:00 PM", "new + // Date('2021-12-18T22:23').toLocaleString('en-US')"); + // toLocale("12/18/21 10:23 PM", "new Date('2021-12-18T22:23').toLocaleString('en-US')"); + toLocale("12/18/21, 10:23 PM", "new Date('2021-12-18T22:23').toLocaleString('en-US')"); + + // real browser toLocale("12/18/2021", "new + // Date('2021-12-18T22:23').toLocaleDateString('en-US')"); + toLocale("12/18/21", "new Date('2021-12-18T22:23').toLocaleDateString('en-US')"); + + // real browser toLocale("10:23:00 PM", "new + // Date('2021-12-18T22:23').toLocaleTimeString('en-US')"); + toLocale("10:23 PM", "new Date('2021-12-18T22:23').toLocaleTimeString('en-US')"); + } + + @Test + public void toLocaleDeDe() { + // real browser toLocale("18.12.2021, 22:23:00", "new + // Date('2021-12-18T22:23').toLocaleString('de-DE')"); + // toLocale("18.12.21 22:23", "new Date('2021-12-18T22:23').toLocaleString('de-DE')"); + toLocale("18.12.21, 22:23", "new Date('2021-12-18T22:23').toLocaleString('de-DE')"); + + // real browser toLocale("18.12.2021", "new + // Date('2021-12-18T22:23').toLocaleDateString('de-DE')"); + toLocale("18.12.21", "new Date('2021-12-18T22:23').toLocaleDateString('de-DE')"); + + // real browser toLocale("22:23:00", "new + // Date('2021-12-18T22:23').toLocaleTimeString('de-DE')"); + toLocale("22:23", "new Date('2021-12-18T22:23').toLocaleTimeString('de-DE')"); + } + + @Test + public void toLocaleJaJp() { + // real browser toLocale("2021/12/18 22:23:00", "new + // Date('2021-12-18T22:23').toLocaleString('ja-JP')"); + // toLocale("21/12/18 22:23", "new Date('2021-12-18T22:23').toLocaleString('ja-JP')"); + toLocale("2021/12/18 22:23", "new Date('2021-12-18T22:23').toLocaleString('ja-JP')"); + + // real browser toLocale("2021/12/18", "new + // Date('2021-12-18T22:23').toLocaleDateString('ja-JP')"); + // toLocale("21/12/18", "new Date('2021-12-18T22:23').toLocaleDateString('ja-JP')"); + toLocale("2021/12/18", "new Date('2021-12-18T22:23').toLocaleDateString('ja-JP')"); + + // real browser toLocale("22:23:00", "new + // Date('2021-12-18T22:23').toLocaleTimeString('ja-JP')"); + toLocale("22:23", "new Date('2021-12-18T22:23').toLocaleTimeString('ja-JP')"); + } + + @Test + public void toLocaleArray() { + // real browser toLocale("2021/12/18 22:23:00", "new + // Date('2021-12-18T22:23').toLocaleString(['foo', 'ja-JP', 'en-US'])"); + // toLocale("21/12/18 22:23", "new Date('2021-12-18T22:23').toLocaleString(['foo', 'ja-JP', + // 'en-US'])"); + toLocale( + "2021/12/18 22:23", + "new Date('2021-12-18T22:23').toLocaleString(['foo', 'ja-JP', 'en-US'])"); + + // real browser toLocale("2021/12/18", "new + // Date('2021-12-18T22:23').toLocaleDateString(['foo', 'ja-JP', 'en-US'])"); + // toLocale("21/12/18", "new Date('2021-12-18T22:23').toLocaleDateString(['foo', 'ja-JP', + // 'en-US'])"); + toLocale( + "2021/12/18", + "new Date('2021-12-18T22:23').toLocaleDateString(['foo', 'ja-JP', 'en-US'])"); + + // real browser toLocale("22:23:00", "new + // Date('2021-12-18T22:23').toLocaleTimeString(['foo', 'ja-JP', 'en-US'])"); + toLocale( + "22:23", + "new Date('2021-12-18T22:23').toLocaleTimeString(['foo', 'ja-JP', 'en-US'])"); + } + private static void toLocale(final String expected, final String js) { Utils.runWithAllOptimizationLevels( cx -> { final Scriptable scope = cx.initStandardObjects(); cx.setLanguageVersion(Context.VERSION_ES6); - cx.setTimeZone(TimeZone.getTimeZone("Europe/Berlin")); + cx.setTimeZone(TimeZone.getTimeZone("GMT")); final Object res = cx.evaluateString(scope, js, "test.js", 0, null); - assertEquals("2021-12-18T00:00:00.000Z", res); + assertEquals(expected, res); return null; }); } @Test - public void ctorDateNewYork() { - String js = "new Date('2021-12-18').toISOString()"; + public void toDateStringGMT() { + toDateString("Sat Dec 18 2021", "GMT"); + } + + @Test + public void toDateStringUTC() { + toDateString("Sat Dec 18 2021", "UTC"); + } + + @Test + public void toDateStringEST() { + toDateString("Sat Dec 18 2021", "EST"); + } + + @Test + public void toDateStringBerlin() { + toDateString("Sat Dec 18 2021", "Europe/Berlin"); + } + + @Test + public void toDateStringNewYork() { + toDateString("Sat Dec 18 2021", "America/New_York"); + } + + @Test + public void toDateStringTokyo() { + toDateString("Sun Dec 19 2021", "Asia/Tokyo"); + } + @Test + public void toDateStringJST() { + toDateString("Sun Dec 19 2021", "JST"); + } + + private static void toDateString(final String expected, final String tz) { + final String js = "new Date('Sat, 18 Dec 2021 22:23:00 UTC').toDateString()"; Utils.runWithAllOptimizationLevels( cx -> { final Scriptable scope = cx.initStandardObjects(); cx.setLanguageVersion(Context.VERSION_ES6); - cx.setTimeZone(TimeZone.getTimeZone("America/New_York")); + cx.setTimeZone(TimeZone.getTimeZone(tz)); final Object res = cx.evaluateString(scope, js, "test.js", 0, null); - assertEquals("2021-12-18T00:00:00.000Z", res); + assertEquals(expected, res); return null; }); } @Test - public void timezoneOffset() { - String js = "new Date(0).getTimezoneOffset()"; + public void toTimeStringGMT() { + toTimeString("22:23:00 GMT-0000 (GMT)", "GMT"); + } + + @Test + public void toTimeStringUTC() { + toTimeString("22:23:00 GMT-0000 (UTC)", "UTC"); + } + + @Test + public void toTimeStringEST() { + toTimeString("17:23:00 GMT-0500 (-05:00)", "EST"); + } + + @Test + public void toTimeStringBerlin() { + // real browser toTimeString("22:23:00 GMT+0100 (Central European Standard Time)", + // "Europe/Berlin"); + toTimeString("23:23:00 GMT+0100 (CET)", "Europe/Berlin"); + } + + @Test + public void toTimeStringNewYork() { + toTimeString("17:23:00 GMT-0500 (EST)", "America/New_York"); + } + + @Test + public void toTimeStringTokyo() { + toTimeString("07:23:00 GMT+0900 (JST)", "Asia/Tokyo"); + } + + @Test + public void toTimeStringJST() { + toTimeString("07:23:00 GMT+0900 (JST)", "JST"); + } + private static void toTimeString(final String expected, final String tz) { + final String js = "new Date('Sat, 18 Dec 2021 22:23:00 UTC').toTimeString()"; Utils.runWithAllOptimizationLevels( cx -> { final Scriptable scope = cx.initStandardObjects(); cx.setLanguageVersion(Context.VERSION_ES6); - cx.setTimeZone(TimeZone.getTimeZone("GMT")); + cx.setTimeZone(TimeZone.getTimeZone(tz)); - final Double res = (Double) cx.evaluateString(scope, js, "test.js", 0, null); - assertEquals(0, res.doubleValue(), 0.0001); + final Object res = cx.evaluateString(scope, js, "test.js", 0, null); + assertEquals(expected, res); return null; }); } @Test - public void timezoneOffsetBerlin() { - String js = "new Date(0).getTimezoneOffset()"; + public void toUTCStringGMT() { + toUTCString("Sat, 18 Dec 2021 22:23:00 GMT", "GMT"); + } + + @Test + public void toUTCStringUTC() { + toUTCString("Sat, 18 Dec 2021 22:23:00 GMT", "UTC"); + } + + @Test + public void toUTCStringEST() { + toUTCString("Sat, 18 Dec 2021 22:23:00 GMT", "EST"); + } + + @Test + public void toUTCStringBerlin() { + toUTCString("Sat, 18 Dec 2021 22:23:00 GMT", "Europe/Berlin"); + } + + @Test + public void toUTCStringNewYork() { + toUTCString("Sat, 18 Dec 2021 22:23:00 GMT", "America/New_York"); + } + + @Test + public void toUTCStringTokyo() { + toUTCString("Sat, 18 Dec 2021 22:23:00 GMT", "Asia/Tokyo"); + } + + @Test + public void toUTCStringJST() { + toUTCString("Sat, 18 Dec 2021 22:23:00 GMT", "JST"); + } + private static void toUTCString(final String expected, final String tz) { + final String js = "new Date('Sat, 18 Dec 2021 22:23:00 UTC').toUTCString()"; Utils.runWithAllOptimizationLevels( cx -> { final Scriptable scope = cx.initStandardObjects(); cx.setLanguageVersion(Context.VERSION_ES6); - cx.setTimeZone(TimeZone.getTimeZone("Europe/Berlin")); + cx.setTimeZone(TimeZone.getTimeZone(tz)); - final Double res = (Double) cx.evaluateString(scope, js, "test.js", 0, null); - assertEquals(-60, res.doubleValue(), 0.0001); + final Object res = cx.evaluateString(scope, js, "test.js", 0, null); + assertEquals(expected, res); return null; }); } + @Test + public void timezoneOffsetGMT() { + timezoneOffset(0, "GMT"); + } + + @Test + public void timezoneOffsetUTC() { + timezoneOffset(0, "UTC"); + } + + @Test + public void timezoneOffsetEST() { + timezoneOffset(300, "EST"); + } + + @Test + public void timezoneOffsetBerlin() { + timezoneOffset(-60, "Europe/Berlin"); + } + @Test public void timezoneOffsetNewYork() { - String js = "new Date(0).getTimezoneOffset()"; + timezoneOffset(300, "America/New_York"); + } + + @Test + public void timezoneOffsetTokyo() { + timezoneOffset(-540, "Asia/Tokyo"); + } + + @Test + public void timezoneOffsetJST() { + timezoneOffset(-540, "JST"); + } + private static void timezoneOffset(final int expected, final String tz) { + final String js = "new Date(0).getTimezoneOffset()"; Utils.runWithAllOptimizationLevels( cx -> { final Scriptable scope = cx.initStandardObjects(); cx.setLanguageVersion(Context.VERSION_ES6); - cx.setTimeZone(TimeZone.getTimeZone("America/New_York")); + cx.setTimeZone(TimeZone.getTimeZone(tz)); final Double res = (Double) cx.evaluateString(scope, js, "test.js", 0, null); - assertEquals(300, res.doubleValue(), 0.0001); + assertEquals(expected, res.doubleValue(), 0.0001); return null; }); }