diff --git a/icu4c/source/i18n/units_converter.cpp b/icu4c/source/i18n/units_converter.cpp
index 710363efb65..4858cbd233c 100644
--- a/icu4c/source/i18n/units_converter.cpp
+++ b/icu4c/source/i18n/units_converter.cpp
@@ -490,15 +490,36 @@ Convertibility U_I18N_API extractConvertibility(const MeasureUnitImpl &source,
}
UnitsConverter::UnitsConverter(const MeasureUnitImpl &source, const MeasureUnitImpl &target,
- const ConversionRates &ratesInfo, UErrorCode &status)
+ const ConversionRates &ratesInfo, UErrorCode &status)
: conversionRate_(source.copy(status), target.copy(status)) {
- if (source.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED ||
- target.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) {
+ this->init(ratesInfo, status);
+}
+
+UnitsConverter::UnitsConverter(StringPiece sourceIdentifier, StringPiece targetIdentifier,
+ UErrorCode &status)
+ : conversionRate_(MeasureUnitImpl::forIdentifier(sourceIdentifier, status),
+ MeasureUnitImpl::forIdentifier(targetIdentifier, status)) {
+ if (U_FAILURE(status)) {
+ return;
+ }
+
+ ConversionRates ratesInfo(status);
+ this->init(ratesInfo, status);
+}
+
+void UnitsConverter::init(const ConversionRates &ratesInfo, UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return;
+ }
+
+ if (this->conversionRate_.source.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED ||
+ this->conversionRate_.target.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) {
status = U_INTERNAL_PROGRAM_ERROR;
return;
}
- Convertibility unitsState = extractConvertibility(source, target, ratesInfo, status);
+ Convertibility unitsState = extractConvertibility(this->conversionRate_.source,
+ this->conversionRate_.target, ratesInfo, status);
if (U_FAILURE(status)) return;
if (unitsState == Convertibility::UNCONVERTIBLE) {
status = U_INTERNAL_PROGRAM_ERROR;
@@ -507,11 +528,12 @@ UnitsConverter::UnitsConverter(const MeasureUnitImpl &source, const MeasureUnitI
loadConversionRate(conversionRate_, conversionRate_.source, conversionRate_.target, unitsState,
ratesInfo, status);
+
}
int32_t UnitsConverter::compareTwoUnits(const MeasureUnitImpl &firstUnit,
- const MeasureUnitImpl &secondUnit,
- const ConversionRates &ratesInfo, UErrorCode &status) {
+ const MeasureUnitImpl &secondUnit,
+ const ConversionRates &ratesInfo, UErrorCode &status) {
if (U_FAILURE(status)) {
return 0;
}
@@ -532,8 +554,9 @@ int32_t UnitsConverter::compareTwoUnits(const MeasureUnitImpl &firstUnit,
return 0;
}
- // Represents the conversion factor from the firstUnit to the base unit that specified in the
- // conversion data which is considered as the root of the firstUnit and the secondUnit.
+ // Represents the conversion factor from the firstUnit to the base
+ // unit that specified in the conversion data which is considered as
+ // the root of the firstUnit and the secondUnit.
Factor firstUnitToBase = loadCompoundFactor(firstUnit, ratesInfo, status);
Factor secondUnitToBase = loadCompoundFactor(secondUnit, ratesInfo, status);
diff --git a/icu4c/source/i18n/units_converter.h b/icu4c/source/i18n/units_converter.h
index e8a0caefbea..1d5a3f86c98 100644
--- a/icu4c/source/i18n/units_converter.h
+++ b/icu4c/source/i18n/units_converter.h
@@ -145,6 +145,20 @@ Convertibility U_I18N_API extractConvertibility(const MeasureUnitImpl &source,
*/
class U_I18N_API UnitsConverter : public UMemory {
public:
+ /**
+ * Constructor of `UnitConverter`.
+ * NOTE:
+ * - source and target must be under the same category
+ * - e.g. meter to mile --> both of them are length units.
+ * NOTE:
+ * This constructor creates an instance of `ConversionRates` internally.
+ *
+ * @param sourceIdentifier represents the source unit identifier.
+ * @param targetIdentifier represents the target unit identifier.
+ * @param status
+ */
+ UnitsConverter(StringPiece sourceIdentifier, StringPiece targetIdentifier, UErrorCode &status);
+
/**
* Constructor of `UnitConverter`.
* NOTE:
@@ -191,6 +205,11 @@ class U_I18N_API UnitsConverter : public UMemory {
private:
ConversionRate conversionRate_;
+
+ /**
+ * Initialises the object.
+ */
+ void init(const ConversionRates &ratesInfo, UErrorCode &status);
};
} // namespace units
diff --git a/icu4c/source/test/intltest/units_test.cpp b/icu4c/source/test/intltest/units_test.cpp
index a18e751e470..8e9bfbc75ed 100644
--- a/icu4c/source/test/intltest/units_test.cpp
+++ b/icu4c/source/test/intltest/units_test.cpp
@@ -363,6 +363,30 @@ void UnitsTest::testConverter() {
assertEqualsNear(
UnicodeString("testConverter inverse: ") + testCase.target + " back to " + testCase.source,
testCase.inputValue, converter.convertInverse(testCase.expectedValue), maxDelta);
+
+
+ // TODO: Test UnitsConverter created using CLDR separately.
+ // Test UnitsConverter created by CLDR unit identifiers
+ UnitsConverter converter2(testCase.source, testCase.target, status);
+ if (status.errIfFailureAndReset("UnitsConverter(<%s>, <%s>, ...)", testCase.source,
+ testCase.target)) {
+ continue;
+ }
+
+ maxDelta = 1e-6 * uprv_fabs(testCase.expectedValue);
+ if (testCase.expectedValue == 0) {
+ maxDelta = 1e-12;
+ }
+ assertEqualsNear(UnicodeString("testConverter2: ") + testCase.source + " to " + testCase.target,
+ testCase.expectedValue, converter2.convert(testCase.inputValue), maxDelta);
+
+ maxDelta = 1e-6 * uprv_fabs(testCase.inputValue);
+ if (testCase.inputValue == 0) {
+ maxDelta = 1e-12;
+ }
+ assertEqualsNear(
+ UnicodeString("testConverter2 inverse: ") + testCase.target + " back to " + testCase.source,
+ testCase.inputValue, converter2.convertInverse(testCase.expectedValue), maxDelta);
}
}
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/units/UnitsConverter.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/units/UnitsConverter.java
index f732c2f5c34..9f80f439246 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/units/UnitsConverter.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/units/UnitsConverter.java
@@ -16,6 +16,26 @@ public class UnitsConverter {
private boolean reciprocal;
private BigDecimal offset;
+ /**
+ * Constructor of UnitsConverter
.
+ * NOTE:
+ * - source and target must be under the same category
+ * - e.g. meter to mile --> both of them are length units.
+ *
+ * NOTE:
+ * This constructor creates an instance of UnitsConverter
internally.
+ *
+ * @param sourceIdentifier represents the source unit identifier.
+ * @param targetIdentifier represents the target unit identifier.
+ */
+ public UnitsConverter(String sourceIdentifier, String targetIdentifier) {
+ this(
+ MeasureUnitImpl.forIdentifier(sourceIdentifier),
+ MeasureUnitImpl.forIdentifier(targetIdentifier),
+ new ConversionRates()
+ );
+ }
+
/**
* Constructor of UnitsConverter
.
* NOTE:
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/impl/UnitsTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/impl/UnitsTest.java
index 952ffcdc756..107503a247e 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/impl/UnitsTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/impl/UnitsTest.java
@@ -371,14 +371,14 @@ public class UnitsTest {
@Test
public void testConverter() {
class TestData {
- MeasureUnitImpl source;
- MeasureUnitImpl target;
- BigDecimal input;
- BigDecimal expected;
+ final String sourceIdentifier;
+ final String targetIdentifier;
+ final BigDecimal input;
+ final BigDecimal expected;
- TestData(String source, String target, double input, double expected) {
- this.source = MeasureUnitImpl.UnitsParser.parseForIdentifier(source);
- this.target = MeasureUnitImpl.UnitsParser.parseForIdentifier(target);
+ TestData(String sourceIdentifier, String targetIdentifier, double input, double expected) {
+ this.sourceIdentifier = sourceIdentifier;
+ this.targetIdentifier = targetIdentifier;
this.input = BigDecimal.valueOf(input);
this.expected = BigDecimal.valueOf(expected);
}
@@ -440,23 +440,48 @@ public class UnitsTest {
ConversionRates conversionRates = new ConversionRates();
for (TestData test : tests) {
- UnitsConverter converter = new UnitsConverter(test.source, test.target, conversionRates);
+ MeasureUnitImpl source = MeasureUnitImpl.forIdentifier(test.sourceIdentifier);
+ MeasureUnitImpl target = MeasureUnitImpl.forIdentifier(test.targetIdentifier);
+
+ UnitsConverter converter = new UnitsConverter(source, target, conversionRates);
double maxDelta = 1e-6 * Math.abs(test.expected.doubleValue());
if (test.expected.doubleValue() == 0) {
maxDelta = 1e-12;
}
- assertEquals("testConverter: " + test.source + " to " + test.target,
- test.expected.doubleValue(), converter.convert(test.input).doubleValue(),
- maxDelta);
+ assertEquals("testConverter: " + test.sourceIdentifier + " to " + test.targetIdentifier,
+ test.expected.doubleValue(), converter.convert(test.input).doubleValue(),
+ maxDelta);
maxDelta = 1e-6 * Math.abs(test.input.doubleValue());
if (test.input.doubleValue() == 0) {
maxDelta = 1e-12;
}
- assertEquals("testConverter inverse: " + test.target + " back to " + test.source,
- test.input.doubleValue(), converter.convertInverse(test.expected).doubleValue(),
- maxDelta);
+ assertEquals(
+ "testConverter inverse: " + test.targetIdentifier + " back to " + test.sourceIdentifier,
+ test.input.doubleValue(), converter.convertInverse(test.expected).doubleValue(),
+ maxDelta);
+
+
+ // TODO: Test UnitsConverter created using CLDR separately.
+ // Test UnitsConverter created by CLDR unit identifiers
+ UnitsConverter converter2 = new UnitsConverter(test.sourceIdentifier, test.targetIdentifier);
+
+ maxDelta = 1e-6 * Math.abs(test.expected.doubleValue());
+ if (test.expected.doubleValue() == 0) {
+ maxDelta = 1e-12;
+ }
+ assertEquals("testConverter2: " + test.sourceIdentifier + " to " + test.targetIdentifier,
+ test.expected.doubleValue(), converter2.convert(test.input).doubleValue(),
+ maxDelta);
+
+ maxDelta = 1e-6 * Math.abs(test.input.doubleValue());
+ if (test.input.doubleValue() == 0) {
+ maxDelta = 1e-12;
+ }
+ assertEquals("testConverter2 inverse: " + test.targetIdentifier + " back to " + test.sourceIdentifier,
+ test.input.doubleValue(), converter2.convertInverse(test.expected).doubleValue(),
+ maxDelta);
}
}