From 7fb4b7bcf6f7f95cfea8a39359a10c368439ff28 Mon Sep 17 00:00:00 2001 From: Travis Keep Date: Thu, 20 Feb 2014 22:41:13 +0000 Subject: [PATCH] ICU-10640 Fix various bugs around duration formatting e.g 3:45:23 X-SVN-Rev: 35186 --- icu4c/source/i18n/measfmt.cpp | 95 +++++++++++++++++----- icu4c/source/test/intltest/measfmttest.cpp | 24 +++++- 2 files changed, 97 insertions(+), 22 deletions(-) diff --git a/icu4c/source/i18n/measfmt.cpp b/icu4c/source/i18n/measfmt.cpp index b3be641336c..b1e05cec3e5 100644 --- a/icu4c/source/i18n/measfmt.cpp +++ b/icu4c/source/i18n/measfmt.cpp @@ -385,31 +385,36 @@ static int32_t toHMS( if (U_FAILURE(status)) { return 0; } - int32_t count = 0; for (int32_t i = 0; i < measureCount; ++i) { if (measures[i].getUnit() == *hourUnit) { - if ((result & 1) == 0) { - ++count; - } else { + // hour must come first + if (result >= 1) { return 0; } hms[0] = measures[i].getNumber(); + if (hms[0].getDouble() < 0.0) { + return 0; + } result |= 1; } else if (measures[i].getUnit() == *minuteUnit) { - if ((result & 2) == 0) { - ++count; - } else { + // minute must come after hour + if (result >= 2) { return 0; } hms[1] = measures[i].getNumber(); + if (hms[1].getDouble() < 0.0) { + return 0; + } result |= 2; } else if (measures[i].getUnit() == *secondUnit) { - if ((result & 4) == 0) { - ++count; - } else { + // second must come after hour and minute + if (result >= 4) { return 0; } hms[2] = measures[i].getNumber(); + if (hms[2].getDouble() < 0.0) { + return 0; + } result |= 4; } else { return 0; @@ -734,9 +739,9 @@ UnicodeString &MeasureFormat::formatNumeric( return appendTo; } UDate millis = - (UDate) (((hms[0].getDouble(status) * 60.0 - + hms[1].getDouble(status)) * 60.0 - + hms[2].getDouble(status)) * 1000.0); + (UDate) (((uprv_trunc(hms[0].getDouble(status)) * 60.0 + + uprv_trunc(hms[1].getDouble(status))) * 60.0 + + uprv_trunc(hms[2].getDouble(status))) * 1000.0); switch (bitMap) { case 5: // hs case 7: // hms @@ -774,9 +779,24 @@ UnicodeString &MeasureFormat::formatNumeric( return appendTo; } +static void appendRange( + const UnicodeString &src, + int32_t start, + int32_t end, + UnicodeString &dest) { + dest.append(src, start, end - start); +} + +static void appendRange( + const UnicodeString &src, + int32_t end, + UnicodeString &dest) { + dest.append(src, end, src.length() - end); +} + UnicodeString &MeasureFormat::formatNumeric( - UDate date, - const DateFormat &dateFmt, + UDate date, // Time since epoch 1:30:00 would be 5400000 + const DateFormat &dateFmt, // h:mm, m:ss, or h:mm:ss UDateFormatField smallestField, const Formattable &smallestAmount, UnicodeString &appendTo, @@ -784,20 +804,53 @@ UnicodeString &MeasureFormat::formatNumeric( if (U_FAILURE(status)) { return appendTo; } + // Format the smallest amount with this object's NumberFormat UnicodeString smallestAmountFormatted; + + // We keep track of the integer part of smallest amount so that + // we can replace it later so that we get '0:00:09.3' instead of + // '0:00:9.3' + FieldPosition intFieldPosition(UNUM_INTEGER_FIELD); (*numberFormat)->format( - smallestAmount, smallestAmountFormatted, status); + smallestAmount, smallestAmountFormatted, intFieldPosition, status); + if ( + intFieldPosition.getBeginIndex() == 0 && + intFieldPosition.getEndIndex() == 0) { + status = U_INTERNAL_PROGRAM_ERROR; + return appendTo; + } + + // Format time. draft becomes something like '5:30:45' FieldPosition smallestFieldPosition(smallestField); UnicodeString draft; dateFmt.format(date, draft, smallestFieldPosition, status); + + // If we find field for smallest amount replace it with the formatted + // smallest amount from above taking care to replace the integer part + // with what is in original time. For example, If smallest amount + // is 9.35s and the formatted time is 0:00:09 then 9.35 becomes 09.35 + // and replacing yields 0:00:09.35 if (smallestFieldPosition.getBeginIndex() != 0 || - smallestFieldPosition.getEndIndex() != 0) { - appendTo.append(draft, 0, smallestFieldPosition.getBeginIndex()); - appendTo.append(smallestAmountFormatted); - appendTo.append( + smallestFieldPosition.getEndIndex() != 0) { + appendRange(draft, 0, smallestFieldPosition.getBeginIndex(), appendTo); + appendRange( + smallestAmountFormatted, + 0, + intFieldPosition.getBeginIndex(), + appendTo); + appendRange( + draft, + smallestFieldPosition.getBeginIndex(), + smallestFieldPosition.getEndIndex(), + appendTo); + appendRange( + smallestAmountFormatted, + intFieldPosition.getEndIndex(), + appendTo); + appendRange( draft, smallestFieldPosition.getEndIndex(), - draft.length()); + appendTo); } else { appendTo.append(draft); } diff --git a/icu4c/source/test/intltest/measfmttest.cpp b/icu4c/source/test/intltest/measfmttest.cpp index dcb25920e28..64013939b53 100644 --- a/icu4c/source/test/intltest/measfmttest.cpp +++ b/icu4c/source/test/intltest/measfmttest.cpp @@ -314,10 +314,19 @@ void MeasureFormatTest::TestFormatPeriodEn() { Measure(5.0, MeasureUnit::createHour(status), status), Measure(17.0, MeasureUnit::createMinute(status), status) }; + Measure t_neg5h_17m[] = { + Measure(-5.0, MeasureUnit::createHour(status), status), + Measure(17.0, MeasureUnit::createMinute(status), status) + }; Measure t_19m_28s[] = { Measure(19.0, MeasureUnit::createMinute(status), status), Measure(28.0, MeasureUnit::createSecond(status), status) }; + Measure t_0h_0m_9s[] = { + Measure(0.0, MeasureUnit::createHour(status), status), + Measure(0.0, MeasureUnit::createMinute(status), status), + Measure(9.0, MeasureUnit::createSecond(status), status) + }; Measure t_0h_0m_17s[] = { Measure(0.0, MeasureUnit::createHour(status), status), Measure(0.0, MeasureUnit::createMinute(status), status), @@ -327,6 +336,16 @@ void MeasureFormatTest::TestFormatPeriodEn() { Measure(6.0, MeasureUnit::createHour(status), status), Measure(56.92, MeasureUnit::createMinute(status), status) }; + Measure t_3h_4s_5m[] = { + Measure(3.0, MeasureUnit::createHour(status), status), + Measure(4.0, MeasureUnit::createSecond(status), status), + Measure(5.0, MeasureUnit::createMinute(status), status) + }; + Measure t_6_7h_56_92m[] = { + Measure(6.7, MeasureUnit::createHour(status), status), + Measure(56.92, MeasureUnit::createMinute(status), status) + }; + Measure t_3h_5h[] = { Measure(3.0, MeasureUnit::createHour(status), status), Measure(5.0, MeasureUnit::createHour(status), status) @@ -367,10 +386,13 @@ void MeasureFormatTest::TestFormatPeriodEn() { {t_1h_23_5m, LENGTHOF(t_1h_23_5m), "1:23.5"}, {t_1h_0m_23s, LENGTHOF(t_1h_0m_23s), "1:00:23"}, {t_5h_17m, LENGTHOF(t_5h_17m), "5:17"}, + {t_neg5h_17m, LENGTHOF(t_neg5h_17m), "-5h 17m"}, {t_19m_28s, LENGTHOF(t_19m_28s), "19:28"}, {t_2y_5M_3w_4d, LENGTHOF(t_2y_5M_3w_4d), "2y 5m 3w 4d"}, - {t_0h_0m_17s, LENGTHOF(t_0h_0m_17s), "0:00:17"}, + {t_0h_0m_9s, LENGTHOF(t_0h_0m_9s), "0:00:09"}, {t_6h_56_92m, LENGTHOF(t_6h_56_92m), "6:56.92"}, + {t_6_7h_56_92m, LENGTHOF(t_6_7h_56_92m), "6:56.92"}, + {t_3h_4s_5m, LENGTHOF(t_3h_4s_5m), "3h 4s 5m"}, {t_3h_5h, LENGTHOF(t_3h_5h), "3h 5h"}}; ExpectedResult fullDataDe[] = { -- 2.49.0