|
48 | 48 | import java.time.DateTimeException; |
49 | 49 | import java.time.ZoneId; |
50 | 50 | import java.util.ArrayList; |
| 51 | +import java.util.Date; |
51 | 52 | import java.util.HashMap; |
52 | 53 | import java.util.List; |
| 54 | +import java.util.Locale; |
53 | 55 | import java.util.Map; |
54 | 56 | import java.util.Objects; |
55 | 57 | import java.util.SplittableRandom; |
|
59 | 61 | import org.graalvm.home.HomeFinder; |
60 | 62 | import org.graalvm.options.OptionValues; |
61 | 63 |
|
| 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; |
62 | 72 | import com.oracle.truffle.api.CompilerAsserts; |
63 | 73 | import com.oracle.truffle.api.CompilerDirectives; |
64 | 74 | import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; |
|
168 | 178 | import com.oracle.truffle.js.runtime.objects.PropertyDescriptor; |
169 | 179 | import com.oracle.truffle.js.runtime.objects.PropertyProxy; |
170 | 180 | import com.oracle.truffle.js.runtime.objects.Undefined; |
| 181 | +import com.oracle.truffle.js.runtime.util.IntlUtil; |
171 | 182 | import com.oracle.truffle.js.runtime.util.PrintWriterWrapper; |
172 | 183 | import com.oracle.truffle.js.runtime.util.SimpleArrayList; |
173 | 184 | import com.oracle.truffle.js.runtime.util.TRegexUtil; |
@@ -382,6 +393,17 @@ public class JSRealm { |
382 | 393 | * Local time zone ID. Initialized lazily. |
383 | 394 | */ |
384 | 395 | @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; |
385 | 407 |
|
386 | 408 | public static final long NANOSECONDS_PER_MILLISECOND = 1000000; |
387 | 409 | private SplittableRandom random; |
@@ -2257,6 +2279,17 @@ public void setAgent(JSAgent newAgent) { |
2257 | 2279 | this.agent = newAgent; |
2258 | 2280 | } |
2259 | 2281 |
|
| 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 | + |
2260 | 2293 | public ZoneId getLocalTimeZoneId() { |
2261 | 2294 | ZoneId id = localTimeZoneId; |
2262 | 2295 | if (CompilerDirectives.injectBranchProbability(CompilerDirectives.SLOWPATH_PROBABILITY, id == null)) { |
@@ -2453,4 +2486,99 @@ public DynamicObject getForeignIterablePrototype() { |
2453 | 2486 | return foreignIterablePrototype; |
2454 | 2487 | } |
2455 | 2488 |
|
| 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 | + |
2456 | 2584 | } |
0 commit comments