diff --git a/common/supplemental/units.xml b/common/supplemental/units.xml
index ebaa8e9d08f..33d6d68d466 100644
--- a/common/supplemental/units.xml
+++ b/common/supplemental/units.xml
@@ -255,7 +255,7 @@ For terms of use, see http://www.unicode.org/copyright.html
-
+
diff --git a/tools/cldr-code/src/main/java/org/unicode/cldr/util/Rational.java b/tools/cldr-code/src/main/java/org/unicode/cldr/util/Rational.java
index 98c3521689b..30a0ebdb456 100644
--- a/tools/cldr-code/src/main/java/org/unicode/cldr/util/Rational.java
+++ b/tools/cldr-code/src/main/java/org/unicode/cldr/util/Rational.java
@@ -330,6 +330,10 @@ public static Rational of(BigDecimal bigDecimal) {
}
}
+ public static Rational of(double doubleValue) {
+ return of(new BigDecimal(doubleValue));
+ }
+
public enum FormatStyle {
/**
* Simple numerator / denominator, plain BigInteger.toString(), dropping " / 1".
@@ -728,4 +732,8 @@ public boolean approximatelyEquals(Rational b, Rational epsilon) {
public boolean approximatelyEquals(Rational b) {
return approximatelyEquals(b, EPSILON);
}
+
+ public boolean approximatelyEquals(Number b) {
+ return approximatelyEquals(Rational.of(b.doubleValue()), EPSILON);
+ }
}
diff --git a/tools/cldr-code/src/main/java/org/unicode/cldr/util/UnitConverter.java b/tools/cldr-code/src/main/java/org/unicode/cldr/util/UnitConverter.java
index 694dee2e677..8327307b7a0 100644
--- a/tools/cldr-code/src/main/java/org/unicode/cldr/util/UnitConverter.java
+++ b/tools/cldr-code/src/main/java/org/unicode/cldr/util/UnitConverter.java
@@ -1825,19 +1825,27 @@ public Set getSystemsEnum(String unit) {
UnitId id = createUnitId(unit);
// we walk through all the units in the numerator and denominator, and keep the
- // *intersection* of
- // the units. So {ussystem} and {ussystem, uksystem} => ussystem
- // Special case: {dmetric} intersect {metric} => {dmetric}. We do that by adding dmetric to
- // any set with metric, then removing dmetric if there is a metric
+ // *intersection* of the units.
+ // So {ussystem} and {ussystem, uksystem} => ussystem
+ // Special case: {metric_adjacent} intersect {metric} => {metric_adjacent}.
+ // We do that by adding metric_adjacent to any set with metric,
+ // then removing metric_adjacent if there is a metric.
+ // Same for si_acceptable.
main:
for (Map unitsToPowers :
Arrays.asList(id.denUnitsToPowers, id.numUnitsToPowers)) {
for (String subunit : unitsToPowers.keySet()) {
subunit = UnitConverter.stripPrefix(subunit, null);
Set systems = new TreeSet<>(sourceToSystems.get(subunit));
+ if (systems.contains(UnitSystem.metric)) {
+ systems.add(UnitSystem.metric_adjacent);
+ }
+ if (systems.contains(UnitSystem.si)) {
+ systems.add(UnitSystem.si_acceptable);
+ }
if (result == null) {
- result = systems;
+ result = systems; // first setting
} else {
result.retainAll(systems);
}
@@ -1846,9 +1854,17 @@ public Set getSystemsEnum(String unit) {
}
}
}
- return result == null || result.isEmpty()
- ? ImmutableSet.of(UnitSystem.other)
- : ImmutableSet.copyOf(EnumSet.copyOf(result));
+ if (result == null || result.isEmpty()) {
+ return ImmutableSet.of(UnitSystem.other);
+ }
+ if (result.contains(UnitSystem.metric)) {
+ result.remove(UnitSystem.metric_adjacent);
+ }
+ if (result.contains(UnitSystem.si)) {
+ result.remove(UnitSystem.si_acceptable);
+ }
+
+ return ImmutableSet.copyOf(EnumSet.copyOf(result)); // the enum is to sort
}
// private void addSystems(Set result, String subunit) {
diff --git a/tools/cldr-code/src/main/java/org/unicode/cldr/util/UnitPreferences.java b/tools/cldr-code/src/main/java/org/unicode/cldr/util/UnitPreferences.java
index 79ccb40a39c..299af7824ea 100644
--- a/tools/cldr-code/src/main/java/org/unicode/cldr/util/UnitPreferences.java
+++ b/tools/cldr-code/src/main/java/org/unicode/cldr/util/UnitPreferences.java
@@ -265,9 +265,15 @@ public UnitPreference getUnitPreference(
Rational conversion = converter.convert(sourceAmount, sourceUnit, mu, false);
return new UnitPreference(conversion, mu, null);
}
+ String region = resolveRegion(locale);
+
+ return getUnitPreference(sourceAmount, sourceUnit, usage, region);
+ }
+ public UnitPreference getUnitPreference(
+ Rational sourceAmount, String sourceUnit, String usage, String region) {
+ UnitConverter converter = SupplementalDataInfo.getInstance().getUnitConverter();
String quantity = converter.getQuantityFromUnit(sourceUnit, false);
- String baseUnit = converter.getBaseUnitFromQuantity(quantity);
Map> usageToRegionsToInfo =
getFastMap().get(quantity);
@@ -296,7 +302,6 @@ public UnitPreference getUnitPreference(
sourceAmount = Rational.NEGATIVE_ONE;
}
- String region = resolveRegion(locale);
Collection infoList = regionToInfo.get(region);
if (infoList == null || infoList.isEmpty()) {
infoList = regionToInfo.get("001");
diff --git a/tools/cldr-code/src/test/java/org/unicode/cldr/unittest/TestUnits.java b/tools/cldr-code/src/test/java/org/unicode/cldr/unittest/TestUnits.java
index 4a20addbb1b..a2c94c4cd6e 100644
--- a/tools/cldr-code/src/test/java/org/unicode/cldr/unittest/TestUnits.java
+++ b/tools/cldr-code/src/test/java/org/unicode/cldr/unittest/TestUnits.java
@@ -39,10 +39,12 @@
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
+import java.io.UncheckedIOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -4519,17 +4521,104 @@ public void testQuantitiesMissingFromPreferences() {
"There are no explicit preferences for %s, but %s is not metric",
quantity, unit));
}
- Set systems = converter.getSystemsEnum(pref.unit);
+ Set prefSystems = converter.getSystemsEnum(pref.unit);
String errorOrWarningString =
String.format(
"Test default preference is metric: input unit=%s, quantity=%s, pref-unit=%s, systems: %s",
- unit, quantity, pref.unit, systems);
- if (Collections.disjoint(systems, UnitSystem.SiOrMetric)) {
+ unit, quantity, pref.unit, prefSystems);
+ if (Collections.disjoint(prefSystems, UnitSystem.SiOrMetric)) {
+ Set prefSystems2 = converter.getSystemsEnum(pref.unit);
errln(errorOrWarningString);
} else {
logln("OK " + errorOrWarningString);
}
}
}
+
+ public void testUnitPreferencesTest() {
+ try {
+ UnitPreferences prefs = SDI.getUnitPreferences();
+
+ // # Quantity; Usage; Region; Input (r); Input (d); Input Unit; Output (r);
+ // Output (d); Output Unit
+ // Example:
+ // area; default; 001; 1100000; 1100000.0; square-meter;
+ // 11/10; 1.1; square-kilometer
+ // duration; media; 001; 66; 66.0; second; 1; minute; 6;
+ // 6.0; second
+ Files.lines(Path.of(CLDRPaths.TEST_DATA + "units/unitPreferencesTest.txt"))
+ .forEach(
+ line -> {
+ if (line.startsWith("#") || line.isBlank()) {
+ return;
+ }
+ try {
+ List parts = SPLIT_SEMI.splitToList(line);
+ Map highMixed_unit_identifiers =
+ new LinkedHashMap<>();
+ String quantity = parts.get(0);
+ String usage = parts.get(1);
+ String region = parts.get(2);
+ Rational inputRational = Rational.of(parts.get(3));
+ double inputDouble = Double.parseDouble(parts.get(4));
+ String inputUnit = parts.get(5);
+ // account for multi-part output
+ int size = parts.size();
+ // This section has larger elements with integer values
+ for (int i = 6; i < size - 3; i += 2) {
+ highMixed_unit_identifiers.put(
+ parts.get(i + 1), Long.parseLong(parts.get(i)));
+ }
+ Rational expectedValue = Rational.of(parts.get(size - 3));
+ Double expectedValueDouble =
+ Double.parseDouble(parts.get(size - 2));
+ String expectedOutputUnit = parts.get(size - 1);
+
+ // Check that the double values are approximately the same as
+ // the Rational ones
+ assertTrue(
+ String.format(
+ "input rational ~ input double, %s %s",
+ inputRational, inputDouble),
+ inputRational.approximatelyEquals(inputDouble));
+ assertTrue(
+ String.format(
+ "output rational ~ output double, %s %s",
+ expectedValue, expectedValueDouble),
+ expectedValue.approximatelyEquals(expectedValueDouble));
+
+ // check that the quantity is consistent
+ String expectedQuantity =
+ converter.getQuantityFromUnit(inputUnit, false);
+ assertEquals(
+ "Input: Quantity consistency check",
+ expectedQuantity,
+ quantity);
+
+ // TODO handle mixed_unit_identifiers
+ if (!highMixed_unit_identifiers.isEmpty()) {
+ warnln("mixed_unit_identifiers not yet checked: " + line);
+ return;
+ }
+ // check output unit, then value
+ UnitPreference unitPreference =
+ prefs.getUnitPreference(
+ inputRational, inputUnit, usage, region);
+ String actualUnit = unitPreference.unit;
+ assertEquals("Output unit", expectedOutputUnit, actualUnit);
+
+ Rational actualValue =
+ converter.convert(
+ inputRational, inputUnit, actualUnit, false);
+ assertEquals(
+ "Output numeric value", expectedValue, actualValue);
+ } catch (Exception e) {
+ errln(e.getMessage() + "\n\t" + line);
+ }
+ });
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
}