1 /********************************************************************
2 * Copyright (c) 2008-2014, International Business Machines Corporation and
3 * others. All Rights Reserved.
4 ********************************************************************/
6 #include "unicode/utypes.h"
8 #if !UCONFIG_NO_FORMATTING
10 #include "unicode/decimfmt.h"
11 #include "unicode/tmunit.h"
12 #include "unicode/tmutamt.h"
13 #include "unicode/tmutfmt.h"
16 #include "unicode/ustring.h"
18 //TODO: put as compilation flag
19 //#define TUFMTTS_DEBUG 1
25 void TimeUnitTest::runIndexedTest( int32_t index
, UBool exec
, const char* &name
, char* /*par*/ ) {
26 if (exec
) logln("TestSuite TimeUnitTest");
28 TESTCASE(0, testBasic
);
30 TESTCASE(2, testGreekWithFallback
);
31 TESTCASE(3, testGreekWithSanitization
);
32 TESTCASE(4, test10219Plurals
);
33 default: name
= ""; break;
37 // This function is more lenient than equals operator as it considers integer 3 hours and
38 // double 3.0 hours to be equal
39 static UBool
tmaEqual(const TimeUnitAmount
& left
, const TimeUnitAmount
& right
) {
40 if (left
.getTimeUnitField() != right
.getTimeUnitField()) {
43 UErrorCode status
= U_ZERO_ERROR
;
44 if (!left
.getNumber().isNumeric() || !right
.getNumber().isNumeric()) {
47 UBool result
= left
.getNumber().getDouble(status
) == right
.getNumber().getDouble(status
);
48 if (U_FAILURE(status
)) {
57 void TimeUnitTest::testBasic() {
58 const char* locales
[] = {"en", "sl", "fr", "zh", "ar", "ru", "zh_Hant", "pa"};
59 for ( unsigned int locIndex
= 0;
60 locIndex
< sizeof(locales
)/sizeof(locales
[0]);
62 UErrorCode status
= U_ZERO_ERROR
;
63 Locale
loc(locales
[locIndex
]);
64 TimeUnitFormat
** formats
= new TimeUnitFormat
*[2];
65 formats
[UTMUTFMT_FULL_STYLE
] = new TimeUnitFormat(loc
, status
);
66 if (!assertSuccess("TimeUnitFormat(full)", status
, TRUE
)) return;
67 formats
[UTMUTFMT_ABBREVIATED_STYLE
] = new TimeUnitFormat(loc
, UTMUTFMT_ABBREVIATED_STYLE
, status
);
68 if (!assertSuccess("TimeUnitFormat(short)", status
)) return;
70 std::cout
<< "locale: " << locales
[locIndex
] << "\n";
72 for (int style
= UTMUTFMT_FULL_STYLE
;
73 style
<= UTMUTFMT_ABBREVIATED_STYLE
;
75 for (TimeUnit::UTimeUnitFields j
= TimeUnit::UTIMEUNIT_YEAR
;
76 j
< TimeUnit::UTIMEUNIT_FIELD_COUNT
;
77 j
= (TimeUnit::UTimeUnitFields
)(j
+1)) {
79 std::cout
<< "time unit: " << j
<< "\n";
81 double tests
[] = {0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 5, 10, 100, 101.35};
82 for (unsigned int i
= 0; i
< sizeof(tests
)/sizeof(tests
[0]); ++i
) {
84 std::cout
<< "number: " << tests
[i
] << "\n";
86 TimeUnitAmount
* source
= new TimeUnitAmount(tests
[i
], j
, status
);
87 if (!assertSuccess("TimeUnitAmount()", status
)) return;
88 UnicodeString formatted
;
89 Formattable formattable
;
90 formattable
.adoptObject(source
);
91 formatted
= ((Format
*)formats
[style
])->format(formattable
, formatted
, status
);
92 if (!assertSuccess("format()", status
)) return;
94 char formatResult
[1000];
95 formatted
.extract(0, formatted
.length(), formatResult
, "UTF-8");
96 std::cout
<< "format result: " << formatResult
<< "\n";
99 ((Format
*)formats
[style
])->parseObject(formatted
, result
, status
);
100 if (!assertSuccess("parseObject()", status
)) return;
101 if (!tmaEqual(*((TimeUnitAmount
*)result
.getObject()), *((TimeUnitAmount
*) formattable
.getObject()))) {
102 dataerrln("No round trip: ");
104 // other style parsing
105 Formattable result_1
;
106 ((Format
*)formats
[1-style
])->parseObject(formatted
, result_1
, status
);
107 if (!assertSuccess("parseObject()", status
)) return;
108 if (!tmaEqual(*((TimeUnitAmount
*)result_1
.getObject()), *((TimeUnitAmount
*) formattable
.getObject()))) {
109 dataerrln("No round trip: ");
114 delete formats
[UTMUTFMT_FULL_STYLE
];
115 delete formats
[UTMUTFMT_ABBREVIATED_STYLE
];
121 void TimeUnitTest::testAPI() {
122 //================= TimeUnit =================
123 UErrorCode status
= U_ZERO_ERROR
;
125 TimeUnit
* tmunit
= TimeUnit::createInstance(TimeUnit::UTIMEUNIT_YEAR
, status
);
126 if (!assertSuccess("TimeUnit::createInstance", status
)) return;
128 TimeUnit
* another
= (TimeUnit
*)tmunit
->clone();
129 TimeUnit
third(*tmunit
);
130 TimeUnit fourth
= third
;
132 assertTrue("orig and clone are equal", (*tmunit
== *another
));
133 assertTrue("copied and assigned are equal", (third
== fourth
));
135 TimeUnit
* tmunit_m
= TimeUnit::createInstance(TimeUnit::UTIMEUNIT_MONTH
, status
);
136 assertTrue("year != month", (*tmunit
!= *tmunit_m
));
138 TimeUnit::UTimeUnitFields field
= tmunit_m
->getTimeUnitField();
139 assertTrue("field of month time unit is month", (field
== TimeUnit::UTIMEUNIT_MONTH
));
141 //===== Interoperability with MeasureUnit ======
142 MeasureUnit
**ptrs
= new MeasureUnit
*[TimeUnit::UTIMEUNIT_FIELD_COUNT
];
144 ptrs
[TimeUnit::UTIMEUNIT_YEAR
] = MeasureUnit::createYear(status
);
145 ptrs
[TimeUnit::UTIMEUNIT_MONTH
] = MeasureUnit::createMonth(status
);
146 ptrs
[TimeUnit::UTIMEUNIT_DAY
] = MeasureUnit::createDay(status
);
147 ptrs
[TimeUnit::UTIMEUNIT_WEEK
] = MeasureUnit::createWeek(status
);
148 ptrs
[TimeUnit::UTIMEUNIT_HOUR
] = MeasureUnit::createHour(status
);
149 ptrs
[TimeUnit::UTIMEUNIT_MINUTE
] = MeasureUnit::createMinute(status
);
150 ptrs
[TimeUnit::UTIMEUNIT_SECOND
] = MeasureUnit::createSecond(status
);
151 if (!assertSuccess("TimeUnit::createInstance", status
)) return;
153 for (TimeUnit::UTimeUnitFields j
= TimeUnit::UTIMEUNIT_YEAR
;
154 j
< TimeUnit::UTIMEUNIT_FIELD_COUNT
;
155 j
= (TimeUnit::UTimeUnitFields
)(j
+1)) {
156 MeasureUnit
*ptr
= TimeUnit::createInstance(j
, status
);
157 if (!assertSuccess("TimeUnit::createInstance", status
)) return;
158 // We have to convert *ptr to a MeasureUnit or else == will fail over
159 // differing types (TimeUnit vs. MeasureUnit).
161 "Time unit should be equal to corresponding MeasureUnit",
162 MeasureUnit(*ptr
) == *ptrs
[j
]);
168 for (int i
= 0; i
< TimeUnit::UTIMEUNIT_FIELD_COUNT
; ++i
) {
174 //================= TimeUnitAmount =================
176 Formattable
formattable((int32_t)2);
177 TimeUnitAmount
tma_long(formattable
, TimeUnit::UTIMEUNIT_DAY
, status
);
178 if (!assertSuccess("TimeUnitAmount(formattable...)", status
)) return;
180 formattable
.setDouble(2);
181 TimeUnitAmount
tma_double(formattable
, TimeUnit::UTIMEUNIT_DAY
, status
);
182 if (!assertSuccess("TimeUnitAmount(formattable...)", status
)) return;
184 formattable
.setDouble(3);
185 TimeUnitAmount
tma_double_3(formattable
, TimeUnit::UTIMEUNIT_DAY
, status
);
186 if (!assertSuccess("TimeUnitAmount(formattable...)", status
)) return;
188 TimeUnitAmount
tma(2, TimeUnit::UTIMEUNIT_DAY
, status
);
189 if (!assertSuccess("TimeUnitAmount(number...)", status
)) return;
191 TimeUnitAmount
tma_h(2, TimeUnit::UTIMEUNIT_HOUR
, status
);
192 if (!assertSuccess("TimeUnitAmount(number...)", status
)) return;
194 TimeUnitAmount
second(tma
);
195 TimeUnitAmount third_tma
= tma
;
196 TimeUnitAmount
* fourth_tma
= (TimeUnitAmount
*)tma
.clone();
198 assertTrue("orig and copy are equal", (second
== tma
));
199 assertTrue("clone and assigned are equal", (third_tma
== *fourth_tma
));
200 assertTrue("different if number diff", (tma_double
!= tma_double_3
));
201 assertTrue("different if number type diff", (tma_double
!= tma_long
));
202 assertTrue("different if time unit diff", (tma
!= tma_h
));
203 assertTrue("same even different constructor", (tma_double
== tma
));
205 assertTrue("getTimeUnitField", (tma
.getTimeUnitField() == TimeUnit::UTIMEUNIT_DAY
));
208 //================= TimeUnitFormat =================
210 TimeUnitFormat
* tmf_en
= new TimeUnitFormat(Locale("en"), status
);
211 if (!assertSuccess("TimeUnitFormat(en...)", status
, TRUE
)) return;
212 TimeUnitFormat
tmf_fr(Locale("fr"), status
);
213 if (!assertSuccess("TimeUnitFormat(fr...)", status
)) return;
215 assertTrue("TimeUnitFormat: en and fr diff", (*tmf_en
!= tmf_fr
));
217 TimeUnitFormat tmf_assign
= *tmf_en
;
218 assertTrue("TimeUnitFormat: orig and assign are equal", (*tmf_en
== tmf_assign
));
220 TimeUnitFormat
tmf_copy(tmf_fr
);
221 assertTrue("TimeUnitFormat: orig and copy are equal", (tmf_fr
== tmf_copy
));
223 TimeUnitFormat
* tmf_clone
= (TimeUnitFormat
*)tmf_en
->clone();
224 assertTrue("TimeUnitFormat: orig and clone are equal", (*tmf_en
== *tmf_clone
));
227 tmf_en
->setLocale(Locale("fr"), status
);
228 if (!assertSuccess("setLocale(fr...)", status
)) return;
230 NumberFormat
* numberFmt
= NumberFormat::createInstance(
231 Locale("fr"), status
);
232 if (!assertSuccess("NumberFormat::createInstance()", status
)) return;
233 tmf_en
->setNumberFormat(*numberFmt
, status
);
234 if (!assertSuccess("setNumberFormat(en...)", status
)) return;
235 assertTrue("TimeUnitFormat: setLocale", (*tmf_en
== tmf_fr
));
239 TimeUnitFormat
* en_long
= new TimeUnitFormat(Locale("en"), UTMUTFMT_FULL_STYLE
, status
);
240 if (!assertSuccess("TimeUnitFormat(en...)", status
)) return;
243 TimeUnitFormat
* en_short
= new TimeUnitFormat(Locale("en"), UTMUTFMT_ABBREVIATED_STYLE
, status
);
244 if (!assertSuccess("TimeUnitFormat(en...)", status
)) return;
247 TimeUnitFormat
* format
= new TimeUnitFormat(status
);
248 format
->setLocale(Locale("zh"), status
);
249 format
->setNumberFormat(*numberFmt
, status
);
250 if (!assertSuccess("TimeUnitFormat(en...)", status
)) return;
256 * Tests for Greek Language.
257 * This tests that requests for short unit names correctly fall back
258 * to long unit names for a locale where the locale data does not
259 * provide short unit names. As of CLDR 1.9, Greek is one such language.
261 void TimeUnitTest::testGreekWithFallback() {
262 UErrorCode status
= U_ZERO_ERROR
;
264 const char* locales
[] = {"el-GR", "el"};
265 TimeUnit::UTimeUnitFields tunits
[] = {TimeUnit::UTIMEUNIT_SECOND
, TimeUnit::UTIMEUNIT_MINUTE
, TimeUnit::UTIMEUNIT_HOUR
, TimeUnit::UTIMEUNIT_DAY
, TimeUnit::UTIMEUNIT_MONTH
, TimeUnit::UTIMEUNIT_YEAR
};
266 UTimeUnitFormatStyle styles
[] = {UTMUTFMT_FULL_STYLE
, UTMUTFMT_ABBREVIATED_STYLE
};
267 const int numbers
[] = {1, 7};
269 const UChar oneSecond
[] = {0x0031, 0x0020, 0x03b4, 0x03b5, 0x03c5, 0x03c4, 0x03b5, 0x03c1, 0x03cc, 0x03bb, 0x03b5, 0x03c0, 0x03c4, 0x03bf, 0};
270 const UChar oneSecondShort
[] = {0x0031, 0x0020, 0x03b4, 0x03b5, 0x03c5, 0x03c4, 0x002e, 0};
271 const UChar oneMinute
[] = {0x0031, 0x0020, 0x03bb, 0x03b5, 0x03c0, 0x03c4, 0x03cc, 0};
272 const UChar oneMinuteShort
[] = {0x0031, 0x0020, 0x03bb, 0x03b5, 0x03c0, 0x002e, 0};
273 const UChar oneHour
[] = {0x0031, 0x0020, 0x03ce, 0x03c1, 0x03b1, 0};
274 const UChar oneDay
[] = {0x0031, 0x0020, 0x03b7, 0x03bc, 0x03ad, 0x03c1, 0x03b1, 0};
275 const UChar oneMonth
[] = {0x0031, 0x0020, 0x03bc, 0x03ae, 0x03bd, 0x03b1, 0x03c2, 0};
276 const UChar oneMonthShort
[] = {0x0031, 0x0020, 0x03bc, 0x03ae, 0x03bd, 0x002e, 0};
277 const UChar oneYear
[] = {0x0031, 0x0020, 0x03ad, 0x03c4, 0x03bf, 0x03c2, 0};
278 const UChar oneYearShort
[] = {0x0031, 0x0020, 0x03ad, 0x03c4, 0x002e, 0};
279 const UChar sevenSeconds
[] = {0x0037, 0x0020, 0x03b4, 0x03b5, 0x03c5, 0x03c4, 0x03b5, 0x03c1, 0x03cc, 0x03bb, 0x03b5, 0x03c0, 0x03c4, 0x03b1, 0};
280 const UChar sevenSecondsShort
[] = {0x0037, 0x0020, 0x03b4, 0x03b5, 0x03c5, 0x03c4, 0x002e, 0};
281 const UChar sevenMinutes
[] = {0x0037, 0x0020, 0x03bb, 0x03b5, 0x03c0, 0x03c4, 0x03ac, 0};
282 const UChar sevenMinutesShort
[] = {0x0037, 0x0020, 0x03bb, 0x03b5, 0x03c0, 0x002e, 0};
283 const UChar sevenHours
[] = {0x0037, 0x0020, 0x03ce, 0x03c1, 0x03b5, 0x03c2, 0};
284 const UChar sevenHoursShort
[] = {0x0037, 0x0020, 0x03ce, 0x03c1, 0x002e, 0};
285 const UChar sevenDays
[] = {0x0037, 0x0020, 0x03b7, 0x03bc, 0x03ad, 0x03c1, 0x03b5, 0x03c2, 0};
286 const UChar sevenMonths
[] = {0x0037, 0x0020, 0x03bc, 0x03ae, 0x03bd, 0x03b5, 0x3c2, 0};
287 const UChar sevenMonthsShort
[] = {0x0037, 0x0020, 0x03bc, 0x03ae, 0x03bd, 0x002e, 0};
288 const UChar sevenYears
[] = {0x0037, 0x0020, 0x03ad, 0x03c4, 0x03b7, 0};
289 const UChar sevenYearsShort
[] = {0x0037, 0x0020, 0x03ad, 0x03c4, 0x002e, 0};
291 const UnicodeString
oneSecondStr(oneSecond
);
292 const UnicodeString
oneSecondShortStr(oneSecondShort
);
293 const UnicodeString
oneMinuteStr(oneMinute
);
294 const UnicodeString
oneMinuteShortStr(oneMinuteShort
);
295 const UnicodeString
oneHourStr(oneHour
);
296 const UnicodeString
oneDayStr(oneDay
);
297 const UnicodeString
oneMonthStr(oneMonth
);
298 const UnicodeString
oneMonthShortStr(oneMonthShort
);
299 const UnicodeString
oneYearStr(oneYear
);
300 const UnicodeString
oneYearShortStr(oneYearShort
);
301 const UnicodeString
sevenSecondsStr(sevenSeconds
);
302 const UnicodeString
sevenSecondsShortStr(sevenSecondsShort
);
303 const UnicodeString
sevenMinutesStr(sevenMinutes
);
304 const UnicodeString
sevenMinutesShortStr(sevenMinutesShort
);
305 const UnicodeString
sevenHoursStr(sevenHours
);
306 const UnicodeString
sevenHoursShortStr(sevenHoursShort
);
307 const UnicodeString
sevenDaysStr(sevenDays
);
308 const UnicodeString
sevenMonthsStr(sevenMonths
);
309 const UnicodeString
sevenMonthsShortStr(sevenMonthsShort
);
310 const UnicodeString
sevenYearsStr(sevenYears
);
311 const UnicodeString
sevenYearsShortStr(sevenYearsShort
);
313 const UnicodeString expected
[] = {
314 oneSecondStr
, oneMinuteStr
, oneHourStr
, oneDayStr
, oneMonthStr
, oneYearStr
,
315 oneSecondShortStr
, oneMinuteShortStr
, oneHourStr
, oneDayStr
, oneMonthShortStr
, oneYearShortStr
,
316 sevenSecondsStr
, sevenMinutesStr
, sevenHoursStr
, sevenDaysStr
, sevenMonthsStr
, sevenYearsStr
,
317 sevenSecondsShortStr
, sevenMinutesShortStr
, sevenHoursShortStr
, sevenDaysStr
, sevenMonthsShortStr
, sevenYearsShortStr
,
319 oneSecondStr
, oneMinuteStr
, oneHourStr
, oneDayStr
, oneMonthStr
, oneYearStr
,
320 oneSecondShortStr
, oneMinuteShortStr
, oneHourStr
, oneDayStr
, oneMonthShortStr
, oneYearShortStr
,
321 sevenSecondsStr
, sevenMinutesStr
, sevenHoursStr
, sevenDaysStr
, sevenMonthsStr
, sevenYearsStr
,
322 sevenSecondsShortStr
, sevenMinutesShortStr
, sevenHoursShortStr
, sevenDaysStr
, sevenMonthsShortStr
, sevenYearsShortStr
};
325 for ( unsigned int locIndex
= 0;
326 locIndex
< sizeof(locales
)/sizeof(locales
[0]);
329 Locale l
= Locale::createFromName(locales
[locIndex
]);
331 for ( unsigned int numberIndex
= 0;
332 numberIndex
< sizeof(numbers
)/sizeof(int);
335 for ( unsigned int styleIndex
= 0;
336 styleIndex
< sizeof(styles
)/sizeof(styles
[0]);
339 for ( unsigned int unitIndex
= 0;
340 unitIndex
< sizeof(tunits
)/sizeof(tunits
[0]);
343 TimeUnitAmount
*tamt
= new TimeUnitAmount(numbers
[numberIndex
], tunits
[unitIndex
], status
);
344 if (U_FAILURE(status
)) {
345 dataerrln("generating TimeUnitAmount Object failed.");
347 std::cout
<< "Failed to get TimeUnitAmount for " << tunits
[unitIndex
] << "\n";
352 TimeUnitFormat
*tfmt
= new TimeUnitFormat(l
, styles
[styleIndex
], status
);
353 if (U_FAILURE(status
)) {
354 dataerrln("generating TimeUnitAmount Object failed.");
356 std::cout
<< "Failed to get TimeUnitFormat for " << locales
[locIndex
] << "\n";
364 fmt
.adoptObject(tamt
);
365 str
= ((Format
*)tfmt
)->format(fmt
, str
, status
);
366 if (!assertSuccess("formatting relative time failed", status
)) {
369 std::cout
<< "Failed to format" << "\n";
375 char tmp
[128]; //output
376 char tmp1
[128]; //expected
378 u_strToUTF8(tmp
, 128, &len
, str
.getTerminatedBuffer(), str
.length(), &status
);
379 u_strToUTF8(tmp1
, 128, &len
, expected
[counter
].unescape().getTerminatedBuffer(), expected
[counter
].unescape().length(), &status
);
380 std::cout
<< "Formatted string : " << tmp
<< " expected : " << tmp1
<< "\n";
382 if (!assertEquals("formatted time string is not expected, locale: " + UnicodeString(locales
[locIndex
]) + " style: " + (int)styles
[styleIndex
] + " units: " + (int)tunits
[unitIndex
], expected
[counter
], str
)) {
397 void TimeUnitTest::testGreekWithSanitization() {
399 UErrorCode status
= U_ZERO_ERROR
;
401 NumberFormat
* numberFmt
= NumberFormat::createInstance(Locale("el"), status
);
402 if (!assertSuccess("NumberFormat::createInstance for el locale", status
, TRUE
)) return;
403 numberFmt
->setMaximumFractionDigits(1);
405 TimeUnitFormat
* timeUnitFormat
= new TimeUnitFormat(elLoc
, status
);
406 if (!assertSuccess("TimeUnitFormat::TimeUnitFormat for el locale", status
)) return;
408 timeUnitFormat
->setNumberFormat(*numberFmt
, status
);
411 delete timeUnitFormat
;
414 void TimeUnitTest::test10219Plurals() {
415 Locale
usLocale("en_US");
416 double values
[2] = {1.588, 1.011};
417 UnicodeString expected
[2][3] = {
418 {"1 minute", "1.5 minutes", "1.58 minutes"},
419 {"1 minute", "1.0 minutes", "1.01 minutes"}
421 UErrorCode status
= U_ZERO_ERROR
;
422 TimeUnitFormat
tuf(usLocale
, status
);
423 if (U_FAILURE(status
)) {
424 dataerrln("generating TimeUnitFormat Object failed: %s", u_errorName(status
));
427 LocalPointer
<DecimalFormat
> nf((DecimalFormat
*) NumberFormat::createInstance(usLocale
, status
));
428 if (U_FAILURE(status
)) {
429 dataerrln("generating NumberFormat Object failed: %s", u_errorName(status
));
432 for (int32_t j
= 0; j
< UPRV_LENGTHOF(values
); ++j
) {
433 for (int32_t i
= 0; i
< UPRV_LENGTHOF(expected
[j
]); ++i
) {
434 nf
->setMinimumFractionDigits(i
);
435 nf
->setMaximumFractionDigits(i
);
436 nf
->setRoundingMode(DecimalFormat::kRoundDown
);
437 tuf
.setNumberFormat(*nf
, status
);
438 if (U_FAILURE(status
)) {
439 dataerrln("setting NumberFormat failed: %s", u_errorName(status
));
442 UnicodeString actual
;
444 LocalPointer
<TimeUnitAmount
> tamt(
445 new TimeUnitAmount(values
[j
], TimeUnit::UTIMEUNIT_MINUTE
, status
), status
);
446 if (U_FAILURE(status
)) {
447 dataerrln("generating TimeUnitAmount Object failed: %s", u_errorName(status
));
450 fmt
.adoptObject(tamt
.orphan());
451 tuf
.format(fmt
, actual
, status
);
452 if (U_FAILURE(status
)) {
453 dataerrln("Actual formatting failed: %s", u_errorName(status
));
456 if (expected
[j
][i
] != actual
) {
457 errln("Expected " + expected
[j
][i
] + ", got " + actual
);
465 UnicodeString formattedString
= "1 minutes";
466 tuf
.parseObject(formattedString
, result
, pos
);
467 if (formattedString
.length() != pos
.getIndex()) {
468 errln("Expect parsing to go all the way to the end of the string.");