diff --git a/README.md b/README.md index e12028de..2754c308 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ If you have a `crossProject`, the setting must be used only in the JS part: ```scala lazy val myCross = crossProject. ... - jsSettings.( + .jsSettings( libraryDependencies += "com.github.cquiroz" %%% "scala-java-locales" % "0.2.0+29" ) ``` diff --git a/core/src/main/scala/java/text/DateFormat.scala b/core/src/main/scala/java/text/DateFormat.scala new file mode 100644 index 00000000..2c4ebfb8 --- /dev/null +++ b/core/src/main/scala/java/text/DateFormat.scala @@ -0,0 +1,105 @@ +package java.text + +import java.util.Locale + +import locales.LocaleRegistry +import locales.cldr.{CalendarPatterns, LDML} + +abstract class DateFormat protected () extends Format { + // override final def format(obj: AnyRef, toAppendTo: StringBuffer, pos: FieldPosition): StringBuffer = ??? + // def format(date: Date, toAppendTo: StringBuffer, pos: FieldPosition): StringBuffer = ??? + + // TODO implement + // final def format(date: Date): String = ??? + // def parse(source: String, parsePosition: ParsePosition): Date = ??? + // def parse(source: String): Date = ??? + // override final def parseObject(source: String, pos: ParsePosition): AnyRef = ??? + // def setCalendar(newCalendar: Calendar): Unit = ??? + // def getCalendar(): Calendar = ??? + // def setNumberFormat(newNumberFormat: NumberFormat): Unit = ??? + // def getNumberFormat(): NumberFormat = ??? + // def setTimeZone(zone: TimeZone): Unit = ??? + // def getTimeZone(): TimeZone = ??? + // def setLenient(lenient: Boolean): Unit = ??? + // def isLenient(): Boolean = ??? + // override def hashCode(): Int = ??? + // override def equals(obj: Any): Boolean = ??? + // override def clone(): Any = ??? +} + +object DateFormat { + val ERA_FIELD: Int = 0 + val YEAR_FIELD: Int = 1 + val MONTH_FIELD: Int = 2 + val DATE_FIELD: Int = 3 + val HOUR_OF_DAY1_FIELD: Int = 4 + val HOUR_OF_DAY0_FIELD: Int = 5 + val MINUTE_FIELD: Int = 6 + val SECOND_FIELD: Int = 7 + val MILLISECOND_FIELD: Int = 8 + val DAY_OF_WEEK_FIELD: Int = 9 + val DAY_OF_YEAR_FIELD: Int = 10 + val DAY_OF_WEEK_IN_MONTH_FIELD: Int = 11 + val WEEK_OF_YEAR_FIELD: Int = 12 + val WEEK_OF_MONTH_FIELD: Int = 13 + val AM_PM_FIELD: Int = 14 + val HOUR1_FIELD: Int = 15 + val HOUR0_FIELD: Int = 16 + val TIMEZONE_FIELD: Int = 17 + + val FULL: Int = 0 + val LONG: Int = 1 + val MEDIUM: Int = 2 + val SHORT: Int = 3 + val DEFAULT: Int = 2 + + private def parentPatterns(ldml: LDML): Option[CalendarPatterns] = + ldml.calendarPatterns.orElse(ldml.parent.flatMap(parentPatterns)) + + private def patternsR(ldml: LDML, get: CalendarPatterns => Option[String]): Option[String] = + ldml.calendarPatterns.flatMap(get).orElse(ldml.parent.flatMap(patternsR(_, get))) + + final def getTimeInstance(): DateFormat = getTimeInstance(DEFAULT) + + final def getTimeInstance(style: Int): DateFormat = + getTimeInstance(style, Locale.getDefault(Locale.Category.FORMAT)) + + final def getTimeInstance(style: Int, aLocale: Locale): DateFormat = + LocaleRegistry.ldml(aLocale).flatMap { ldml => + val ptrn = patternsR(ldml, _.timePatterns.get(style)) + ptrn.map(new SimpleDateFormat(_, aLocale)) + }.getOrElse(new SimpleDateFormat("", aLocale)) + + final def getDateInstance(): DateFormat = getDateInstance(DEFAULT) + + final def getDateInstance(style: Int): DateFormat = + getDateInstance(style, Locale.getDefault(Locale.Category.FORMAT)) + + final def getDateInstance(style: Int, aLocale: Locale): DateFormat = + LocaleRegistry.ldml(aLocale).flatMap { ldml => + val ptrn = patternsR(ldml, _.datePatterns.get(style)) + ptrn.map(new SimpleDateFormat(_, aLocale)) + }.getOrElse(new SimpleDateFormat("", aLocale)) + + final def getDateTimeInstance(): DateFormat = getDateInstance(DEFAULT) + + final def getDateTimeInstance(dateStyle: Int, timeStyle: Int): DateFormat = + getDateTimeInstance(dateStyle, timeStyle, Locale.getDefault(Locale.Category.FORMAT)) + + final def getDateTimeInstance(dateStyle: Int, timeStyle: Int, aLocale: Locale): DateFormat = + LocaleRegistry.ldml(aLocale).flatMap { ldml => + val datePtrn = patternsR(ldml, _.datePatterns.get(dateStyle)) + val timePtrn = patternsR(ldml, _.timePatterns.get(timeStyle)) + (datePtrn, timePtrn) match { + case (Some(d), Some(t)) => Some(new SimpleDateFormat(s"$d $t", aLocale)) + case (Some(d), None) => Some(new SimpleDateFormat(s"$d", aLocale)) + case (None, Some(t)) => Some(new SimpleDateFormat(s"$t", aLocale)) + case _ => Some(new SimpleDateFormat("", aLocale)) + } + }.getOrElse(new SimpleDateFormat("", aLocale)) + + final def getInstance(): DateFormat = + getDateTimeInstance(SHORT, SHORT) + + def getAvailableLocales(): Array[Locale] = Locale.getAvailableLocales +} diff --git a/core/src/main/scala/java/text/DateFormatSymbols.scala b/core/src/main/scala/java/text/DateFormatSymbols.scala index 4a69a320..e91d29cc 100644 --- a/core/src/main/scala/java/text/DateFormatSymbols.scala +++ b/core/src/main/scala/java/text/DateFormatSymbols.scala @@ -38,7 +38,7 @@ object DateFormatSymbols { private def toDFS(locale: Locale, dfs: DateFormatSymbols, ldml: LDML): DateFormatSymbols = { def parentSymbols(ldml: LDML): Option[CalendarSymbols] = - ldml.calendar.orElse(ldml.parent.flatMap(parentSymbols)) + ldml.calendarSymbols.orElse(ldml.parent.flatMap(parentSymbols)) def elementsArray(ldml: LDML, read: CalendarSymbols => Option[List[String]]): Option[List[String]] = parentSymbols(ldml).flatMap { s => diff --git a/core/src/main/scala/java/text/DecimalFormat.scala b/core/src/main/scala/java/text/DecimalFormat.scala new file mode 100644 index 00000000..08867547 --- /dev/null +++ b/core/src/main/scala/java/text/DecimalFormat.scala @@ -0,0 +1,57 @@ +package java.text + +class DecimalFormat(private[this] val pattern: String, private[this] var symbols: DecimalFormatSymbols) extends Format { + def this(pattern: String) = this(pattern, DecimalFormatSymbols.getInstance()) + + //def this() = this("???", DecimalFormatSymbols.getInstance()) + + override final def format(obj: AnyRef, toAppendTo: StringBuffer, pos: FieldPosition): StringBuffer = ??? + + override final def parseObject(source: String, pos: ParsePosition): AnyRef = ??? + + // TODO implement + //def format(number: Double, toAppendTo: StringBuffer, pos: FieldPosition): StringBuffer = ??? + //def format(number: Long, toAppendTo: StringBuffer, pos: FieldPosition): StringBuffer = ??? + //def parse(source: String, parsePosition: ParsePosition): Number = ??? + //def parse(source: String): Number = ??? + def getDecimalFormatSymbols(): DecimalFormatSymbols = symbols + + def setDecimalFormatSymbols(symbols: DecimalFormatSymbols): Unit = + this.symbols = symbols + + // def getPositivePrefix(): String = ??? + // def setPositivePrefix(newValue: String): Unit = ??? + // def getNegativePrefix(): String = ??? + // def setNegativePrefix(newValue: String): Unit = ??? + // def getPositiveSuffix(): String = ??? + // def setPositiveSuffix(newValue: String): Unit = ??? + // def getNegativeSuffix(): String = ??? + // def setNegativeSuffix(newValue: String): Unit = ??? + // def getMultiplier(): Int = ??? + // def setMultiplier(newValue: Int): Unit = ??? + // override def setGroupingUsed(newValue: Boolean): Unit = ??? + // def getGroupingSize(): Int = ??? + // def setGroupingSize(newValue: Int): Unit = ??? + // def isDecimalSeparatorAlwaysShown(): Boolean = ??? + // def setDecimalSeparatorAlwaysShown(newValue: Boolean): Unit = ??? + // def isParseBigDecimal(): Boolean = ??? + // def setParseBigDecimal(newValue: Boolean): Unit = ??? + // override def clone(): Any = ??? + // override def hashCode(): Int = ??? + // override def equals(obj: Any): Boolean = ??? + def toPattern(): String = pattern + // def toLocalizedPattern(): String = pattern + // def applyPattern(pattern: String): Unit = ??? + // def setMaximumIntegerDigits(newValue: Int): Unit = ??? + // def setMinimumIntegerDigits(newValue: Int): Unit = ??? + // def setMaximumFractionDigits(newValue: Int): Unit = ??? + // def setMinimumFractionDigits(newValue: Int): Unit = ??? + // def getMaximumIntegerDigits(): Int = ??? + // def getMinimumIntegerDigits(): Int = ??? + // def getMaximumFractionDigits(): Int = ??? + // def getMinimumFractionDigits(): Int = ??? + // def getCurrency(): Currency = ??? + // def setCurrency(currency: Currency): Unit = ??? + // def getRoundingMode(): RoundingMode = ??? + // def setRoundingMode(currency: RoundingMode): Unit = ??? +} diff --git a/core/src/main/scala/java/text/DecimalFormatSymbols.scala b/core/src/main/scala/java/text/DecimalFormatSymbols.scala index fed2777c..668f8680 100644 --- a/core/src/main/scala/java/text/DecimalFormatSymbols.scala +++ b/core/src/main/scala/java/text/DecimalFormatSymbols.scala @@ -21,13 +21,14 @@ object DecimalFormatSymbols { private def initialize(locale: Locale, dfs: DecimalFormatSymbols): DecimalFormatSymbols = { // Find the correct numbering systems for the ldml - def ns(ldml: LDML): NumberingSystem = + def ns(ldml: LDML): NumberingSystem = { ldml.defaultNS.flatMap { n => root.digitSymbols.find(_.ns == n).collect { - case s @ Symbols(_, Some(alias), _, _, _, _, _, _, _, _, _) => alias - case s => n + case s@Symbols(_, Some(alias), _, _, _, _, _, _, _, _, _) => alias + case s => n } }.getOrElse(latn) + } LocaleRegistry .ldml(locale) diff --git a/core/src/main/scala/java/text/NumberFormat.scala b/core/src/main/scala/java/text/NumberFormat.scala new file mode 100644 index 00000000..2829d530 --- /dev/null +++ b/core/src/main/scala/java/text/NumberFormat.scala @@ -0,0 +1,53 @@ +package java.text + +import java.util.Locale + +abstract class NumberFormat protected () extends Format { + override final def parseObject(source: String, pos: ParsePosition): AnyRef = ??? + + override def format(obj: AnyRef, toAppendTo: StringBuffer, pos: FieldPosition): StringBuffer = ??? + + // TODO implement + // final def format(number: Double): String = ??? + // final def format(number: Long): String = ??? + // def format(number: Double, toAppendTo: StringBuffer, pos: FieldPosition): StringBuffer = ??? + // def format(number: Long, toAppendTo: StringBuffer, pos: FieldPosition): StringBuffer = ??? + // def parse(source: String, parsePosition: ParsePosition): Number = ??? + // def parse(source: String): Number = ??? + // def isParseIntegerOnly(): Boolean = ??? + // def setParseIntegerOnly(value: Boolean): Unit = ??? + // override def hashCode(): Int = ??? + // override def equals(obj: Any): Boolean = ??? + // override def clone(): Any = ??? + // def isGroupingUsed(): Boolean = ??? + // def setGroupingUsed(newValue: Boolean): Unit = ??? + // def getMaximumIntegerDigits(): Int = ??? + // def setMaximumIntegerDigits(newValue: Int): Unit = ??? + // def getMinimumIntegerDigits(): Int = ??? + // def setMinimumIntegerDigits(newValue: Int): Unit = ??? + // def getMaximumFractionDigits(): Int = ??? + // def setMaximumFractionDigits(newValue: Int): Unit = ??? + // def getMinimumFractionDigits(): Int = ??? + // def setMinimumFractionDigits(newValue: Int): Unit = ??? + // def getCurrency(): Currency = ??? + // def setCurrency(currency: Currency): Unit = ??? + // def getRoundingMode(): RoundingMode = ??? + // def setRoundingMode(currency: RoundingMode): Unit = ??? +} + +object NumberFormat { + val INTEGER_FIELD: Int = 0 + val FRACTION_FIELD: Int = 1 + + //final def getInstance(): NumberFormat = ??? + //def getInstance(inLocale: Locale): NumberFormat = ??? + //final def getNumberInstance(): NumberFormat = ??? + //def getNumberInstance(inLocale: Locale): NumberFormat = ??? + //final def getIntegerInstance(): NumberFormat = ??? + //def getIntegerInstance(inLocale: Locale): NumberFormat = ??? + //final def getCurrencyInstance(): NumberFormat = ??? + //def getCurrencyInstance(inLocale: Locale): NumberFormat = ??? + //final def getPercentInstance(): NumberFormat = ??? + //def getPercentInstance(inLocale: Locale): NumberFormat = ??? + //def getAvailableLocales(): Array[Locale] = ??? +} diff --git a/core/src/main/scala/java/text/SimpleDateFormat.scala b/core/src/main/scala/java/text/SimpleDateFormat.scala new file mode 100644 index 00000000..38cf5980 --- /dev/null +++ b/core/src/main/scala/java/text/SimpleDateFormat.scala @@ -0,0 +1,34 @@ +package java.text + +import java.util.Locale + +class SimpleDateFormat(private[this] val pattern: String, + private[this] var symbols: DateFormatSymbols) extends DateFormat { + def this(pattern: String) = this(pattern, DateFormatSymbols.getInstance()) + + def this(pattern: String, aLocale: Locale) = this(pattern, DateFormatSymbols.getInstance(aLocale)) + + //def this() = this("???", DecimalFormatSymbols.getInstance()) + + override final def format(obj: AnyRef, toAppendTo: StringBuffer, pos: FieldPosition): StringBuffer = ??? + + override final def parseObject(source: String, pos: ParsePosition): AnyRef = ??? + + // def set2DigitYearStart(startDate: Date): Unit = ??? + // def get2DigitYearStart(): Date = ??? + // override final def format(date: Date, toAppendTo: StringBuffer, pos: FieldPosition): StringBuffer = ??? + // def formatToCharacterIterator(obj: AnyRef): AttributedCharacterIterator = + // def parse(text: String, pos: ParsePosition): Date = ??? + def toPattern(): String = pattern + // def toLocalizedPattern(): String = pattern + // def applyPattern(pattern: String): Unit = ??? + // def applyLocalizedPattern(pattern: String): Unit = ??? + def getDateFormatSymbols(): DateFormatSymbols = symbols + + def setDateFormatSymbols(symbols: DateFormatSymbols): Unit = + this.symbols = symbols + + // override def clone(): Any = ??? + // override def hashCode(): Int = ??? + // override def equals(obj: Any): Boolean = ??? +} diff --git a/core/src/main/scala/locales/LocaleRegistry.scala b/core/src/main/scala/locales/LocaleRegistry.scala index 8a90ce45..073f94d6 100644 --- a/core/src/main/scala/locales/LocaleRegistry.scala +++ b/core/src/main/scala/locales/LocaleRegistry.scala @@ -63,7 +63,7 @@ object LocaleRegistry { private var defaultLocale: Locale = en.toLocale private var defaultPerCategory: Map[Locale.Category, Option[Locale]] = - Locale.Category.values().map(_ -> None).toMap + Locale.Category.values().map(_ -> Some(defaultLocale)).toMap private lazy val ldmls: mutable.Map[String, LDML] = mutable.Map.empty @@ -80,7 +80,7 @@ object LocaleRegistry { def resetRegistry(): Unit = { defaultLocale = en.toLocale defaultPerCategory = - Locale.Category.values().map(_ -> None).toMap + Locale.Category.values().map(_ -> Some(defaultLocale)).toMap ldmls.empty initDefaultLocales() } diff --git a/core/src/main/scala/locales/cldr/cldr.scala b/core/src/main/scala/locales/cldr/cldr.scala index 797fa2e3..bb715133 100644 --- a/core/src/main/scala/locales/cldr/cldr.scala +++ b/core/src/main/scala/locales/cldr/cldr.scala @@ -18,6 +18,8 @@ case class CalendarSymbols(months: List[String], shortMonths: List[String], weekdays: List[String], shortWeekdays: List[String], amPm: List[String], eras: List[String]) +case class CalendarPatterns(datePatterns: Map[Int, String], timePatterns: Map[Int, String]) + /** * Interfaces describing an LDML Locale */ @@ -29,7 +31,7 @@ case class LDMLLocale(language: String, territory: Option[String], */ case class LDML(parent: Option[LDML], locale: LDMLLocale, defaultNS: Option[NumberingSystem], digitSymbols: List[Symbols] = Nil, - calendar: Option[CalendarSymbols]) { + calendarSymbols: Option[CalendarSymbols], calendarPatterns: Option[CalendarPatterns]) { def languageTag: String = toLocale.toLanguageTag diff --git a/project/src/main/scala/locales/ScalaLocaleCodeGen.scala b/project/src/main/scala/locales/ScalaLocaleCodeGen.scala index a6cbdc89..952a7e8f 100644 --- a/project/src/main/scala/locales/ScalaLocaleCodeGen.scala +++ b/project/src/main/scala/locales/ScalaLocaleCodeGen.scala @@ -40,6 +40,14 @@ object EraSymbols { case class CalendarSymbols(months: MonthSymbols, weekdays: WeekdaysSymbols, amPm: AmPmSymbols, eras: EraSymbols) +case class DateTimePattern(patternType: String, pattern: String) + +case class CalendarPatterns(datePatterns: List[DateTimePattern], timePatterns: List[DateTimePattern]) + +object CalendarPatterns { + val zero = CalendarPatterns(Nil, Nil) +} + case class NumericSystem(id: String, digits: String) case class NumberSymbols(system: NumericSystem, @@ -64,7 +72,9 @@ case class XMLLDMLLocale(language: String, territory: Option[String], variant: Option[String], script: Option[String]) case class XMLLDML(locale: XMLLDMLLocale, defaultNS: Option[NumericSystem], - digitSymbols: Map[NumericSystem, NumberSymbols], calendar: Option[CalendarSymbols]) { + digitSymbols: Map[NumericSystem, NumberSymbols], calendar: Option[CalendarSymbols], + datePatterns: Option[CalendarPatterns]) { + val scalaSafeName: String = { List(Some(locale.language), locale.script, locale.territory, locale.variant) .flatten.mkString("_") @@ -96,6 +106,7 @@ object CodeGenerator { IMPORT("locales.cldr.LDMLLocale"), IMPORT("locales.cldr.Symbols"), IMPORT("locales.cldr.CalendarSymbols"), + IMPORT("locales.cldr.CalendarPatterns"), IMPORT("locales.cldr.data.numericsystems._")) ++ objectBlock ) inPackage "locales.cldr.data" } @@ -118,6 +129,7 @@ object CodeGenerator { val ldmlSym = getModule("LDML") val ldmlNumericSym = getModule("Symbols") val ldmlCalendarSym = getModule("CalendarSymbols") + val ldmlCalendarPatternsSym = getModule("CalendarPatterns") val ldmlLocaleSym = getModule("LDMLLocale") val parent = findParent(root, langs, ldml).fold(NONE)(v => SOME(REF(v))) @@ -151,8 +163,22 @@ object CodeGenerator { LIST(cs.amPm.amPm.map(LIT(_))), LIST(cs.eras.eras.map(LIT(_)))) }.fold(NONE)(s => SOME(s)) + val gcp = ldml.datePatterns.map { cs => + def patternToIndex(i: String) = i match { + case "full" => 0 + case "long" => 1 + case "medium" => 2 + case "short" => 3 + case x => throw new IllegalArgumentException(s"Unknown format $x, abort ") + } + + val dates = MAKE_MAP(cs.datePatterns.map(p => TUPLE(LIT(patternToIndex(p.patternType)), LIT(p.pattern)))) + val times = MAKE_MAP(cs.timePatterns.map(p => TUPLE(LIT(patternToIndex(p.patternType)), LIT(p.pattern)))) + Apply(ldmlCalendarPatternsSym, dates, times) + }.fold(NONE)(s => SOME(s)) + OBJECTDEF(ldml.scalaSafeName) withParents Apply(ldmlSym, parent, - ldmlLocaleTree, defaultNS, LIST(numericSymbols), gc) + ldmlLocaleTree, defaultNS, LIST(numericSymbols), gc, gcp) } def metadata(codes: List[String], languages: List[String], scripts: List[String]): Tree = { @@ -289,6 +315,28 @@ object ScalaLocaleCodeGen { } } + def readCalendarPatterns(xml: Node): Option[CalendarPatterns] = { + def readPatterns(n: Node, sub: String, formatType: String): Seq[DateTimePattern] = + for { + ft <- n \ formatType + p <- ft \ sub \ "pattern" + if (p \ "@alt").text != "variant" + } yield DateTimePattern((ft \ "@type").text, p.text) + + val datePatterns = (for { + df <- xml \\ "dateFormats" + } yield { + readPatterns(df, "dateFormat", "dateFormatLength") + }).headOption.map(_.toList) + + val timePatterns = (for { + df <- xml \\ "timeFormats" + } yield { + readPatterns(df, "timeFormat", "timeFormatLength") + }).headOption.map(_.toList) + + Some(CalendarPatterns(datePatterns.getOrElse(Nil), timePatterns.getOrElse(Nil))) + } /** * Parse the xml into an XMLLDML object */ @@ -309,6 +357,12 @@ object ScalaLocaleCodeGen { if n.text.nonEmpty } yield readCalendarData(n) + val gregorianDatePatterns = for { + n <- xml \ "dates" \\ "calendar" + if (n \ "@type").text == "gregorian" + if n.text.nonEmpty + } yield readCalendarPatterns(n) + // Find out the default numeric system val defaultNS = Option((xml \ "numbers" \ "defaultNumberingSystem").text) .filter(_.nonEmpty).filter(ns.contains) @@ -352,7 +406,7 @@ object ScalaLocaleCodeGen { nsSymbols } XMLLDML(XMLLDMLLocale(language, territory, variant, script), - defaultNS.flatMap(ns.get), symbols.toMap, gregorian.flatten.headOption) + defaultNS.flatMap(ns.get), symbols.toMap, gregorian.flatten.headOption, gregorianDatePatterns.flatten.headOption) } // Note this must be a def or there could be issues with concurrency diff --git a/testSuite/shared/src/test/scala/testsuite/javalib/text/DateFormatTest.scala b/testSuite/shared/src/test/scala/testsuite/javalib/text/DateFormatTest.scala new file mode 100644 index 00000000..99f7021d --- /dev/null +++ b/testSuite/shared/src/test/scala/testsuite/javalib/text/DateFormatTest.scala @@ -0,0 +1,853 @@ +package testsuite.javalib.text + +import java.text.{DateFormat, SimpleDateFormat} +import java.util.Locale + +import locales.LocaleRegistry +import locales.cldr.LDML +import locales.cldr.data._ +import org.junit.Assert._ +import org.junit.Test +import testsuite.utils.{LocaleTestSetup, Platform} + +class DateFormatTest extends LocaleTestSetup { + case class TestCase(ldml: LDML, tag: String, l: Locale, cldr21: Boolean, dateFormats: Map[Int, String], timeFormats: Map[Int, String]) + + @Test def test_constants(): Unit = { + assertEquals(0, DateFormat.ERA_FIELD) + assertEquals(1, DateFormat.YEAR_FIELD) + assertEquals(2, DateFormat.MONTH_FIELD) + assertEquals(3, DateFormat.DATE_FIELD) + assertEquals(4, DateFormat.HOUR_OF_DAY1_FIELD) + assertEquals(5, DateFormat.HOUR_OF_DAY0_FIELD) + assertEquals(6, DateFormat.MINUTE_FIELD) + assertEquals(7, DateFormat.SECOND_FIELD) + assertEquals(8, DateFormat.MILLISECOND_FIELD) + assertEquals(9, DateFormat.DAY_OF_WEEK_FIELD) + assertEquals(10, DateFormat.DAY_OF_YEAR_FIELD) + assertEquals(11, DateFormat.DAY_OF_WEEK_IN_MONTH_FIELD) + assertEquals(12, DateFormat.WEEK_OF_YEAR_FIELD) + assertEquals(13, DateFormat.WEEK_OF_MONTH_FIELD) + assertEquals(14, DateFormat.AM_PM_FIELD) + assertEquals(15, DateFormat.HOUR1_FIELD) + assertEquals(16, DateFormat.HOUR0_FIELD) + assertEquals(17, DateFormat.TIMEZONE_FIELD) + + assertEquals(0, DateFormat.FULL) + assertEquals(1, DateFormat.LONG) + assertEquals(2, DateFormat.MEDIUM) + assertEquals(3, DateFormat.SHORT) + assertEquals(2, DateFormat.DEFAULT) + } + + @Test def test_available_locales(): Unit = { + assertTrue(DateFormat.getAvailableLocales.contains(Locale.ENGLISH)) + } + + @Test def test_default_date_format(): Unit = { + assertEquals("EEEE, MMMM d, y", DateFormat.getDateInstance(DateFormat.FULL).asInstanceOf[SimpleDateFormat].toPattern()) + assertEquals("MMMM d, y", DateFormat.getDateInstance(DateFormat.LONG).asInstanceOf[SimpleDateFormat].toPattern()) + assertEquals("MMM d, y", DateFormat.getDateInstance(DateFormat.MEDIUM).asInstanceOf[SimpleDateFormat].toPattern()) + assertEquals("M/d/yy", DateFormat.getDateInstance(DateFormat.SHORT).asInstanceOf[SimpleDateFormat].toPattern()) + + assertEquals("h:mm:ss a zzzz", DateFormat.getTimeInstance(DateFormat.FULL).asInstanceOf[SimpleDateFormat].toPattern()) + assertEquals("h:mm:ss a z", DateFormat.getTimeInstance(DateFormat.LONG).asInstanceOf[SimpleDateFormat].toPattern()) + assertEquals("h:mm:ss a", DateFormat.getTimeInstance(DateFormat.MEDIUM).asInstanceOf[SimpleDateFormat].toPattern()) + assertEquals("h:mm a", DateFormat.getTimeInstance(DateFormat.SHORT).asInstanceOf[SimpleDateFormat].toPattern()) + + assertEquals("EEEE, MMMM d, y h:mm:ss a zzzz", DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).asInstanceOf[SimpleDateFormat].toPattern()) + assertEquals("EEEE, MMMM d, y h:mm:ss a z", DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.LONG).asInstanceOf[SimpleDateFormat].toPattern()) + assertEquals("EEEE, MMMM d, y h:mm:ss a", DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.MEDIUM).asInstanceOf[SimpleDateFormat].toPattern()) + assertEquals("EEEE, MMMM d, y h:mm a", DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.SHORT).asInstanceOf[SimpleDateFormat].toPattern()) + assertEquals("MMMM d, y h:mm:ss a zzzz", DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.FULL).asInstanceOf[SimpleDateFormat].toPattern()) + assertEquals("MMMM d, y h:mm:ss a z", DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG).asInstanceOf[SimpleDateFormat].toPattern()) + assertEquals("MMMM d, y h:mm:ss a", DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.MEDIUM).asInstanceOf[SimpleDateFormat].toPattern()) + assertEquals("MMMM d, y h:mm a", DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.SHORT).asInstanceOf[SimpleDateFormat].toPattern()) + assertEquals("MMM d, y h:mm:ss a zzzz", DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.FULL).asInstanceOf[SimpleDateFormat].toPattern()) + assertEquals("MMM d, y h:mm:ss a z", DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).asInstanceOf[SimpleDateFormat].toPattern()) + assertEquals("MMM d, y h:mm:ss a", DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM).asInstanceOf[SimpleDateFormat].toPattern()) + assertEquals("MMM d, y h:mm a", DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT).asInstanceOf[SimpleDateFormat].toPattern()) + assertEquals("M/d/yy h:mm:ss a zzzz", DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.FULL).asInstanceOf[SimpleDateFormat].toPattern()) + assertEquals("M/d/yy h:mm:ss a z", DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.LONG).asInstanceOf[SimpleDateFormat].toPattern()) + assertEquals("M/d/yy h:mm:ss a", DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM).asInstanceOf[SimpleDateFormat].toPattern()) + assertEquals("M/d/yy h:mm a", DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).asInstanceOf[SimpleDateFormat].toPattern()) + } + + val stdLocales = List( + TestCase(root, "", Locale.ENGLISH, cldr21 = false, Map( + DateFormat.FULL -> "EEEE, MMMM d, y", + DateFormat.LONG -> "MMMM d, y", + DateFormat.MEDIUM -> "MMM d, y", + DateFormat.SHORT -> "M/d/yy"), + Map( + DateFormat.FULL -> "h:mm:ss a zzzz", + DateFormat.LONG -> "h:mm:ss a z", + DateFormat.MEDIUM -> "h:mm:ss a", + DateFormat.SHORT -> "h:mm a")), + TestCase(root, "", Locale.US, cldr21 = false, Map( + DateFormat.FULL -> "EEEE, MMMM d, y", + DateFormat.LONG -> "MMMM d, y", + DateFormat.MEDIUM -> "MMM d, y", + DateFormat.SHORT -> "M/d/yy"), + Map( + DateFormat.FULL -> "h:mm:ss a zzzz", + DateFormat.LONG -> "h:mm:ss a z", + DateFormat.MEDIUM -> "h:mm:ss a", + DateFormat.SHORT -> "h:mm a")) + ) + + val stdLocalesDiff = List( + TestCase(root, "", Locale.FRENCH, cldr21 = true, Map( // JVM + DateFormat.FULL -> "EEEE d MMMM y", + DateFormat.LONG -> "d MMMM y", + DateFormat.MEDIUM -> "d MMM y", + DateFormat.SHORT -> "dd/MM/yy"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")), + TestCase(root, "", Locale.FRENCH, cldr21 = false, Map( // JS + DateFormat.FULL -> "EEEE d MMMM y", + DateFormat.LONG -> "d MMMM y", + DateFormat.MEDIUM -> "d MMM y", + DateFormat.SHORT -> "dd/MM/y"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")), + TestCase(root, "", Locale.GERMAN, cldr21 = true, Map( + DateFormat.FULL -> "EEEE, d. MMMM y", + DateFormat.LONG -> "d. MMMM y", + DateFormat.MEDIUM -> "dd.MM.yyyy", + DateFormat.SHORT -> "dd.MM.yy"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")), + TestCase(root, "", Locale.GERMAN, cldr21 = false, Map( + DateFormat.FULL -> "EEEE, d. MMMM y", + DateFormat.LONG -> "d. MMMM y", + DateFormat.MEDIUM -> "dd.MM.y", + DateFormat.SHORT -> "dd.MM.yy"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")), + TestCase(root, "", Locale.ITALIAN, cldr21 = true, Map( + DateFormat.FULL -> "EEEE d MMMM y", + DateFormat.LONG -> "dd MMMM y", + DateFormat.MEDIUM -> "dd/MMM/y", + DateFormat.SHORT -> "dd/MM/yy"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")), + TestCase(root, "", Locale.ITALIAN, cldr21 = false, Map( + DateFormat.FULL -> "EEEE d MMMM y", + DateFormat.LONG -> "d MMMM y", + DateFormat.MEDIUM -> "dd MMM y", + DateFormat.SHORT -> "dd/MM/yy"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")), + TestCase(root, "", Locale.JAPANESE, cldr21 = true, Map( + DateFormat.FULL -> "y年M月d日EEEE", + DateFormat.LONG -> "y年M月d日", + DateFormat.MEDIUM -> "yyyy/MM/dd", + DateFormat.SHORT -> "yyyy/MM/dd"), + Map( + DateFormat.FULL -> "H時mm分ss秒 zzzz", + DateFormat.LONG -> "H:mm:ss z", + DateFormat.MEDIUM -> "H:mm:ss", + DateFormat.SHORT -> "H:mm")), + TestCase(root, "", Locale.JAPANESE, cldr21 = false, Map( + DateFormat.FULL -> "y年M月d日EEEE", + DateFormat.LONG -> "y年M月d日", + DateFormat.MEDIUM -> "y/MM/dd", + DateFormat.SHORT -> "y/MM/dd"), + Map( + DateFormat.FULL -> "H時mm分ss秒 zzzz", + DateFormat.LONG -> "H:mm:ss z", + DateFormat.MEDIUM -> "H:mm:ss", + DateFormat.SHORT -> "H:mm")), + TestCase(root, "", Locale.KOREAN, cldr21 = true, Map( + DateFormat.FULL -> "y년 M월 d일 EEEE", + DateFormat.LONG -> "y년 M월 d일", + DateFormat.MEDIUM -> "yyyy. M. d.", + DateFormat.SHORT -> "yy. M. d."), + Map( + DateFormat.FULL -> "a h시 m분 s초 zzzz", + DateFormat.LONG -> "a h시 m분 s초 z", + DateFormat.MEDIUM -> "a h:mm:ss", + DateFormat.SHORT -> "a h:mm")), + TestCase(root, "", Locale.KOREAN, cldr21 = false, Map( + DateFormat.FULL -> "y년 M월 d일 EEEE", + DateFormat.LONG -> "y년 M월 d일", + DateFormat.MEDIUM -> "y. M. d.", + DateFormat.SHORT -> "yy. M. d."), + Map( + DateFormat.FULL -> "a h시 m분 s초 zzzz", + DateFormat.LONG -> "a h시 m분 s초 z", + DateFormat.MEDIUM -> "a h:mm:ss", + DateFormat.SHORT -> "a h:mm")), + TestCase(root, "", Locale.CHINESE, cldr21 = true, Map( + DateFormat.FULL -> "y年M月d日EEEE", + DateFormat.LONG -> "y年M月d日", + DateFormat.MEDIUM -> "yyyy-M-d", + DateFormat.SHORT -> "yy-M-d"), + Map( + DateFormat.FULL -> "zzzzah时mm分ss秒", + DateFormat.LONG -> "zah时mm分ss秒", + DateFormat.MEDIUM -> "ah:mm:ss", + DateFormat.SHORT -> "ah:mm")), + TestCase(root, "", Locale.CHINESE, cldr21 = false, Map( + DateFormat.FULL -> "y年M月d日EEEE", + DateFormat.LONG -> "y年M月d日", + DateFormat.MEDIUM -> "y年M月d日", + DateFormat.SHORT -> "y/M/d"), + Map( + DateFormat.FULL -> "zzzz ah:mm:ss", + DateFormat.LONG -> "z ah:mm:ss", + DateFormat.MEDIUM -> "ah:mm:ss", + DateFormat.SHORT -> "ah:mm")), + TestCase(root, "", Locale.SIMPLIFIED_CHINESE, cldr21 = true, Map( + DateFormat.FULL -> "y年M月d日EEEE", + DateFormat.LONG -> "y年M月d日", + DateFormat.MEDIUM -> "yyyy-M-d", + DateFormat.SHORT -> "yy-M-d"), + Map( + DateFormat.FULL -> "zzzzah时mm分ss秒", + DateFormat.LONG -> "zah时mm分ss秒", + DateFormat.MEDIUM -> "ah:mm:ss", + DateFormat.SHORT -> "ah:mm")), + TestCase(root, "", Locale.SIMPLIFIED_CHINESE, cldr21 = false, Map( + DateFormat.FULL -> "y年M月d日EEEE", + DateFormat.LONG -> "y年M月d日", + DateFormat.MEDIUM -> "y年M月d日", + DateFormat.SHORT -> "y/M/d"), + Map( + DateFormat.FULL -> "zzzz ah:mm:ss", + DateFormat.LONG -> "z ah:mm:ss", + DateFormat.MEDIUM -> "ah:mm:ss", + DateFormat.SHORT -> "ah:mm")), + TestCase(root, "", Locale.TRADITIONAL_CHINESE, cldr21 = true, Map( + DateFormat.FULL -> "y年M月d日EEEE", + DateFormat.LONG -> "y年M月d日", + DateFormat.MEDIUM -> "yyyy/M/d", + DateFormat.SHORT -> "y/M/d"), + Map( + DateFormat.FULL -> "zzzzah時mm分ss秒", + DateFormat.LONG -> "zah時mm分ss秒", + DateFormat.MEDIUM -> "ah:mm:ss", + DateFormat.SHORT -> "ah:mm")), + TestCase(root, "", Locale.TRADITIONAL_CHINESE, cldr21 = false, Map( + DateFormat.FULL -> "y年M月d日 EEEE", + DateFormat.LONG -> "y年M月d日", + DateFormat.MEDIUM -> "y年M月d日", + DateFormat.SHORT -> "y/M/d"), + Map( + DateFormat.FULL -> "ah:mm:ss [zzzz]", + DateFormat.LONG -> "ah:mm:ss [z]", + DateFormat.MEDIUM -> "ah:mm:ss", + DateFormat.SHORT -> "ah:mm")), + TestCase(root, "", Locale.FRANCE, cldr21 = true, Map( // JVM + DateFormat.FULL -> "EEEE d MMMM y", + DateFormat.LONG -> "d MMMM y", + DateFormat.MEDIUM -> "d MMM y", + DateFormat.SHORT -> "dd/MM/yy"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")), + TestCase(root, "", Locale.FRANCE, cldr21 = false, Map( // JS + DateFormat.FULL -> "EEEE d MMMM y", + DateFormat.LONG -> "d MMMM y", + DateFormat.MEDIUM -> "d MMM y", + DateFormat.SHORT -> "dd/MM/y"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")), + TestCase(root, "", Locale.GERMANY, cldr21 = true, Map( + DateFormat.FULL -> "EEEE, d. MMMM y", + DateFormat.LONG -> "d. MMMM y", + DateFormat.MEDIUM -> "dd.MM.yyyy", + DateFormat.SHORT -> "dd.MM.yy"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")), + TestCase(root, "", Locale.GERMANY, cldr21 = false, Map( + DateFormat.FULL -> "EEEE, d. MMMM y", + DateFormat.LONG -> "d. MMMM y", + DateFormat.MEDIUM -> "dd.MM.y", + DateFormat.SHORT -> "dd.MM.yy"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")), + TestCase(root, "", Locale.ITALY, cldr21 = true, Map( + DateFormat.FULL -> "EEEE d MMMM y", + DateFormat.LONG -> "dd MMMM y", + DateFormat.MEDIUM -> "dd/MMM/y", + DateFormat.SHORT -> "dd/MM/yy"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")), + TestCase(root, "", Locale.ITALY, cldr21 = false, Map( + DateFormat.FULL -> "EEEE d MMMM y", + DateFormat.LONG -> "d MMMM y", + DateFormat.MEDIUM -> "dd MMM y", + DateFormat.SHORT -> "dd/MM/yy"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")), + TestCase(root, "", Locale.JAPAN, cldr21 = true, Map( + DateFormat.FULL -> "y年M月d日EEEE", + DateFormat.LONG -> "y年M月d日", + DateFormat.MEDIUM -> "yyyy/MM/dd", + DateFormat.SHORT -> "yyyy/MM/dd"), + Map( + DateFormat.FULL -> "H時mm分ss秒 zzzz", + DateFormat.LONG -> "H:mm:ss z", + DateFormat.MEDIUM -> "H:mm:ss", + DateFormat.SHORT -> "H:mm")), + TestCase(root, "", Locale.JAPAN, cldr21 = false, Map( + DateFormat.FULL -> "y年M月d日EEEE", + DateFormat.LONG -> "y年M月d日", + DateFormat.MEDIUM -> "y/MM/dd", + DateFormat.SHORT -> "y/MM/dd"), + Map( + DateFormat.FULL -> "H時mm分ss秒 zzzz", + DateFormat.LONG -> "H:mm:ss z", + DateFormat.MEDIUM -> "H:mm:ss", + DateFormat.SHORT -> "H:mm")), + TestCase(root, "", Locale.KOREA, cldr21 = true, Map( + DateFormat.FULL -> "y년 M월 d일 EEEE", + DateFormat.LONG -> "y년 M월 d일", + DateFormat.MEDIUM -> "yyyy. M. d.", + DateFormat.SHORT -> "yy. M. d."), + Map( + DateFormat.FULL -> "a h시 m분 s초 zzzz", + DateFormat.LONG -> "a h시 m분 s초 z", + DateFormat.MEDIUM -> "a h:mm:ss", + DateFormat.SHORT -> "a h:mm")), + TestCase(root, "", Locale.KOREA, cldr21 = false, Map( + DateFormat.FULL -> "y년 M월 d일 EEEE", + DateFormat.LONG -> "y년 M월 d일", + DateFormat.MEDIUM -> "y. M. d.", + DateFormat.SHORT -> "yy. M. d."), + Map( + DateFormat.FULL -> "a h시 m분 s초 zzzz", + DateFormat.LONG -> "a h시 m분 s초 z", + DateFormat.MEDIUM -> "a h:mm:ss", + DateFormat.SHORT -> "a h:mm")), + TestCase(root, "", Locale.CHINA, cldr21 = true, Map( + DateFormat.FULL -> "y年M月d日EEEE", + DateFormat.LONG -> "y年M月d日", + DateFormat.MEDIUM -> "yyyy-M-d", + DateFormat.SHORT -> "yy-M-d"), + Map( + DateFormat.FULL -> "zzzzah时mm分ss秒", + DateFormat.LONG -> "zah时mm分ss秒", + DateFormat.MEDIUM -> "ah:mm:ss", + DateFormat.SHORT -> "ah:mm")), + TestCase(root, "", Locale.CHINA, cldr21 = false, Map( + DateFormat.FULL -> "y年M月d日EEEE", + DateFormat.LONG -> "y年M月d日", + DateFormat.MEDIUM -> "y年M月d日", + DateFormat.SHORT -> "y/M/d"), + Map( + DateFormat.FULL -> "zzzz ah:mm:ss", + DateFormat.LONG -> "z ah:mm:ss", + DateFormat.MEDIUM -> "ah:mm:ss", + DateFormat.SHORT -> "ah:mm")), + TestCase(root, "", Locale.TAIWAN, cldr21 = true, Map( + DateFormat.FULL -> "y年M月d日EEEE", + DateFormat.LONG -> "y年M月d日", + DateFormat.MEDIUM -> "yyyy/M/d", + DateFormat.SHORT -> "y/M/d"), + Map( + DateFormat.FULL -> "zzzzah時mm分ss秒", + DateFormat.LONG -> "zah時mm分ss秒", + DateFormat.MEDIUM -> "ah:mm:ss", + DateFormat.SHORT -> "ah:mm")), + TestCase(root, "", Locale.TAIWAN, cldr21 = false, Map( + DateFormat.FULL -> "y年M月d日 EEEE", + DateFormat.LONG -> "y年M月d日", + DateFormat.MEDIUM -> "y年M月d日", + DateFormat.SHORT -> "y/M/d"), + Map( + DateFormat.FULL -> "ah:mm:ss [zzzz]", + DateFormat.LONG -> "ah:mm:ss [z]", + DateFormat.MEDIUM -> "ah:mm:ss", + DateFormat.SHORT -> "ah:mm")), + TestCase(root, "", Locale.UK, cldr21 = true, Map( + DateFormat.FULL -> "EEEE, d MMMM y", + DateFormat.LONG -> "d MMMM y", + DateFormat.MEDIUM -> "d MMM y", + DateFormat.SHORT -> "dd/MM/yyyy"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")), + TestCase(root, "", Locale.UK, cldr21 = false, Map( + DateFormat.FULL -> "EEEE, MMMM d, y", + DateFormat.LONG -> "MMMM d, y", + DateFormat.MEDIUM -> "MMM d, y", + DateFormat.SHORT -> "M/d/yy"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")), + TestCase(root, "", Locale.CANADA, cldr21 = true, Map( + DateFormat.FULL -> "EEEE, d MMMM, y", + DateFormat.LONG -> "d MMMM, y", + DateFormat.MEDIUM -> "yyyy-MM-dd", + DateFormat.SHORT -> "yy-MM-dd"), + Map( + DateFormat.FULL -> "h:mm:ss a zzzz", + DateFormat.LONG -> "h:mm:ss a z", + DateFormat.MEDIUM -> "h:mm:ss a", + DateFormat.SHORT -> "h:mm a")), + TestCase(root, "", Locale.CANADA, cldr21 = false, Map( + DateFormat.FULL -> "EEEE, MMMM d, y", + DateFormat.LONG -> "MMMM d, y", + DateFormat.MEDIUM -> "MMM d, y", + DateFormat.SHORT -> "y-MM-dd"), + Map( + DateFormat.FULL -> "h:mm:ss a zzzz", + DateFormat.LONG -> "h:mm:ss a z", + DateFormat.MEDIUM -> "h:mm:ss a", + DateFormat.SHORT -> "h:mm a")), + TestCase(root, "", Locale.CANADA_FRENCH, cldr21 = true, Map( // JVM + DateFormat.FULL -> "EEEE d MMMM y", + DateFormat.LONG -> "d MMMM y", + DateFormat.MEDIUM -> "yyyy-MM-dd", + DateFormat.SHORT -> "yy-MM-dd"), + Map( + DateFormat.FULL -> "HH 'h' mm 'min' ss 's' zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")), + TestCase(root, "", Locale.CANADA_FRENCH, cldr21 = false, Map( // JS + DateFormat.FULL -> "EEEE d MMMM y", + DateFormat.LONG -> "d MMMM y", + DateFormat.MEDIUM -> "d MMM y", + DateFormat.SHORT -> "yy-MM-dd"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")) + ) + + // Test cases by language tag where the JVM gives the same asJS + val localesByTag = List( + TestCase(bn, "bn", Locale.ROOT, cldr21 = true, Map( // JVM + DateFormat.FULL -> "EEEE, d MMMM, y", + DateFormat.LONG -> "d MMMM, y", + DateFormat.MEDIUM -> "d MMM, y", + DateFormat.SHORT -> "d/M/yy"), + Map( + DateFormat.FULL -> "h:mm:ss a zzzz", + DateFormat.LONG -> "h:mm:ss a z", + DateFormat.MEDIUM -> "h:mm:ss a", + DateFormat.SHORT -> "h:mm a")), + TestCase(lv, "lv", Locale.ROOT, cldr21 = false, Map( // JS + DateFormat.FULL -> "EEEE, y. 'gada' d. MMMM", + DateFormat.LONG -> "y. 'gada' d. MMMM", + DateFormat.MEDIUM -> "y. 'gada' d. MMM", + DateFormat.SHORT -> "dd.MM.yy"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")) + ) + + // Test cases by language tag where the JVM differs from JS + val localesByTagDiff = List( + TestCase(af, "af", Locale.ROOT, cldr21 = true, Map( // JVM + DateFormat.FULL -> "EEEE dd MMMM y", + DateFormat.LONG -> "dd MMMM y", + DateFormat.MEDIUM -> "dd MMM y", + DateFormat.SHORT -> "yyyy-MM-dd"), + Map( + DateFormat.FULL -> "h:mm:ss a zzzz", + DateFormat.LONG -> "h:mm:ss a z", + DateFormat.MEDIUM -> "h:mm:ss a", + DateFormat.SHORT -> "h:mm a")), + TestCase(af, "af", Locale.ROOT, cldr21 = false, Map( // JS + DateFormat.FULL -> "EEEE, dd MMMM y", + DateFormat.LONG -> "dd MMMM y", + DateFormat.MEDIUM -> "dd MMM y", + DateFormat.SHORT -> "y-MM-dd"), + Map( + DateFormat.FULL -> "h:mm:ss a zzzz", + DateFormat.LONG -> "h:mm:ss a z", + DateFormat.MEDIUM -> "h:mm:ss a", + DateFormat.SHORT -> "h:mm a")), + TestCase(az, "az", Locale.ROOT, cldr21 = true, Map( // JVM + DateFormat.FULL -> "EEEE, d, MMMM, y", + DateFormat.LONG -> "d MMMM , y", + DateFormat.MEDIUM -> "d MMM, y", + DateFormat.SHORT -> "yyyy-MM-dd"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")), + TestCase(az, "az", Locale.ROOT, cldr21 = false, Map( // JS + DateFormat.FULL -> "d MMMM y, EEEE", + DateFormat.LONG -> "d MMMM y", + DateFormat.MEDIUM -> "d MMM y", + DateFormat.SHORT -> "dd.MM.yy"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")), + TestCase(az_Cyrl, "az-Cyrl", Locale.ROOT, cldr21 = true, Map( // JVM + DateFormat.FULL -> "EEEE, d, MMMM, y", + DateFormat.LONG -> "d MMMM , y", + DateFormat.MEDIUM -> "d MMM, y", + DateFormat.SHORT -> "yyyy-MM-dd"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")), + TestCase(az_Cyrl, "az-Cyrl", Locale.ROOT, cldr21 = false, Map( // JS + DateFormat.FULL -> "EEEE, d, MMMM, y", + DateFormat.LONG -> "d MMMM, y", + DateFormat.MEDIUM -> "d MMM, y", + DateFormat.SHORT -> "dd.MM.yy"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")), + TestCase(es_CL, "es-CL", Locale.ROOT, cldr21 = true, Map( // JVM + DateFormat.FULL -> "EEEE, d 'de' MMMM 'de' y", + DateFormat.LONG -> "d 'de' MMMM 'de' y", + DateFormat.MEDIUM -> "dd-MM-yyyy", + DateFormat.SHORT -> "dd-MM-yy"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "H:mm:ss z", + DateFormat.MEDIUM -> "H:mm:ss", + DateFormat.SHORT -> "H:mm")), + TestCase(es_CL, "es-CL", Locale.ROOT, cldr21 = false, Map( // JS + DateFormat.FULL -> "EEEE, d 'de' MMMM 'de' y", + DateFormat.LONG -> "d 'de' MMMM 'de' y", + DateFormat.MEDIUM -> "dd-MM-y", + DateFormat.SHORT -> "dd-MM-yy"), + Map( + DateFormat.FULL -> "H:mm:ss (zzzz)", + DateFormat.LONG -> "H:mm:ss z", + DateFormat.MEDIUM -> "H:mm:ss", + DateFormat.SHORT -> "H:mm")), + TestCase(it_CH, "it-CH", Locale.ROOT, cldr21 = true, Map( // JVM + DateFormat.FULL -> "EEEE, d MMMM y", + DateFormat.LONG -> "d MMMM y", + DateFormat.MEDIUM -> "d-MMM-y", + DateFormat.SHORT -> "dd.MM.yy"), + Map( + DateFormat.FULL -> "HH.mm:ss 'h' zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")), + TestCase(it_CH, "it-CH", Locale.ROOT, cldr21 = false, Map( // JS + DateFormat.FULL -> "EEEE, d MMMM y", + DateFormat.LONG -> "d MMMM y", + DateFormat.MEDIUM -> "d MMM y", + DateFormat.SHORT -> "dd.MM.yy"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")), + TestCase(zh, "zh", Locale.ROOT, cldr21 = true, Map( // JVM + DateFormat.FULL -> "y年M月d日EEEE", + DateFormat.LONG -> "y年M月d日", + DateFormat.MEDIUM -> "yyyy-M-d", + DateFormat.SHORT -> "yy-M-d"), + Map( + DateFormat.FULL -> "zzzzah时mm分ss秒", + DateFormat.LONG -> "zah时mm分ss秒", + DateFormat.MEDIUM -> "ah:mm:ss", + DateFormat.SHORT -> "ah:mm")), + TestCase(zh, "zh", Locale.ROOT, cldr21 = false, Map( // JS + DateFormat.FULL -> "y年M月d日EEEE", + DateFormat.LONG -> "y年M月d日", + DateFormat.MEDIUM -> "y年M月d日", + DateFormat.SHORT -> "y/M/d"), + Map( + DateFormat.FULL -> "zzzz ah:mm:ss", + DateFormat.LONG -> "z ah:mm:ss", + DateFormat.MEDIUM -> "ah:mm:ss", + DateFormat.SHORT -> "ah:mm")), + TestCase(zh_Hant, "zh-Hant", Locale.ROOT, cldr21 = true, Map( // JVM + DateFormat.FULL -> "y年M月d日EEEE", + DateFormat.LONG -> "y年M月d日", + DateFormat.MEDIUM -> "yyyy/M/d", + DateFormat.SHORT -> "y/M/d"), + Map( + DateFormat.FULL -> "zzzzah時mm分ss秒", + DateFormat.LONG -> "zah時mm分ss秒", + DateFormat.MEDIUM -> "ah:mm:ss", + DateFormat.SHORT -> "ah:mm")), + TestCase(zh_Hant, "zh-Hant", Locale.ROOT, cldr21 = false, Map( // JS + DateFormat.FULL -> "y年M月d日 EEEE", + DateFormat.LONG -> "y年M月d日", + DateFormat.MEDIUM -> "y年M月d日", + DateFormat.SHORT -> "y/M/d"), + Map( + DateFormat.FULL -> "ah:mm:ss [zzzz]", + DateFormat.LONG -> "ah:mm:ss [z]", + DateFormat.MEDIUM -> "ah:mm:ss", + DateFormat.SHORT -> "ah:mm")), + TestCase(ar, "ar", Locale.ROOT, cldr21 = true, Map( // JVM + DateFormat.FULL -> "EEEE، d MMMM، y", + DateFormat.LONG -> "d MMMM، y", + DateFormat.MEDIUM -> "dd‏/MM‏/yyyy", + DateFormat.SHORT -> "d‏/M‏/yyyy"), + Map( + DateFormat.FULL -> "zzzz h:mm:ss a", + DateFormat.LONG -> "z h:mm:ss a", + DateFormat.MEDIUM -> "h:mm:ss a", + DateFormat.SHORT -> "h:mm a")), + TestCase(ar, "ar", Locale.ROOT, cldr21 = false, Map( // JS + DateFormat.FULL -> "EEEE، d MMMM، y", + DateFormat.LONG -> "d MMMM، y", + DateFormat.MEDIUM -> "dd‏/MM‏/y", + DateFormat.SHORT -> "d‏/M‏/y"), + Map( + DateFormat.FULL -> "h:mm:ss a zzzz", + DateFormat.LONG -> "h:mm:ss a z", + DateFormat.MEDIUM -> "h:mm:ss a", + DateFormat.SHORT -> "h:mm a")), + TestCase(fa, "fa", Locale.ROOT, cldr21 = true, Map( // JVM + DateFormat.FULL -> "EEEE d MMMM y", + DateFormat.LONG -> "d MMMM y", + DateFormat.MEDIUM -> "d MMM y", + DateFormat.SHORT -> "yyyy/M/d"), + Map( + DateFormat.FULL -> "H:mm:ss (zzzz)", + DateFormat.LONG -> "H:mm:ss (z)", + DateFormat.MEDIUM -> "H:mm:ss", + DateFormat.SHORT -> "H:mm")), + TestCase(fa, "fa", Locale.ROOT, cldr21 = false, Map( // JS + DateFormat.FULL -> "EEEE d MMMM y", + DateFormat.LONG -> "d MMMM y", + DateFormat.MEDIUM -> "d MMM y", + DateFormat.SHORT -> "y/M/d"), + Map( + DateFormat.FULL -> "H:mm:ss (zzzz)", + DateFormat.LONG -> "H:mm:ss (z)", + DateFormat.MEDIUM -> "H:mm:ss", + DateFormat.SHORT -> "H:mm")), + TestCase(fi_FI, "fi-FI", Locale.ROOT, cldr21 = true, Map( // JVM + DateFormat.FULL -> "EEEE, d. MMMM y", + DateFormat.LONG -> "d. MMMM y", + DateFormat.MEDIUM -> "d.M.yyyy", + DateFormat.SHORT -> "d.M.yyyy"), + Map( + DateFormat.FULL -> "H.mm.ss zzzz", + DateFormat.LONG -> "H.mm.ss z", + DateFormat.MEDIUM -> "H.mm.ss", + DateFormat.SHORT -> "H.mm")), + TestCase(fi_FI, "fi-FI", Locale.ROOT, cldr21 = false, Map( // JS + DateFormat.FULL -> "cccc d. MMMM y", + DateFormat.LONG -> "d. MMMM y", + DateFormat.MEDIUM -> "d.M.y", + DateFormat.SHORT -> "d.M.y"), + Map( + DateFormat.FULL -> "H.mm.ss zzzz", + DateFormat.LONG -> "H.mm.ss z", + DateFormat.MEDIUM -> "H.mm.ss", + DateFormat.SHORT -> "H.mm")), + TestCase(ka, "ka", Locale.ROOT, cldr21 = true, Map( // JVM + DateFormat.FULL -> "EEEE, y MMMM dd", + DateFormat.LONG -> "y MMMM d", + DateFormat.MEDIUM -> "y MMM d", + DateFormat.SHORT -> "yyyy-MM-dd"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")), + TestCase(ka, "ka", Locale.ROOT, cldr21 = false, Map( // JS + DateFormat.FULL -> "EEEE, dd MMMM, y", + DateFormat.LONG -> "d MMMM, y", + DateFormat.MEDIUM -> "d MMM. y", + DateFormat.SHORT -> "dd.MM.yy"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")), + TestCase(my, "my", Locale.ROOT, cldr21 = true, Map( // JVM + DateFormat.FULL -> "EEEE, y MMMM dd", + DateFormat.LONG -> "y MMMM d", + DateFormat.MEDIUM -> "y MMM d", + DateFormat.SHORT -> "yy/MM/dd"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")), + TestCase(my, "my", Locale.ROOT, cldr21 = false, Map( // JS + DateFormat.FULL -> "EEEE၊ dd MMMM y", + DateFormat.LONG -> "d MMMM y", + DateFormat.MEDIUM -> "d MMM y", + DateFormat.SHORT -> "dd-MM-yy"), + Map( + DateFormat.FULL -> "HH:mm:ss zzzz", + DateFormat.LONG -> "HH:mm:ss z", + DateFormat.MEDIUM -> "HH:mm:ss", + DateFormat.SHORT -> "HH:mm")), + TestCase(ru_RU, "ru-RU", Locale.ROOT, cldr21 = true, Map( // JVM + DateFormat.FULL -> "EEEE, d MMMM y\u00A0'г'.", + DateFormat.LONG -> "d MMMM y\u00A0'г'.", + DateFormat.MEDIUM -> "dd.MM.yyyy", + DateFormat.SHORT -> "dd.MM.yy"), + Map( + DateFormat.FULL -> "H:mm:ss zzzz", + DateFormat.LONG -> "H:mm:ss z", + DateFormat.MEDIUM -> "H:mm:ss", + DateFormat.SHORT -> "H:mm")), + TestCase(ru_RU, "ru-RU", Locale.ROOT, cldr21 = false, Map( // JS + DateFormat.FULL -> "EEEE, d MMMM y 'г'.", + DateFormat.LONG -> "d MMMM y 'г'.", + DateFormat.MEDIUM -> "d MMM y 'г'.", + DateFormat.SHORT -> "dd.MM.yy"), + Map( + DateFormat.FULL -> "H:mm:ss zzzz", + DateFormat.LONG -> "H:mm:ss z", + DateFormat.MEDIUM -> "H:mm:ss", + DateFormat.SHORT -> "H:mm")) + ) + + @Test def test_standard_locales(): Unit = { + stdLocales.foreach { tc => + for { + df <- tc.dateFormats + } yield { + assertEquals(df._2, DateFormat.getDateInstance(df._1, tc.l).asInstanceOf[SimpleDateFormat].toPattern()) + } + + for { + tf <- tc.timeFormats + } yield { + assertEquals(tf._2, DateFormat.getTimeInstance(tf._1, tc.l).asInstanceOf[SimpleDateFormat].toPattern()) + } + + for { + df <- tc.dateFormats + tf <- tc.timeFormats + } yield { + assertEquals(s"${df._2} ${tf._2}", DateFormat.getDateTimeInstance(df._1, tf._1, tc.l).asInstanceOf[SimpleDateFormat].toPattern()) + } + } + } + + @Test def test_standard_locales_diff(): Unit = { + stdLocalesDiff.filter(tc => (Platform.executingInJVM && tc.cldr21) || (!Platform.executingInJVM && !tc.cldr21)).foreach { tc => + for { + df <- tc.dateFormats + } yield { + assertEquals(df._2, DateFormat.getDateInstance(df._1, tc.l).asInstanceOf[SimpleDateFormat].toPattern()) + } + + for { + tf <- tc.timeFormats + } yield { + assertEquals(tf._2, DateFormat.getTimeInstance(tf._1, tc.l).asInstanceOf[SimpleDateFormat].toPattern()) + } + + for { + df <- tc.dateFormats + tf <- tc.timeFormats + } yield { + assertEquals(s"${df._2} ${tf._2}", DateFormat.getDateTimeInstance(df._1, tf._1, tc.l).asInstanceOf[SimpleDateFormat].toPattern()) + } + } + } + + @Test def test_extra_locales(): Unit = { + localesByTag.foreach { tc => + if (!Platform.executingInJVM) { + LocaleRegistry.installLocale(tc.ldml) + } + val locale = Locale.forLanguageTag(tc.tag) + + for { + df <- tc.dateFormats + } yield { + assertEquals(df._2, DateFormat.getDateInstance(df._1, locale).asInstanceOf[SimpleDateFormat].toPattern()) + } + + for { + tf <- tc.timeFormats + } yield { + assertEquals(tf._2, DateFormat.getTimeInstance(tf._1, locale).asInstanceOf[SimpleDateFormat].toPattern()) + } + + for { + df <- tc.dateFormats + tf <- tc.timeFormats + } yield { + assertEquals(s"${df._2} ${tf._2}", DateFormat.getDateTimeInstance(df._1, tf._1, locale).asInstanceOf[SimpleDateFormat].toPattern()) + } + } + } + + @Test def test_extra_locales_diff(): Unit = { + localesByTagDiff.filter(tc => (Platform.executingInJVM && tc.cldr21) || (!Platform.executingInJVM && !tc.cldr21)).foreach { tc => + if (!Platform.executingInJVM) { + LocaleRegistry.installLocale(tc.ldml) + } + val locale = Locale.forLanguageTag(tc.tag) + + for { + df <- tc.dateFormats + } yield { + assertEquals(df._2, DateFormat.getDateInstance(df._1, locale).asInstanceOf[SimpleDateFormat].toPattern()) + } + + for { + tf <- tc.timeFormats + } yield { + assertEquals(tf._2, DateFormat.getTimeInstance(tf._1, locale).asInstanceOf[SimpleDateFormat].toPattern()) + } + + for { + df <- tc.dateFormats + tf <- tc.timeFormats + } yield { + if (!tc.cldr21) { + assertEquals(s"${df._2} ${tf._2}", DateFormat.getDateTimeInstance(df._1, tf._1, locale).asInstanceOf[SimpleDateFormat].toPattern()) + } + } + } + } +} diff --git a/testSuite/shared/src/test/scala/testsuite/javalib/text/DecimalFormatTest.scala b/testSuite/shared/src/test/scala/testsuite/javalib/text/DecimalFormatTest.scala new file mode 100644 index 00000000..34122ca8 --- /dev/null +++ b/testSuite/shared/src/test/scala/testsuite/javalib/text/DecimalFormatTest.scala @@ -0,0 +1,14 @@ +package testsuite.javalib.text + +import java.text.{DecimalFormat, DecimalFormatSymbols} + +import org.junit.Assert._ +import org.junit.Test + +class DecimalFormatTest { + @Test def test_constructor(): Unit = { + val f = new DecimalFormat("##0.#####E0") + assertEquals("##0.#####E0", f.toPattern) + assertEquals(DecimalFormatSymbols.getInstance(), f.getDecimalFormatSymbols()) + } +} diff --git a/testSuite/shared/src/test/scala/testsuite/javalib/text/NumberFormatTest.scala b/testSuite/shared/src/test/scala/testsuite/javalib/text/NumberFormatTest.scala new file mode 100644 index 00000000..2bf46a3a --- /dev/null +++ b/testSuite/shared/src/test/scala/testsuite/javalib/text/NumberFormatTest.scala @@ -0,0 +1,13 @@ +package testsuite.javalib.text + +import java.text.NumberFormat + +import org.junit.Test +import org.junit.Assert._ + +class NumberFormatTest { + @Test def test_constants(): Unit = { + assertEquals(0, NumberFormat.INTEGER_FIELD) + assertEquals(1, NumberFormat.FRACTION_FIELD) + } +}