com.gargoylesoftware.htmlunit.javascript.host.intl.DateTimeFormat Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of htmlunit Show documentation
Show all versions of htmlunit Show documentation
A headless browser intended for use in testing web-based applications.
/*
* Copyright (c) 2002-2022 Gargoyle Software Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gargoylesoftware.htmlunit.javascript.host.intl;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_DATE_WITH_LEFT_TO_RIGHT_MARK;
import java.time.ZoneId;
import java.time.chrono.Chronology;
import java.time.chrono.HijrahChronology;
import java.time.chrono.JapaneseChronology;
import java.time.chrono.ThaiBuddhistChronology;
import java.time.format.DateTimeFormatter;
import java.time.format.DecimalStyle;
import java.time.temporal.TemporalAccessor;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.javascript.HtmlUnitScriptable;
import com.gargoylesoftware.htmlunit.javascript.configuration.JsxClass;
import com.gargoylesoftware.htmlunit.javascript.configuration.JsxConstructor;
import com.gargoylesoftware.htmlunit.javascript.configuration.JsxFunction;
import com.gargoylesoftware.htmlunit.javascript.host.Window;
import net.sourceforge.htmlunit.corejs.javascript.Context;
import net.sourceforge.htmlunit.corejs.javascript.Function;
import net.sourceforge.htmlunit.corejs.javascript.NativeArray;
import net.sourceforge.htmlunit.corejs.javascript.ScriptRuntime;
import net.sourceforge.htmlunit.corejs.javascript.Scriptable;
/**
* A JavaScript object for {@code DateTimeFormat}.
*
* @author Ahmed Ashour
* @author Ronald Brill
*/
@JsxClass
public class DateTimeFormat extends HtmlUnitScriptable {
private static final ConcurrentHashMap CHROME_FORMATS_ = new ConcurrentHashMap<>();
private static final ConcurrentHashMap EDGE_FORMATS_ = new ConcurrentHashMap<>();
private static final ConcurrentHashMap FF_FORMATS_ = new ConcurrentHashMap<>();
private static final ConcurrentHashMap FF_ESR_FORMATS_ = new ConcurrentHashMap<>();
private static final ConcurrentHashMap IE_FORMATS_ = new ConcurrentHashMap<>();
private transient DateTimeFormatHelper formatter_;
static {
final String ddSlash = "\u200Edd\u200E/\u200EMM\u200E/\u200EYYYY";
final String ddDash = "\u200Edd\u200E-\u200EMM\u200E-\u200EYYYY";
final String ddDot = "\u200Edd\u200E.\u200EMM\u200E.\u200EYYYY";
final String ddDotDot = "\u200Edd\u200E.\u200EMM\u200E.\u200EYYYY\u200E.";
final String ddDotBlank = "\u200Edd\u200E. \u200EMM\u200E. \u200EYYYY";
final String ddDotBlankDot = "\u200Edd\u200E. \u200EMM\u200E. \u200EYYYY.";
final String mmSlash = "\u200EMM\u200E/\u200Edd\u200E/\u200EYYYY";
final String yyyySlash = "\u200EYYYY\u200E/\u200EMM\u200E/\u200Edd";
final String yyyyDash = "\u200EYYYY\u200E-\u200EMM\u200E-\u200Edd";
final String yyyyDotBlankDot = "\u200EYYYY\u200E. \u200EMM\u200E. \u200Edd.";
final String yyyyDotBlankDotIE = "\u200EYYYY\u200E. \u200EMM\u200E. \u200Edd\u200E.";
final String yyyyDotDot = "\u200EYYYY\u200E.\u200EMM\u200E.\u200Edd\u200E.";
final String yyyyMinus = "\u200EYYYY\u200E-\u200EMM\u200E-\u200Edd";
final String rightToLeft = "\u200Fdd\u200F/\u200FMM\u200F/\u200FYYYY";
final Map commonFormats = new HashMap<>();
commonFormats.put("", mmSlash);
commonFormats.put("ar", "dd\u200F/MM\u200F/YYYY");
commonFormats.put("ar-SA", "d\u200F/M\u200F/YYYY هـ");
commonFormats.put("ban", mmSlash);
commonFormats.put("be", ddDot);
commonFormats.put("bg", ddDot + "\u200E \u0433.");
commonFormats.put("ca", ddSlash);
commonFormats.put("cs", ddDotBlank);
commonFormats.put("da", ddSlash);
commonFormats.put("de", ddDot);
commonFormats.put("el", ddSlash);
commonFormats.put("en-NZ", ddSlash);
commonFormats.put("en-PA", ddSlash);
commonFormats.put("en-PR", ddSlash);
commonFormats.put("en-AU", ddSlash);
commonFormats.put("en-GB", ddSlash);
commonFormats.put("en-IE", ddSlash);
commonFormats.put("en-IN", ddSlash);
commonFormats.put("en-MT", ddSlash);
commonFormats.put("en-SG", ddSlash);
commonFormats.put("en-ZA", yyyySlash);
commonFormats.put("es", ddSlash);
commonFormats.put("es-CL", ddDash);
commonFormats.put("es-PA", mmSlash);
commonFormats.put("es-PR", mmSlash);
commonFormats.put("es-US", mmSlash);
commonFormats.put("et", ddDot);
commonFormats.put("fi", ddDot);
commonFormats.put("fr", ddSlash);
commonFormats.put("fr-CA", yyyyDash);
commonFormats.put("ga", ddSlash);
commonFormats.put("hi", ddSlash);
commonFormats.put("hr", ddDotBlankDot);
commonFormats.put("hu", yyyyDotBlankDot);
commonFormats.put("id", ddSlash);
commonFormats.put("in", ddSlash);
commonFormats.put("is", ddDot);
commonFormats.put("it", ddSlash);
commonFormats.put("iw", ddDot);
commonFormats.put("ja", yyyySlash);
commonFormats.put("ja-JP-u-ca-japanese", "'H'yy/MM/dd");
commonFormats.put("ko", yyyyDotBlankDot);
commonFormats.put("lt", yyyyDash);
commonFormats.put("lv", yyyyDotDot);
commonFormats.put("mk", ddDot);
commonFormats.put("ms", ddSlash);
commonFormats.put("mt", mmSlash);
commonFormats.put("nl", ddDash);
commonFormats.put("pl", ddDot);
commonFormats.put("pt", ddSlash);
commonFormats.put("ro", ddDot);
commonFormats.put("ru", ddDot);
commonFormats.put("sk", ddDotBlank);
commonFormats.put("sl", ddDotBlank);
commonFormats.put("sq", ddDot);
commonFormats.put("sr", ddDotDot);
commonFormats.put("sv", yyyyDash);
commonFormats.put("th", ddSlash);
commonFormats.put("tr", ddDot);
commonFormats.put("uk", ddDot);
commonFormats.put("vi", ddSlash);
commonFormats.put("zh", yyyySlash);
commonFormats.put("zh-HK", ddSlash);
commonFormats.put("zh-SG", "\u200EYYYY\u200E\u5E74\u200EMM\u200E\u6708\u200Edd\u200E\u65E5");
CHROME_FORMATS_.putAll(commonFormats);
EDGE_FORMATS_.putAll(commonFormats);
IE_FORMATS_.putAll(commonFormats);
commonFormats.put("en-CA", yyyyDash);
commonFormats.put("en-PH", ddSlash);
commonFormats.put("es-US", ddSlash);
commonFormats.put("fr-CH", ddDot);
commonFormats.put("nl-BE", ddSlash);
FF_FORMATS_.putAll(commonFormats);
FF_FORMATS_.put("ban", ddDot);
FF_FORMATS_.put("da", ddDot);
FF_FORMATS_.put("en-PH", mmSlash);
FF_FORMATS_.put("lv", ddDotDot);
FF_ESR_FORMATS_.putAll(commonFormats);
FF_ESR_FORMATS_.put("da", ddDot);
CHROME_FORMATS_.put("be", mmSlash);
CHROME_FORMATS_.put("da", ddDot);
CHROME_FORMATS_.put("en-CA", yyyyDash);
CHROME_FORMATS_.put("en-IE", ddSlash);
CHROME_FORMATS_.put("en-MT", ddSlash);
CHROME_FORMATS_.put("en-PH", mmSlash);
CHROME_FORMATS_.put("es-US", ddSlash);
CHROME_FORMATS_.put("fr-CH", ddDot);
CHROME_FORMATS_.put("ga", mmSlash);
CHROME_FORMATS_.put("hr", ddDotBlankDot);
CHROME_FORMATS_.put("in-ID", ddSlash);
CHROME_FORMATS_.put("is", mmSlash);
CHROME_FORMATS_.put("iw", ddDot);
CHROME_FORMATS_.put("lv", ddDotDot);
CHROME_FORMATS_.put("mk", mmSlash);
CHROME_FORMATS_.put("nl-BE", ddSlash);
CHROME_FORMATS_.put("sq", mmSlash);
EDGE_FORMATS_.put("da", ddDot);
EDGE_FORMATS_.put("en-CA", yyyyDash);
EDGE_FORMATS_.put("en-IE", ddSlash);
EDGE_FORMATS_.put("en-MT", ddSlash);
EDGE_FORMATS_.put("en-PH", ddSlash);
EDGE_FORMATS_.put("es-US", ddSlash);
EDGE_FORMATS_.put("fr-CH", ddDot);
EDGE_FORMATS_.put("hr", ddDotBlankDot);
EDGE_FORMATS_.put("in-ID", ddSlash);
EDGE_FORMATS_.put("iw", ddDot);
EDGE_FORMATS_.put("nl-BE", ddSlash);
IE_FORMATS_.put("ar", rightToLeft);
IE_FORMATS_.put("ar-AE", rightToLeft);
IE_FORMATS_.put("ar-BH", rightToLeft);
IE_FORMATS_.put("ar-DZ", "\u200Fdd\u200F-\u200FMM\u200F-\u200FYYYY");
IE_FORMATS_.put("ar-LY", rightToLeft);
IE_FORMATS_.put("ar-MA", "\u200Fdd\u200F-\u200FMM\u200F-\u200FYYYY");
IE_FORMATS_.put("ar-TN", "\u200Fdd\u200F-\u200FMM\u200F-\u200FYYYY");
IE_FORMATS_.put("ar-EG", rightToLeft);
IE_FORMATS_.put("ar-IQ", rightToLeft);
IE_FORMATS_.put("ar-JO", rightToLeft);
IE_FORMATS_.put("ar-KW", rightToLeft);
IE_FORMATS_.put("ar-LB", rightToLeft);
IE_FORMATS_.put("ar-OM", rightToLeft);
IE_FORMATS_.put("ar-QA", rightToLeft);
IE_FORMATS_.put("ar-SA", rightToLeft);
IE_FORMATS_.put("ar-SD", rightToLeft);
IE_FORMATS_.put("ar-SY", rightToLeft);
IE_FORMATS_.put("ar-YE", rightToLeft);
IE_FORMATS_.put("ban", ddDot);
IE_FORMATS_.put("cs", ddDot);
IE_FORMATS_.put("da", ddDash);
IE_FORMATS_.put("en-IN", ddDash);
IE_FORMATS_.put("en-MT", ddSlash);
IE_FORMATS_.put("en-CA", yyyyMinus);
IE_FORMATS_.put("en-PH", ddSlash);
IE_FORMATS_.put("es-PH", ddSlash);
IE_FORMATS_.put("es-PR", mmSlash);
IE_FORMATS_.put("es-US", mmSlash);
IE_FORMATS_.put("fr-CH", ddDot);
IE_FORMATS_.put("ga", ddSlash);
IE_FORMATS_.put("hi", ddDash);
IE_FORMATS_.put("hr", ddDotDot);
IE_FORMATS_.put("hu", yyyyDotBlankDotIE);
IE_FORMATS_.put("hu-HU", yyyyDotBlankDotIE);
IE_FORMATS_.put("iw", ddSlash);
IE_FORMATS_.put("it-CH", ddDot);
IE_FORMATS_.put("ja", "\u200EYYYY\u200E\u5E74\u200EMM\u200E\u6708\u200Edd\u200E\u65E5");
IE_FORMATS_.put("ja-JP-u-ca-japanese", "\u200Eyy\u200E/\u200EMM\u200E/\u200Edd");
IE_FORMATS_.put("ko", "\u200EYYYY\u200E\uB144 \u200EMM\u200E\uC6D4 \u200Edd\u200E\uC77C");
IE_FORMATS_.put("lt", yyyyMinus);
IE_FORMATS_.put("lv", ddDot);
IE_FORMATS_.put("mt", ddSlash);
IE_FORMATS_.put("nl-BE", ddSlash);
IE_FORMATS_.put("no", ddDot);
IE_FORMATS_.put("pl", ddDot);
IE_FORMATS_.put("pt-PT", ddSlash);
IE_FORMATS_.put("sl", ddDotBlank);
IE_FORMATS_.put("sq", ddDot);
IE_FORMATS_.put("sr-BA", ddDot);
IE_FORMATS_.put("sr-CS", ddDot);
IE_FORMATS_.put("sr-ME", ddDot);
IE_FORMATS_.put("sr-RS", ddDot);
IE_FORMATS_.put("zh", "\u200EYYYY\u200E\u5E74\u200EMM\u200E\u6708\u200Edd\u200E\u65E5");
IE_FORMATS_.put("zh-HK", "\u200EYYYY\u200E\u5E74\u200EMM\u200E\u6708\u200Edd\u200E\u65E5");
}
/**
* Default constructor.
*/
public DateTimeFormat() {
}
private DateTimeFormat(final String[] locales, final BrowserVersion browserVersion) {
final Map formats;
if (browserVersion.isChrome()) {
formats = CHROME_FORMATS_;
}
else if (browserVersion.isEdge()) {
formats = EDGE_FORMATS_;
}
else if (browserVersion.isIE()) {
formats = IE_FORMATS_;
}
else if (browserVersion.isFirefox78()) {
formats = FF_ESR_FORMATS_;
}
else {
formats = FF_FORMATS_;
}
String locale = "";
String pattern = null;
for (final String l : locales) {
pattern = getPattern(formats, l);
if (pattern != null) {
locale = l;
}
}
if (pattern == null) {
pattern = formats.get("");
}
if (!browserVersion.hasFeature(JS_DATE_WITH_LEFT_TO_RIGHT_MARK) && !locale.startsWith("ar")) {
pattern = pattern.replace("\u200E", "");
}
formatter_ = new DateTimeFormatHelper(locale, browserVersion, pattern);
}
private static String getPattern(final Map formats, final String locale) {
if ("no-NO-NY".equals(locale)) {
throw ScriptRuntime.rangeError("Invalid language tag: " + locale);
}
String pattern = formats.get(locale);
if (pattern == null && locale.indexOf('-') != -1) {
pattern = formats.get(locale.substring(0, locale.indexOf('-')));
}
return pattern;
}
/**
* JavaScript constructor.
* @param cx the current context
* @param args the arguments to the WebSocket constructor
* @param ctorObj the function object
* @param inNewExpr Is new or not
* @return the java object to allow JavaScript to access
*/
@JsxConstructor
public static Scriptable jsConstructor(final Context cx, final Object[] args, final Function ctorObj,
final boolean inNewExpr) {
final String[] locales;
if (args.length != 0) {
if (args[0] instanceof NativeArray) {
final NativeArray array = (NativeArray) args[0];
locales = new String[(int) array.getLength()];
for (int i = 0; i < locales.length; i++) {
locales[i] = Context.toString(array.get(i));
}
}
else {
locales = new String[] {Context.toString(args[0])};
}
}
else {
locales = new String[] {""};
}
final Window window = getWindow(ctorObj);
final DateTimeFormat format = new DateTimeFormat(locales, window.getBrowserVersion());
format.setParentScope(window);
format.setPrototype(window.getPrototype(format.getClass()));
return format;
}
/**
* Formats a date according to the locale and formatting options of this {@code DateTimeFormat} object.
* @param object the JavaScript object to convert
* @return the dated formated
*/
@JsxFunction
public String format(final Object object) {
final Date date = (Date) Context.jsToJava(object, Date.class);
return formatter_.format(date);
}
/**
* @return A new object with properties reflecting the locale and date and time formatting options
* computed during the initialization of the given {@code DateTimeFormat} object.
*/
@JsxFunction
public Scriptable resolvedOptions() {
return Context.getCurrentContext().newObject(getParentScope());
}
/**
* Helper.
*/
static final class DateTimeFormatHelper {
private final DateTimeFormatter formatter_;
private Chronology chronology_;
DateTimeFormatHelper(final String locale, final BrowserVersion browserVersion, final String pattern) {
if (locale.startsWith("ar")
&& (!"ar-DZ".equals(locale)
&& !"ar-LY".equals(locale)
&& !"ar-MA".equals(locale)
&& !"ar-TN".equals(locale))) {
final DecimalStyle decimalStyle = DecimalStyle.STANDARD.withZeroDigit('\u0660');
formatter_ = DateTimeFormatter.ofPattern(pattern).withDecimalStyle(decimalStyle);
}
else {
formatter_ = DateTimeFormatter.ofPattern(pattern);
}
switch (locale) {
case "ja-JP-u-ca-japanese":
chronology_ = JapaneseChronology.INSTANCE;
break;
case "ar":
if (browserVersion.hasFeature(JS_DATE_WITH_LEFT_TO_RIGHT_MARK)) {
chronology_ = HijrahChronology.INSTANCE;
}
break;
case "ar-SA":
chronology_ = HijrahChronology.INSTANCE;
break;
case "th":
case "th-TH":
chronology_ = ThaiBuddhistChronology.INSTANCE;
break;
default:
}
}
/**
* Formats a date according to the locale and formatting options of this {@code DateTimeFormat} object.
* @param date the JavaScript object to convert
* @return the dated formated
*/
String format(final Date date) {
TemporalAccessor zonedDateTime = date.toInstant().atZone(ZoneId.systemDefault());
if (chronology_ != null) {
zonedDateTime = chronology_.date(zonedDateTime);
}
return formatter_.format(zonedDateTime);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy