mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-07 14:31:31 +00:00
ICU-10274 Add compound duration formatting for C++
X-SVN-Rev: 33986
This commit is contained in:
parent
4b1b47ddb0
commit
ecd7ea193b
13 changed files with 999 additions and 33 deletions
2
.gitattributes
vendored
2
.gitattributes
vendored
|
@ -82,6 +82,8 @@ icu4c/source/extra/uconv/uconv.vcxproj -text
|
|||
icu4c/source/extra/uconv/uconv.vcxproj.filters -text
|
||||
icu4c/source/i18n/i18n.vcxproj -text
|
||||
icu4c/source/i18n/i18n.vcxproj.filters -text
|
||||
icu4c/source/i18n/timeperiod.cpp -text
|
||||
icu4c/source/i18n/unicode/timeperiod.h -text
|
||||
icu4c/source/i18n/unicode/uregion.h -text
|
||||
icu4c/source/i18n/uregion.cpp -text
|
||||
icu4c/source/io/io.vcxproj -text
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2012, International Business Machines
|
||||
* Copyright (C) 2013, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*
|
||||
*******************************************************************************
|
||||
|
@ -29,6 +29,7 @@ static Hashtable* listPatternHash = NULL;
|
|||
static UMutex listFormatterMutex = U_MUTEX_INITIALIZER;
|
||||
static UChar FIRST_PARAMETER[] = { 0x7b, 0x30, 0x7d }; // "{0}"
|
||||
static UChar SECOND_PARAMETER[] = { 0x7b, 0x31, 0x7d }; // "{0}"
|
||||
static const char *STANDARD_STYLE = "standard";
|
||||
|
||||
U_CDECL_BEGIN
|
||||
static UBool U_CALLCONV uprv_listformatter_cleanup() {
|
||||
|
@ -44,9 +45,17 @@ uprv_deleteListFormatData(void *obj) {
|
|||
|
||||
U_CDECL_END
|
||||
|
||||
static ListFormatData* loadListFormatData(const Locale& locale, UErrorCode& errorCode);
|
||||
static ListFormatData* loadListFormatData(const Locale& locale, const char* style, UErrorCode& errorCode);
|
||||
static void getStringByKey(const UResourceBundle* rb, const char* key, UnicodeString& result, UErrorCode& errorCode);
|
||||
|
||||
ListFormatter::ListFormatter(const ListFormatter& other) : data(other.data) {
|
||||
}
|
||||
|
||||
ListFormatter& ListFormatter::operator=(const ListFormatter& other) {
|
||||
data = other.data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void ListFormatter::initializeHash(UErrorCode& errorCode) {
|
||||
if (U_FAILURE(errorCode)) {
|
||||
return;
|
||||
|
@ -64,11 +73,13 @@ void ListFormatter::initializeHash(UErrorCode& errorCode) {
|
|||
}
|
||||
|
||||
const ListFormatData* ListFormatter::getListFormatData(
|
||||
const Locale& locale, UErrorCode& errorCode) {
|
||||
const Locale& locale, const char *style, UErrorCode& errorCode) {
|
||||
if (U_FAILURE(errorCode)) {
|
||||
return NULL;
|
||||
}
|
||||
UnicodeString key(locale.getName(), -1, US_INV);
|
||||
CharString keyBuffer(locale.getName(), errorCode);
|
||||
keyBuffer.append(':', errorCode).append(style, errorCode);
|
||||
UnicodeString key(keyBuffer.data(), -1, US_INV);
|
||||
ListFormatData* result = NULL;
|
||||
{
|
||||
Mutex m(&listFormatterMutex);
|
||||
|
@ -83,7 +94,7 @@ const ListFormatData* ListFormatter::getListFormatData(
|
|||
if (result != NULL) {
|
||||
return result;
|
||||
}
|
||||
result = loadListFormatData(locale, errorCode);
|
||||
result = loadListFormatData(locale, style, errorCode);
|
||||
if (U_FAILURE(errorCode)) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -104,14 +115,22 @@ const ListFormatData* ListFormatter::getListFormatData(
|
|||
return result;
|
||||
}
|
||||
|
||||
static ListFormatData* loadListFormatData(const Locale& locale, UErrorCode& errorCode) {
|
||||
static ListFormatData* loadListFormatData(
|
||||
const Locale& locale, const char * style, UErrorCode& errorCode) {
|
||||
UResourceBundle* rb = ures_open(NULL, locale.getName(), &errorCode);
|
||||
if (U_FAILURE(errorCode)) {
|
||||
ures_close(rb);
|
||||
return NULL;
|
||||
}
|
||||
rb = ures_getByKeyWithFallback(rb, "listPattern", rb, &errorCode);
|
||||
rb = ures_getByKeyWithFallback(rb, "standard", rb, &errorCode);
|
||||
rb = ures_getByKeyWithFallback(rb, style, rb, &errorCode);
|
||||
|
||||
// TODO(Travis Keep): This is a hack until fallbacks can be added for
|
||||
// listPattern/duration and listPattern/duration-narrow in CLDR.
|
||||
if (errorCode == U_MISSING_RESOURCE_ERROR) {
|
||||
errorCode = U_ZERO_ERROR;
|
||||
rb = ures_getByKeyWithFallback(rb, "standard", rb, &errorCode);
|
||||
}
|
||||
if (U_FAILURE(errorCode)) {
|
||||
ures_close(rb);
|
||||
return NULL;
|
||||
|
@ -148,12 +167,16 @@ ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) {
|
|||
}
|
||||
|
||||
ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& errorCode) {
|
||||
return createInstance(locale, STANDARD_STYLE, errorCode);
|
||||
}
|
||||
|
||||
ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *style, UErrorCode& errorCode) {
|
||||
Locale tempLocale = locale;
|
||||
const ListFormatData* listFormatData = getListFormatData(tempLocale, errorCode);
|
||||
const ListFormatData* listFormatData = getListFormatData(tempLocale, style, errorCode);
|
||||
if (U_FAILURE(errorCode)) {
|
||||
return NULL;
|
||||
}
|
||||
ListFormatter* p = new ListFormatter(*listFormatData);
|
||||
ListFormatter* p = new ListFormatter(listFormatData);
|
||||
if (p == NULL) {
|
||||
errorCode = U_MEMORY_ALLOCATION_ERROR;
|
||||
return NULL;
|
||||
|
@ -161,7 +184,8 @@ ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& e
|
|||
return p;
|
||||
}
|
||||
|
||||
ListFormatter::ListFormatter(const ListFormatData& listFormatterData) : data(listFormatterData) {
|
||||
|
||||
ListFormatter::ListFormatter(const ListFormatData* listFormatterData) : data(listFormatterData) {
|
||||
}
|
||||
|
||||
ListFormatter::~ListFormatter() {}
|
||||
|
@ -171,18 +195,22 @@ UnicodeString& ListFormatter::format(const UnicodeString items[], int32_t nItems
|
|||
if (U_FAILURE(errorCode)) {
|
||||
return appendTo;
|
||||
}
|
||||
if (data == NULL) {
|
||||
errorCode = U_INVALID_STATE_ERROR;
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
if (nItems > 0) {
|
||||
UnicodeString newString = items[0];
|
||||
if (nItems == 2) {
|
||||
addNewString(data.twoPattern, newString, items[1], errorCode);
|
||||
addNewString(data->twoPattern, newString, items[1], errorCode);
|
||||
} else if (nItems > 2) {
|
||||
addNewString(data.startPattern, newString, items[1], errorCode);
|
||||
addNewString(data->startPattern, newString, items[1], errorCode);
|
||||
int32_t i;
|
||||
for (i = 2; i < nItems - 1; ++i) {
|
||||
addNewString(data.middlePattern, newString, items[i], errorCode);
|
||||
addNewString(data->middlePattern, newString, items[i], errorCode);
|
||||
}
|
||||
addNewString(data.endPattern, newString, items[nItems - 1], errorCode);
|
||||
addNewString(data->endPattern, newString, items[nItems - 1], errorCode);
|
||||
}
|
||||
if (U_SUCCESS(errorCode)) {
|
||||
appendTo += newString;
|
||||
|
|
|
@ -60,12 +60,26 @@ struct ListFormatData : public UMemory {
|
|||
class U_COMMON_API ListFormatter : public UObject{
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
* @draft ICU 52
|
||||
*/
|
||||
ListFormatter(const ListFormatter&);
|
||||
|
||||
/**
|
||||
* Assignment operator.
|
||||
* @draft ICU 52
|
||||
*/
|
||||
ListFormatter& operator=(const ListFormatter& other);
|
||||
|
||||
/**
|
||||
* Creates a ListFormatter appropriate for the default locale.
|
||||
*
|
||||
* @param errorCode ICU error code, set if no data available for default locale.
|
||||
* @return Pointer to a ListFormatter object for the default locale,
|
||||
* created from internal data derived from CLDR data.
|
||||
* @deprecated
|
||||
* @draft ICU 50
|
||||
*/
|
||||
static ListFormatter* createInstance(UErrorCode& errorCode);
|
||||
|
@ -77,10 +91,22 @@ class U_COMMON_API ListFormatter : public UObject{
|
|||
* @param errorCode ICU error code, set if no data available for the given locale.
|
||||
* @return A ListFormatter object created from internal data derived from
|
||||
* CLDR data.
|
||||
* @deprecated
|
||||
* @draft ICU 50
|
||||
*/
|
||||
static ListFormatter* createInstance(const Locale& locale, UErrorCode& errorCode);
|
||||
|
||||
/**
|
||||
* Creates a ListFormatter appropriate for a locale and style.
|
||||
*
|
||||
* @param locale The locale.
|
||||
* @param style the style, either "standard", "duration", or "duration-short"
|
||||
* @param errorCode ICU error code, set if no data available for the given locale.
|
||||
* @return A ListFormatter object created from internal data derived from
|
||||
* CLDR data.
|
||||
* @internal
|
||||
*/
|
||||
static ListFormatter* createInstance(const Locale& locale, const char* style, UErrorCode& errorCode);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
|
@ -106,20 +132,17 @@ class U_COMMON_API ListFormatter : public UObject{
|
|||
/**
|
||||
* @internal constructor made public for testing.
|
||||
*/
|
||||
ListFormatter(const ListFormatData& listFormatterData);
|
||||
ListFormatter(const ListFormatData* listFormatterData);
|
||||
|
||||
private:
|
||||
static void initializeHash(UErrorCode& errorCode);
|
||||
static const ListFormatData* getListFormatData(const Locale& locale, UErrorCode& errorCode);
|
||||
static const ListFormatData* getListFormatData(const Locale& locale, const char *style, UErrorCode& errorCode);
|
||||
|
||||
ListFormatter();
|
||||
ListFormatter(const ListFormatter&);
|
||||
|
||||
ListFormatter& operator = (const ListFormatter&);
|
||||
void addNewString(const UnicodeString& pattern, UnicodeString& originalString,
|
||||
const UnicodeString& newString, UErrorCode& errorCode) const;
|
||||
|
||||
const ListFormatData& data;
|
||||
const ListFormatData* data;
|
||||
};
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
|
|
@ -86,7 +86,9 @@ tmunit.o tmutamt.o tmutfmt.o currpinf.o \
|
|||
uspoof.o uspoof_impl.o uspoof_build.o uspoof_conf.o uspoof_wsconf.o decfmtst.o smpdtfst.o \
|
||||
ztrans.o zrule.o vzone.o fphdlimp.o fpositer.o locdspnm.o \
|
||||
decNumber.o decContext.o alphaindex.o tznames.o tznames_impl.o tzgnames.o \
|
||||
tzfmt.o compactdecimalformat.o gender.o region.o uregion.o scriptset.o identifier_info.o
|
||||
tzfmt.o compactdecimalformat.o gender.o region.o scriptset.o identifier_info.o \
|
||||
tzfmt.o compactdecimalformat.o gender.o region.o uregion.o scriptset.o \
|
||||
identifier_info.o timeperiod.o
|
||||
|
||||
## Header files to install
|
||||
HEADERS = $(srcdir)/unicode/*.h
|
||||
|
|
|
@ -330,6 +330,7 @@
|
|||
<ClCompile Include="smpdtfmt.cpp" />
|
||||
<ClCompile Include="smpdtfst.cpp" />
|
||||
<ClCompile Include="taiwncal.cpp" />
|
||||
<ClCompile Include="timeperiod.cpp" />
|
||||
<ClCompile Include="timezone.cpp" />
|
||||
<ClCompile Include="tmunit.cpp" />
|
||||
<ClCompile Include="tmutamt.cpp" />
|
||||
|
@ -1179,6 +1180,20 @@
|
|||
</CustomBuild>
|
||||
<ClInclude Include="smpdtfst.h" />
|
||||
<ClInclude Include="taiwncal.h" />
|
||||
<CustomBuild Include="unicode\timeperiod.h">
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">copy "%(FullPath)" ..\..\include\unicode
|
||||
</Command>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">copy "%(FullPath)" ..\..\include\unicode
|
||||
</Command>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">copy "%(FullPath)" ..\..\include\unicode
|
||||
</Command>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">copy "%(FullPath)" ..\..\include\unicode
|
||||
</Command>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="unicode\timezone.h">
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">copy "%(FullPath)" ..\..\include\unicode
|
||||
</Command>
|
||||
|
|
|
@ -249,6 +249,9 @@
|
|||
<ClCompile Include="taiwncal.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="timeperiod.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="timezone.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
|
@ -942,6 +945,9 @@
|
|||
<CustomBuild Include="unicode\smpdtfmt.h">
|
||||
<Filter>formatting</Filter>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="unicode\timeperiod.h">
|
||||
<Filter>formatting</Filter>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="unicode\timezone.h">
|
||||
<Filter>formatting</Filter>
|
||||
</CustomBuild>
|
||||
|
|
119
icu4c/source/i18n/timeperiod.cpp
Normal file
119
icu4c/source/i18n/timeperiod.cpp
Normal file
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2013-2013, Google, International Business Machines Corporation
|
||||
* and others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include "unicode/timeperiod.h"
|
||||
#include "putilimp.h"
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
TimePeriod::TimePeriod(const TimePeriod& other) : fLength(other.fLength) {
|
||||
for (int32_t i = 0; i < sizeof(fFields) / sizeof(fFields[0]); ++i) {
|
||||
if (other.fFields[i] == NULL) {
|
||||
fFields[i] = NULL;
|
||||
} else {
|
||||
fFields[i] = (TimeUnitAmount *) other.fFields[i]->clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TimePeriod::TimePeriod(
|
||||
const TimeUnitAmount * const *timeUnitAmounts,
|
||||
int32_t length,
|
||||
UErrorCode& status) : fLength(0) {
|
||||
int32_t fieldCount = sizeof(fFields) / sizeof(fFields[0]);
|
||||
for (int32_t i = 0; i < fieldCount; ++i) {
|
||||
fFields[i] = NULL;
|
||||
}
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
for (int32_t i = 0; i < length; ++i) {
|
||||
const TimeUnitAmount *tua = timeUnitAmounts[i];
|
||||
int32_t idx = tua->getTimeUnitField();
|
||||
if (fFields[idx] != NULL) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
}
|
||||
fFields[idx] = (TimeUnitAmount *) tua->clone();
|
||||
++fLength;
|
||||
}
|
||||
validate(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TimePeriod::validate(UErrorCode& status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
if (fLength == 0) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
}
|
||||
int32_t fieldCount = sizeof(fFields) / sizeof(fFields[0]);
|
||||
UBool fractionalFieldEncountered = FALSE;
|
||||
for (int32_t i = 0; i < fieldCount; ++i) {
|
||||
if (fFields[i] == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (fractionalFieldEncountered) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
}
|
||||
UErrorCode doubleStatus = U_ZERO_ERROR;
|
||||
double value = fFields[i]->getNumber().getDouble(doubleStatus);
|
||||
if (value != uprv_floor(value)) {
|
||||
fractionalFieldEncountered = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TimePeriod::~TimePeriod() {
|
||||
int32_t fieldCount = sizeof(fFields) / sizeof(fFields[0]);
|
||||
for (int32_t i = 0; i < fieldCount; ++i) {
|
||||
delete fFields[i];
|
||||
}
|
||||
}
|
||||
|
||||
UBool TimePeriod::operator==(const TimePeriod& other) const {
|
||||
// If same object, they are equal
|
||||
if (this == &other) {
|
||||
return TRUE;
|
||||
}
|
||||
int32_t fieldCount = sizeof(fFields) / sizeof(fFields[0]);
|
||||
for (int32_t i = 0; i < fieldCount; ++i) {
|
||||
if (fFields[i] == other.fFields[i]) {
|
||||
continue;
|
||||
}
|
||||
// One is NULL, the other isn't.
|
||||
if (fFields[i] == NULL || other.fFields[i] == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
if (*fFields[i] != *other.fFields[i]) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
UBool TimePeriod::operator!=(const TimePeriod& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
const TimeUnitAmount *TimePeriod::getAmount(
|
||||
TimeUnit::UTimeUnitFields field) const {
|
||||
return fFields[field];
|
||||
}
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif
|
|
@ -17,7 +17,12 @@
|
|||
#include "cstring.h"
|
||||
#include "hash.h"
|
||||
#include "uresimp.h"
|
||||
#include "unicode/datefmt.h"
|
||||
#include "unicode/listformatter.h"
|
||||
#include "unicode/msgfmt.h"
|
||||
#include "unicode/smpdtfmt.h"
|
||||
#include "unicode/timeperiod.h"
|
||||
#include "unicode/udat.h"
|
||||
#include "uassert.h"
|
||||
|
||||
#define LEFT_CURLY_BRACKET ((UChar)0x007B)
|
||||
|
@ -76,24 +81,48 @@ static const UChar PLURAL_COUNT_ZERO[] = {LOW_Z, LOW_E, LOW_R, LOW_O, 0};
|
|||
static const UChar PLURAL_COUNT_ONE[] = {LOW_O, LOW_N, LOW_E, 0};
|
||||
static const UChar PLURAL_COUNT_TWO[] = {LOW_T, LOW_W, LOW_O, 0};
|
||||
|
||||
static const TimeUnit::UTimeUnitFields FIELDS_BY_DURATION[] = {
|
||||
TimeUnit::UTIMEUNIT_YEAR,
|
||||
TimeUnit::UTIMEUNIT_MONTH,
|
||||
TimeUnit::UTIMEUNIT_WEEK,
|
||||
TimeUnit::UTIMEUNIT_DAY,
|
||||
TimeUnit::UTIMEUNIT_HOUR,
|
||||
TimeUnit::UTIMEUNIT_MINUTE,
|
||||
TimeUnit::UTIMEUNIT_SECOND,
|
||||
TimeUnit::UTIMEUNIT_FIELD_COUNT};
|
||||
|
||||
static void cloneDateFormat(DateFormat *&, const DateFormat *);
|
||||
static double getAmountOrZero(const TimePeriod& timePeriod, TimeUnit::UTimeUnitFields field);
|
||||
|
||||
TimeUnitFormat::TimeUnitFormat(UErrorCode& status)
|
||||
: fNumberFormat(NULL),
|
||||
fPluralRules(NULL) {
|
||||
fPluralRules(NULL),
|
||||
fListFormatter(NULL),
|
||||
fHourMinute(NULL),
|
||||
fHourMinuteSecond(NULL),
|
||||
fMinuteSecond(NULL) {
|
||||
create(Locale::getDefault(), UTMUTFMT_FULL_STYLE, status);
|
||||
}
|
||||
|
||||
|
||||
TimeUnitFormat::TimeUnitFormat(const Locale& locale, UErrorCode& status)
|
||||
: fNumberFormat(NULL),
|
||||
fPluralRules(NULL) {
|
||||
fPluralRules(NULL),
|
||||
fListFormatter(NULL),
|
||||
fHourMinute(NULL),
|
||||
fHourMinuteSecond(NULL),
|
||||
fMinuteSecond(NULL) {
|
||||
create(locale, UTMUTFMT_FULL_STYLE, status);
|
||||
}
|
||||
|
||||
|
||||
TimeUnitFormat::TimeUnitFormat(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status)
|
||||
: fNumberFormat(NULL),
|
||||
fPluralRules(NULL) {
|
||||
fPluralRules(NULL),
|
||||
fListFormatter(NULL),
|
||||
fHourMinute(NULL),
|
||||
fHourMinuteSecond(NULL),
|
||||
fMinuteSecond(NULL) {
|
||||
create(locale, style, status);
|
||||
}
|
||||
|
||||
|
@ -102,8 +131,11 @@ TimeUnitFormat::TimeUnitFormat(const TimeUnitFormat& other)
|
|||
: MeasureFormat(other),
|
||||
fNumberFormat(NULL),
|
||||
fPluralRules(NULL),
|
||||
fStyle(UTMUTFMT_FULL_STYLE)
|
||||
{
|
||||
fListFormatter(NULL),
|
||||
fHourMinute(NULL),
|
||||
fHourMinuteSecond(NULL),
|
||||
fMinuteSecond(NULL),
|
||||
fStyle(UTMUTFMT_FULL_STYLE) {
|
||||
for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
|
||||
i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
|
||||
i = (TimeUnit::UTimeUnitFields)(i+1)) {
|
||||
|
@ -124,6 +156,14 @@ TimeUnitFormat::~TimeUnitFormat() {
|
|||
}
|
||||
delete fPluralRules;
|
||||
fPluralRules = NULL;
|
||||
delete fListFormatter;
|
||||
fListFormatter = NULL;
|
||||
delete fHourMinute;
|
||||
fHourMinute = NULL;
|
||||
delete fHourMinuteSecond;
|
||||
fHourMinuteSecond = NULL;
|
||||
delete fMinuteSecond;
|
||||
fMinuteSecond = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
@ -151,6 +191,16 @@ TimeUnitFormat::operator=(const TimeUnitFormat& other) {
|
|||
} else {
|
||||
fNumberFormat = NULL;
|
||||
}
|
||||
delete fListFormatter;
|
||||
if (other.fListFormatter) {
|
||||
fListFormatter = new ListFormatter(*other.fListFormatter);
|
||||
} else {
|
||||
fListFormatter = NULL;
|
||||
}
|
||||
cloneDateFormat(fHourMinute, other.fHourMinute);
|
||||
cloneDateFormat(fHourMinuteSecond, other.fHourMinuteSecond);
|
||||
cloneDateFormat(fMinuteSecond, other.fMinuteSecond);
|
||||
|
||||
fLocale = other.fLocale;
|
||||
for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
|
||||
i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
|
||||
|
@ -224,11 +274,17 @@ TimeUnitFormat::format(const Formattable& obj, UnicodeString& toAppendTo,
|
|||
count.extract(0, count.length(), result, "UTF-8");
|
||||
std::cout << "number: " << number << "; format plural count: " << result << "\n";
|
||||
#endif
|
||||
MessageFormat* pattern = ((MessageFormat**)countToPattern->get(count))[fStyle];
|
||||
UTimeUnitFormatStyle effectiveStyle = (fStyle == UTMUTFMT_NUMERIC_STYLE) ?
|
||||
UTMUTFMT_ABBREVIATED_STYLE : fStyle;
|
||||
MessageFormat* pattern = ((MessageFormat**)countToPattern->get(count))[effectiveStyle];
|
||||
Formattable formattable[1];
|
||||
formattable[0].setDouble(number);
|
||||
return pattern->format(formattable, 1, toAppendTo, pos, status);
|
||||
}
|
||||
const TimePeriod* period = dynamic_cast<const TimePeriod*>(formatObj);
|
||||
if (period != NULL) {
|
||||
return formatTimePeriod(*period, toAppendTo, status);
|
||||
}
|
||||
}
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return toAppendTo;
|
||||
|
@ -271,6 +327,10 @@ TimeUnitFormat::parseObject(const UnicodeString& source,
|
|||
MessageFormat** patterns = (MessageFormat**)valueTok.pointer;
|
||||
for (UTimeUnitFormatStyle style = UTMUTFMT_FULL_STYLE; style < UTMUTFMT_FORMAT_STYLE_COUNT;
|
||||
style = (UTimeUnitFormatStyle)(style + 1)) {
|
||||
// We don't support parsing of numeric styles, so skip it.
|
||||
if (style == UTMUTFMT_NUMERIC_STYLE) {
|
||||
continue;
|
||||
}
|
||||
MessageFormat* pattern = patterns[style];
|
||||
pos.setErrorIndex(-1);
|
||||
pos.setIndex(oldPos);
|
||||
|
@ -356,13 +416,129 @@ TimeUnitFormat::parseObject(const UnicodeString& source,
|
|||
}
|
||||
}
|
||||
|
||||
UnicodeString&
|
||||
TimeUnitFormat::formatTimePeriod(
|
||||
const TimePeriod& timePeriod, UnicodeString& toAppendTo, UErrorCode& status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return toAppendTo;
|
||||
}
|
||||
if (fStyle == UTMUTFMT_NUMERIC_STYLE && formatTimePeriodAsNumeric(timePeriod, toAppendTo, status)) {
|
||||
return toAppendTo;
|
||||
}
|
||||
UnicodeString *items = new UnicodeString[timePeriod.length()];
|
||||
if (items == NULL) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return toAppendTo;
|
||||
}
|
||||
int32_t idx = 0;
|
||||
for (int32_t fieldIndex = 0; FIELDS_BY_DURATION[fieldIndex] != TimeUnit::UTIMEUNIT_FIELD_COUNT; ++fieldIndex) {
|
||||
const TimeUnitAmount *amount = timePeriod.getAmount(FIELDS_BY_DURATION[fieldIndex]);
|
||||
if (amount == NULL) {
|
||||
continue;
|
||||
}
|
||||
TimeUnitAmount *timeUnitAmountCopy = new TimeUnitAmount(*amount);
|
||||
if (timeUnitAmountCopy == NULL) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
delete [] items;
|
||||
return toAppendTo;
|
||||
}
|
||||
format(Formattable(timeUnitAmountCopy), items[idx], status);
|
||||
if (U_FAILURE(status)) {
|
||||
delete [] items;
|
||||
return toAppendTo;
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
UnicodeString& result = fListFormatter->format(items, timePeriod.length(), toAppendTo, status);
|
||||
delete [] items;
|
||||
return result;
|
||||
}
|
||||
|
||||
UBool
|
||||
TimeUnitFormat::formatTimePeriodAsNumeric(
|
||||
const TimePeriod& timePeriod, UnicodeString& toAppendTo, UErrorCode& status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return TRUE;
|
||||
}
|
||||
TimeUnit::UTimeUnitFields biggestUnit = TimeUnit::UTIMEUNIT_FIELD_COUNT;
|
||||
TimeUnit::UTimeUnitFields smallestUnit = TimeUnit::UTIMEUNIT_FIELD_COUNT;
|
||||
const TimeUnitAmount *smallestAmount = NULL;
|
||||
for (int32_t fieldIndex = 0; FIELDS_BY_DURATION[fieldIndex] != TimeUnit::UTIMEUNIT_FIELD_COUNT; ++fieldIndex) {
|
||||
const TimeUnitAmount *amount = timePeriod.getAmount(FIELDS_BY_DURATION[fieldIndex]);
|
||||
if (amount == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (biggestUnit == TimeUnit::UTIMEUNIT_FIELD_COUNT) {
|
||||
biggestUnit = FIELDS_BY_DURATION[fieldIndex];
|
||||
}
|
||||
smallestUnit = FIELDS_BY_DURATION[fieldIndex];
|
||||
smallestAmount = amount;
|
||||
}
|
||||
U_ASSERT(biggestUnit != TimeUnit::UTIMEUNIT_FIELD_COUNT);
|
||||
U_ASSERT(smallestUnit != TimeUnit::UTIMEUNIT_FIELD_COUNT);
|
||||
U_ASSERT(smallestAmount != NULL);
|
||||
|
||||
double millis = ((getAmountOrZero(timePeriod, TimeUnit::UTIMEUNIT_HOUR) * 60.0
|
||||
+ getAmountOrZero(timePeriod, TimeUnit::UTIMEUNIT_MINUTE)) * 60.0
|
||||
+ getAmountOrZero(timePeriod, TimeUnit::UTIMEUNIT_SECOND)) * 1000.0;
|
||||
if (biggestUnit == TimeUnit::UTIMEUNIT_HOUR && smallestUnit == TimeUnit::UTIMEUNIT_SECOND) {
|
||||
numericFormat(millis, *fHourMinuteSecond, UDAT_SECOND_FIELD, smallestAmount->getNumber(), toAppendTo, status);
|
||||
return TRUE;
|
||||
}
|
||||
if (biggestUnit == TimeUnit::UTIMEUNIT_MINUTE && smallestUnit == TimeUnit::UTIMEUNIT_SECOND) {
|
||||
numericFormat(millis, *fMinuteSecond, UDAT_SECOND_FIELD, smallestAmount->getNumber(), toAppendTo, status);
|
||||
return TRUE;
|
||||
}
|
||||
if (biggestUnit == TimeUnit::UTIMEUNIT_HOUR && smallestUnit == TimeUnit::UTIMEUNIT_MINUTE) {
|
||||
numericFormat(millis, *fHourMinute, UDAT_MINUTE_FIELD, smallestAmount->getNumber(), toAppendTo, status);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
TimeUnitFormat::numericFormat(
|
||||
double date,
|
||||
const DateFormat &dateFormat,
|
||||
int32_t smallestField,
|
||||
const Formattable& smallestAmount,
|
||||
UnicodeString& toAppendTo,
|
||||
UErrorCode& status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
UnicodeString smallestAmountFormatted;
|
||||
fNumberFormat->format(smallestAmount, smallestAmountFormatted, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
FieldPositionIterator fpi;
|
||||
int32_t prevLength = toAppendTo.length();
|
||||
dateFormat.format(Formattable(date, Formattable::kIsDate), toAppendTo, &fpi, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
FieldPosition fp;
|
||||
UBool substituted = FALSE;
|
||||
while (fpi.next(fp)) {
|
||||
if (fp.getField() == smallestField) {
|
||||
toAppendTo.replace(
|
||||
prevLength + fp.getBeginIndex(),
|
||||
fp.getEndIndex() - fp.getBeginIndex(),
|
||||
smallestAmountFormatted);
|
||||
substituted = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
U_ASSERT(substituted);
|
||||
}
|
||||
|
||||
void
|
||||
TimeUnitFormat::create(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
if (style < UTMUTFMT_FULL_STYLE || style > UTMUTFMT_ABBREVIATED_STYLE) {
|
||||
if (style < UTMUTFMT_FULL_STYLE || style >= UTMUTFMT_FORMAT_STYLE_COUNT) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
}
|
||||
|
@ -415,6 +591,18 @@ TimeUnitFormat::initDataMembers(UErrorCode& err){
|
|||
}
|
||||
delete fPluralRules;
|
||||
fPluralRules = PluralRules::forLocale(fLocale, err);
|
||||
delete fListFormatter;
|
||||
if (fStyle == UTMUTFMT_FULL_STYLE) {
|
||||
fListFormatter = ListFormatter::createInstance(fLocale, "duration", err);
|
||||
} else {
|
||||
fListFormatter = ListFormatter::createInstance(fLocale, "duration-short", err);
|
||||
}
|
||||
delete fHourMinute;
|
||||
fHourMinute = loadNumericDurationFormat("hm", err);
|
||||
delete fHourMinuteSecond;
|
||||
fHourMinuteSecond = loadNumericDurationFormat("hms", err);
|
||||
delete fMinuteSecond;
|
||||
fMinuteSecond = loadNumericDurationFormat("ms", err);
|
||||
for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
|
||||
i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
|
||||
i = (TimeUnit::UTimeUnitFields)(i+1)) {
|
||||
|
@ -423,8 +611,40 @@ TimeUnitFormat::initDataMembers(UErrorCode& err){
|
|||
}
|
||||
}
|
||||
|
||||
DateFormat *
|
||||
TimeUnitFormat::loadNumericDurationFormat(const char *pattern, UErrorCode& status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return NULL;
|
||||
}
|
||||
UResourceBundle *rb, *unitsRes;
|
||||
rb = ures_open(NULL, fLocale.getName(), &status);
|
||||
if (U_FAILURE(status)) {
|
||||
return NULL;
|
||||
}
|
||||
CharString path("durationUnits", status);
|
||||
path.append('/', status).append(pattern, status);
|
||||
|
||||
|
||||
int32_t fullPatternSize;
|
||||
const UChar *fullPatternChars = ures_getStringByKeyWithFallback(rb, path.data(), &fullPatternSize, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
ures_close(rb);
|
||||
return NULL;
|
||||
}
|
||||
UnicodeString fullPattern(FALSE, fullPatternChars, fullPatternSize);
|
||||
fullPattern.findAndReplace(UnicodeString("h"), UnicodeString("H"));
|
||||
DateFormat *result = new SimpleDateFormat(fullPattern, status);
|
||||
if (result == NULL) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
ures_close(rb);
|
||||
return NULL;
|
||||
}
|
||||
if (U_FAILURE(status)) {
|
||||
delete result;
|
||||
return NULL;
|
||||
}
|
||||
result->setTimeZone(*TimeZone::getGMT());
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
TimeUnitFormat::readFromCurrentLocale(UTimeUnitFormatStyle style, const char* key,
|
||||
|
@ -900,6 +1120,25 @@ TimeUnitFormat::getTimeUnitName(TimeUnit::UTimeUnitFields unitField,
|
|||
}
|
||||
}
|
||||
|
||||
void cloneDateFormat(DateFormat *&dest, const DateFormat *src) {
|
||||
delete dest;
|
||||
if (src) {
|
||||
dest = (DateFormat *) src->clone();
|
||||
} else {
|
||||
dest = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
double getAmountOrZero(const TimePeriod& timePeriod, TimeUnit::UTimeUnitFields field) {
|
||||
const TimeUnitAmount *amount = timePeriod.getAmount(field);
|
||||
if (amount == NULL) {
|
||||
return 0.0;
|
||||
}
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
return amount->getNumber().getDouble(status);
|
||||
}
|
||||
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
|
113
icu4c/source/i18n/unicode/timeperiod.h
Normal file
113
icu4c/source/i18n/unicode/timeperiod.h
Normal file
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2013-2013, Google, International Business Machines Corporation
|
||||
* and others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef __TIMEPERIOD_H__
|
||||
#define __TIMEPERIOD_H__
|
||||
|
||||
// TODO:
|
||||
// 1. Add size() method and test.
|
||||
// 2. testEquals() -> testTimePeriodEquals()
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \brief C++ API: Format and parse duration in single time unit
|
||||
*/
|
||||
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#include "unicode/tmunit.h"
|
||||
#include "unicode/tmutamt.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
class U_I18N_API TimePeriod: public UObject {
|
||||
public:
|
||||
// copy constructor.
|
||||
TimePeriod(const TimePeriod& other);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param timeUnitAmounts an array of TimeUnitAmounts pointers. TimePeriod copies the
|
||||
* data in this array. The caller is responsible for freeing the TimeUnitAmount objects
|
||||
* and the array.
|
||||
* @param length the number of timeUnitAmount pointers in timeUnitAmounts array.
|
||||
* @param status error returned here if timeUnitAmounts is empty;
|
||||
* timeUnitAmounts has duplicate time units; or any timeUnitAmount except the
|
||||
* smallest has a non-integer value.
|
||||
* @draft ICU 52
|
||||
*/
|
||||
TimePeriod(
|
||||
const TimeUnitAmount * const *timeUnitAmounts, int32_t length, UErrorCode& status);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
* @draft ICU 52
|
||||
*/
|
||||
virtual ~TimePeriod();
|
||||
|
||||
/**
|
||||
* Returns true if the given TimePeriod objects are semantically equal.
|
||||
* For two TimePeriod objects to be equal, they must contain the same
|
||||
* units, and the amount for each unit much be equal. For example,
|
||||
* 5 hours, 37 minutes == 37 minutes, 5 hours,
|
||||
* but 0 days, 5 hours != 5 hours.
|
||||
* @param that the TimePeriod to compare with.
|
||||
* @return true if the given TimePeriod objects are semantically equal.
|
||||
* @draft ICU 52
|
||||
*/
|
||||
UBool operator==(const TimePeriod& that) const;
|
||||
|
||||
/**
|
||||
* Returns true if the given TimePeriod objects are semantically
|
||||
* unequal.
|
||||
* @param that the TimePeriod to compare with.
|
||||
* @return true if the given TimePeriod objects are semantically
|
||||
* unequal.
|
||||
* @draft ICU 52
|
||||
*/
|
||||
UBool operator!=(const TimePeriod& that) const;
|
||||
|
||||
/**
|
||||
* Gets a specific field out of a time period.
|
||||
* @param field is the field to fetch
|
||||
* @return The desired field or NULL if it does not exist.
|
||||
*/
|
||||
const TimeUnitAmount* getAmount(TimeUnit::UTimeUnitFields field) const;
|
||||
|
||||
/**
|
||||
* Returns the number of time units in this object.
|
||||
* @draft ICU 52
|
||||
*/
|
||||
inline int32_t length() const {
|
||||
return fLength;
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t fLength;
|
||||
TimeUnitAmount *fFields[TimeUnit::UTIMEUNIT_FIELD_COUNT];
|
||||
|
||||
// Clients use forAmount and never use ctor directly.
|
||||
TimePeriod() {
|
||||
}
|
||||
|
||||
/**
|
||||
* No assignment operator needed because class is immutable.
|
||||
*/
|
||||
TimePeriod& operator=(const TimePeriod& other);
|
||||
|
||||
void validate(UErrorCode& status) const;
|
||||
};
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
||||
#endif // __TMUTFMT_H__
|
||||
//eof
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2008-2012, Google, International Business Machines Corporation
|
||||
* Copyright (C) 2008-2013, Google, International Business Machines Corporation
|
||||
* and others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
@ -37,6 +37,8 @@ enum UTimeUnitFormatStyle {
|
|||
UTMUTFMT_FULL_STYLE,
|
||||
/** @stable ICU 4.8 */
|
||||
UTMUTFMT_ABBREVIATED_STYLE,
|
||||
/** @draft ICU 52 */
|
||||
UTMUTFMT_NUMERIC_STYLE,
|
||||
/** @stable ICU 4.8 */
|
||||
UTMUTFMT_FORMAT_STYLE_COUNT
|
||||
};
|
||||
|
@ -44,8 +46,11 @@ typedef enum UTimeUnitFormatStyle UTimeUnitFormatStyle; /**< @stable ICU 4.8 */
|
|||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
class DateFormat;
|
||||
class Hashtable;
|
||||
class UVector;
|
||||
class TimePeriod;
|
||||
class ListFormatter;
|
||||
|
||||
/**
|
||||
* Format or parse a TimeUnitAmount, using plural rules for the units where available.
|
||||
|
@ -186,6 +191,18 @@ public:
|
|||
Formattable& result,
|
||||
ParsePosition& pos) const;
|
||||
|
||||
/**
|
||||
* Format a time period.
|
||||
* @param timePeriod the time period to format.
|
||||
* @param toAppendTo where the formatted string is stored.
|
||||
* @param status any error is stored here
|
||||
* @return a reference to toAppendto
|
||||
* @draft ICU 52
|
||||
*/
|
||||
UnicodeString& formatTimePeriod(const TimePeriod &timePeriod,
|
||||
UnicodeString& toAppendTo,
|
||||
UErrorCode& status) const;
|
||||
|
||||
/**
|
||||
* Return the class ID for this class. This is useful only for comparing to
|
||||
* a return value from getDynamicClassID(). For example:
|
||||
|
@ -217,8 +234,15 @@ private:
|
|||
Locale fLocale;
|
||||
Hashtable* fTimeUnitToCountToPatterns[TimeUnit::UTIMEUNIT_FIELD_COUNT];
|
||||
PluralRules* fPluralRules;
|
||||
ListFormatter *fListFormatter;
|
||||
DateFormat *fHourMinute;
|
||||
DateFormat *fHourMinuteSecond;
|
||||
DateFormat *fMinuteSecond;
|
||||
UTimeUnitFormatStyle fStyle;
|
||||
|
||||
UBool formatTimePeriodAsNumeric(
|
||||
const TimePeriod& timePeriod, UnicodeString& toAppendTo, UErrorCode& status) const;
|
||||
|
||||
void create(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status);
|
||||
|
||||
// it might actually be simpler to make them Decimal Formats later.
|
||||
|
@ -252,6 +276,19 @@ private:
|
|||
// get time unit name, such as "year", from time unit field enum, such as
|
||||
// UTIMEUNIT_YEAR.
|
||||
static const char* getTimeUnitName(TimeUnit::UTimeUnitFields field, UErrorCode& status);
|
||||
|
||||
void numericFormat(
|
||||
double date,
|
||||
const DateFormat &dateFormat,
|
||||
int32_t smallestField,
|
||||
const Formattable& smallestAmount,
|
||||
UnicodeString& toAppendto,
|
||||
UErrorCode& status) const;
|
||||
|
||||
DateFormat *loadNumericDurationFormat(
|
||||
const char *pattern,
|
||||
UErrorCode& status) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -175,7 +175,7 @@ void ListFormatterTest::TestOutOfOrderPatterns() {
|
|||
|
||||
ListFormatData data("{1} after {0}", "{1} after the first {0}",
|
||||
"{1} after {0}", "{1} in the last after {0}");
|
||||
ListFormatter formatter(data);
|
||||
ListFormatter formatter(&data);
|
||||
|
||||
UnicodeString input1[] = {one};
|
||||
CheckFormatting(&formatter, input1, 1, results[0]);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/********************************************************************
|
||||
* Copyright (c) 2008-2012, International Business Machines Corporation and
|
||||
* Copyright (c) 2008-2013, International Business Machines Corporation and
|
||||
* others. All Rights Reserved.
|
||||
********************************************************************/
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
|||
#include "unicode/tmutamt.h"
|
||||
#include "unicode/tmutfmt.h"
|
||||
#include "tufmtts.h"
|
||||
#include "unicode/timeperiod.h"
|
||||
#include "unicode/ustring.h"
|
||||
|
||||
//TODO: put as compilation flag
|
||||
|
@ -20,6 +21,45 @@
|
|||
#include <iostream>
|
||||
#endif
|
||||
|
||||
struct TimePeriodResult {
|
||||
TimePeriod timePeriod;
|
||||
const char* result;
|
||||
};
|
||||
|
||||
class TimeUnitAmountSubClass : public TimeUnitAmount {
|
||||
public:
|
||||
TimeUnitAmountSubClass(double amount, TimeUnit::UTimeUnitFields timeUnitField, int ex, UErrorCode &status) : TimeUnitAmount(amount, timeUnitField, status), extra(ex) { }
|
||||
|
||||
TimeUnitAmountSubClass(const TimeUnitAmountSubClass &that)
|
||||
: TimeUnitAmount(that), extra(that.extra) { }
|
||||
|
||||
TimeUnitAmountSubClass &operator=(const TimeUnitAmountSubClass &that) {
|
||||
TimeUnitAmount::operator=(that);
|
||||
extra = that.extra;
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual UObject* clone() const {
|
||||
return new TimeUnitAmountSubClass(*this);
|
||||
}
|
||||
|
||||
virtual ~TimeUnitAmountSubClass() { }
|
||||
int extra;
|
||||
};
|
||||
|
||||
static TimePeriod create1m59_9996s(UErrorCode &status);
|
||||
static TimePeriod create19m(UErrorCode &status);
|
||||
static TimePeriod create19m28s(UErrorCode &status);
|
||||
static TimePeriod create19m29s(UErrorCode &status);
|
||||
static TimePeriod create1h23_5s(UErrorCode &status);
|
||||
static TimePeriod create1h23s(UErrorCode &status);
|
||||
static TimePeriod create1h23_5m(UErrorCode &status);
|
||||
static TimePeriod create1h0m23s(UErrorCode &status);
|
||||
static TimePeriod create5h17m(UErrorCode &status);
|
||||
static TimePeriod create2y5M3w4d(UErrorCode &status);
|
||||
static TimePeriod create0h0m17s(UErrorCode &status);
|
||||
static TimePeriod create6h56_92m(UErrorCode &status);
|
||||
|
||||
void TimeUnitTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ ) {
|
||||
if (exec) logln("TestSuite TimeUnitTest");
|
||||
switch (index) {
|
||||
|
@ -27,6 +67,11 @@ void TimeUnitTest::runIndexedTest( int32_t index, UBool exec, const char* &name,
|
|||
TESTCASE(1, testAPI);
|
||||
TESTCASE(2, testGreekWithFallback);
|
||||
TESTCASE(3, testGreekWithSanitization);
|
||||
TESTCASE(4, testFormatPeriodEn);
|
||||
TESTCASE(5, testTimePeriodForAmounts);
|
||||
TESTCASE(6, testTimeUnitAmountSubClass);
|
||||
TESTCASE(7, testTimePeriodEquals);
|
||||
TESTCASE(8, testTimePeriodLength);
|
||||
default: name = ""; break;
|
||||
}
|
||||
}
|
||||
|
@ -342,5 +387,315 @@ void TimeUnitTest::testGreekWithSanitization() {
|
|||
delete timeUnitFormat;
|
||||
}
|
||||
|
||||
void TimeUnitTest::testFormatPeriodEn() {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
|
||||
TimePeriodResult fullResults[] = {
|
||||
{create1m59_9996s(status), "1 minute, 59.9996 seconds"},
|
||||
{create19m(status), "19 minutes"},
|
||||
{create1h23_5s(status), "1 hour, 23.5 seconds"},
|
||||
{create1h23_5m(status), "1 hour, 23.5 minutes"},
|
||||
{create1h0m23s(status), "1 hour, 0 minutes, 23 seconds"},
|
||||
{create2y5M3w4d(status), "2 years, 5 months, 3 weeks, 4 days"}};
|
||||
|
||||
TimePeriodResult abbrevResults[] = {
|
||||
{create1m59_9996s(status), "1 min, 59.9996 secs"},
|
||||
{create19m(status), "19 mins"},
|
||||
{create1h23_5s(status), "1 hr, 23.5 secs"},
|
||||
{create1h23_5m(status), "1 hr, 23.5 mins"},
|
||||
{create1h0m23s(status), "1 hr, 0 mins, 23 secs"},
|
||||
{create2y5M3w4d(status), "2 yrs, 5 mths, 3 wks, 4 days"}};
|
||||
|
||||
TimePeriodResult numericResults[] = {
|
||||
{create1m59_9996s(status), "1:59.9996"},
|
||||
{create19m(status), "19 mins"},
|
||||
{create1h23_5s(status), "1:00:23.5"},
|
||||
{create1h0m23s(status), "1:00:23"},
|
||||
{create5h17m(status), "5:17"},
|
||||
{create19m28s(status), "19:28"},
|
||||
{create2y5M3w4d(status), "2 yrs, 5 mths, 3 wks, 4 days"},
|
||||
{create0h0m17s(status), "0:00:17"},
|
||||
{create6h56_92m(status), "6:56.92"}};
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
dataerrln("Unable to create time periods - %s", u_errorName(status));
|
||||
return;
|
||||
}
|
||||
|
||||
LocalPointer<NumberFormat> nf(NumberFormat::createInstance(Locale::getEnglish(), status));
|
||||
if (U_FAILURE(status)) {
|
||||
dataerrln("Unable to create NumberFormat object - %s", u_errorName(status));
|
||||
return;
|
||||
}
|
||||
nf->setMaximumFractionDigits(4);
|
||||
{
|
||||
TimeUnitFormat tuf(Locale::getEnglish(), UTMUTFMT_FULL_STYLE, status);
|
||||
tuf.setNumberFormat(*nf, status);
|
||||
if (U_FAILURE(status)) {
|
||||
dataerrln("Unable to create TimeUnitFormat object - %s", u_errorName(status));
|
||||
return;
|
||||
}
|
||||
verifyFormatTimePeriod(
|
||||
tuf,
|
||||
fullResults,
|
||||
sizeof(fullResults) / sizeof(TimePeriodResult));
|
||||
}
|
||||
{
|
||||
TimeUnitFormat tuf(Locale::getEnglish(), UTMUTFMT_ABBREVIATED_STYLE, status);
|
||||
tuf.setNumberFormat(*nf, status);
|
||||
if (U_FAILURE(status)) {
|
||||
dataerrln("Unable to create TimeUnitFormat object - %s", u_errorName(status));
|
||||
return;
|
||||
}
|
||||
verifyFormatTimePeriod(
|
||||
tuf,
|
||||
abbrevResults,
|
||||
sizeof(abbrevResults) / sizeof(TimePeriodResult));
|
||||
}
|
||||
{
|
||||
TimeUnitFormat tuf(Locale::getEnglish(), UTMUTFMT_NUMERIC_STYLE, status);
|
||||
tuf.setNumberFormat(*nf, status);
|
||||
if (U_FAILURE(status)) {
|
||||
dataerrln("Unable to create TimeUnitFormat object - %s", u_errorName(status));
|
||||
return;
|
||||
}
|
||||
verifyFormatTimePeriod(
|
||||
tuf,
|
||||
numericResults,
|
||||
sizeof(numericResults) / sizeof(TimePeriodResult));
|
||||
}
|
||||
}
|
||||
|
||||
void TimeUnitTest::testTimePeriodLength() {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
int32_t actual = create1h23_5m(status).length();
|
||||
if (U_FAILURE(status)) {
|
||||
dataerrln("Unable to create time period object - %s", u_errorName(status));
|
||||
return;
|
||||
}
|
||||
if (actual != 2) {
|
||||
errln("Expected 2, got %d", actual);
|
||||
}
|
||||
}
|
||||
|
||||
void TimeUnitTest::testTimePeriodForAmounts() {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
TimeUnitAmount _5h(5.0, TimeUnit::UTIMEUNIT_HOUR, status);
|
||||
TimeUnitAmount _3_5h(3.5, TimeUnit::UTIMEUNIT_HOUR, status);
|
||||
TimeUnitAmount _3h(3.0, TimeUnit::UTIMEUNIT_HOUR, status);
|
||||
TimeUnitAmount _5m(5.0, TimeUnit::UTIMEUNIT_MINUTE, status);
|
||||
if (U_FAILURE(status)) {
|
||||
dataerrln("Unable to alocate time unit amounts - %s", u_errorName(status));
|
||||
return;
|
||||
}
|
||||
{
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
TimeUnitAmount *amounts[] = {&_3h, &_5h};
|
||||
int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
|
||||
TimePeriod(amounts, len, status);
|
||||
if (status != U_ILLEGAL_ARGUMENT_ERROR) {
|
||||
errln("Expected U_ILLEGAL_ARGUMENT_ERROR for 3h + 5h, got %s", u_errorName(status));
|
||||
}
|
||||
}
|
||||
{
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
TimePeriod(NULL, 0, status);
|
||||
if (status != U_ILLEGAL_ARGUMENT_ERROR) {
|
||||
errln("Expected U_ILLEGAL_ARGUMENT_ERROR for empty time period, got %s", u_errorName(status));
|
||||
}
|
||||
}
|
||||
{
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
TimeUnitAmount *amounts[] = {&_3_5h, &_5m};
|
||||
int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
|
||||
TimePeriod(amounts, len, status);
|
||||
if (status != U_ILLEGAL_ARGUMENT_ERROR) {
|
||||
errln("Expected U_ILLEGAL_ARGUMENT_ERROR for 3.5h + 5m, got %s", u_errorName(status));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimeUnitTest::testTimePeriodEquals() {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
|
||||
TimePeriod _1h23s = create1h23s(status);
|
||||
|
||||
// Same variable
|
||||
verifyEquals(_1h23s, _1h23s);
|
||||
|
||||
// Different variables same value
|
||||
verifyEquals(_1h23s, TimePeriod(_1h23s));
|
||||
|
||||
// Different fields
|
||||
verifyNotEqual(_1h23s, create1h0m23s(status));
|
||||
|
||||
// Same fields different values
|
||||
verifyNotEqual(create19m28s(status), create19m29s(status));
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
errln("Failure creating TimePeriods, got %s", u_errorName(status));
|
||||
}
|
||||
}
|
||||
|
||||
void TimeUnitTest::testTimeUnitAmountSubClass() {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
TimeUnitAmountSubClass _6h(6.0, TimeUnit::UTIMEUNIT_HOUR, 1, status);
|
||||
TimeUnitAmountSubClass _5m(5.0, TimeUnit::UTIMEUNIT_MINUTE, 2, status);
|
||||
if (U_FAILURE(status)) {
|
||||
dataerrln("Unable to alocate time unit amounts - %s", u_errorName(status));
|
||||
return;
|
||||
}
|
||||
{
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
TimeUnitAmount *amounts[] = {&_6h, &_5m};
|
||||
int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
|
||||
TimePeriod period(amounts, len, status);
|
||||
if (2 != ((const TimeUnitAmountSubClass *) period.getAmount(TimeUnit::UTIMEUNIT_MINUTE))->extra) {
|
||||
errln("Expected polymorphic behavior.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimeUnitTest::verifyFormatTimePeriod(
|
||||
const TimeUnitFormat& tuf,
|
||||
const TimePeriodResult* timePeriodResults,
|
||||
int32_t numResults) {
|
||||
for (int32_t i = 0; i < numResults; i++) {
|
||||
UnicodeString expected(timePeriodResults[i].result, -1, US_INV);
|
||||
expected = expected.unescape();
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
Formattable formattable(new TimePeriod(timePeriodResults[i].timePeriod));
|
||||
UnicodeString actual;
|
||||
FieldPosition pos(0);
|
||||
tuf.format(formattable, actual, pos, status);
|
||||
if (U_FAILURE(status)) {
|
||||
dataerrln("Unable to format time period - %s", u_errorName(status));
|
||||
return;
|
||||
}
|
||||
if (actual != expected) {
|
||||
errln(UnicodeString("Fail: Expected: ") + expected
|
||||
+ UnicodeString(" Got: ") + actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimeUnitTest::verifyEquals(const TimePeriod& lhs, const TimePeriod& rhs) {
|
||||
if (lhs != rhs) {
|
||||
errln("Expected equal.");
|
||||
return;
|
||||
}
|
||||
if (!(lhs == rhs)) {
|
||||
errln("Expected not not equal.");
|
||||
}
|
||||
}
|
||||
|
||||
void TimeUnitTest::verifyNotEqual(const TimePeriod& lhs, const TimePeriod& rhs) {
|
||||
if (lhs == rhs) {
|
||||
errln("Expected not equal.");
|
||||
return;
|
||||
}
|
||||
if (!(lhs != rhs)) {
|
||||
errln("Expected not not not equal.");
|
||||
}
|
||||
}
|
||||
|
||||
static TimePeriod create1m59_9996s(UErrorCode &status) {
|
||||
TimeUnitAmount minutes(1.0, TimeUnit::UTIMEUNIT_MINUTE, status);
|
||||
TimeUnitAmount seconds(59.9996, TimeUnit::UTIMEUNIT_SECOND, status);
|
||||
TimeUnitAmount *amounts[] = {&minutes, &seconds};
|
||||
int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
|
||||
return TimePeriod(amounts, len, status);
|
||||
}
|
||||
|
||||
static TimePeriod create19m(UErrorCode &status) {
|
||||
TimeUnitAmount minutes(19.0, TimeUnit::UTIMEUNIT_MINUTE, status);
|
||||
TimeUnitAmount *amounts[] = {&minutes};
|
||||
int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
|
||||
return TimePeriod(amounts, len, status);
|
||||
}
|
||||
|
||||
static TimePeriod create19m28s(UErrorCode &status) {
|
||||
TimeUnitAmount minutes(19.0, TimeUnit::UTIMEUNIT_MINUTE, status);
|
||||
TimeUnitAmount seconds(28.0, TimeUnit::UTIMEUNIT_SECOND, status);
|
||||
TimeUnitAmount *amounts[] = {&minutes, &seconds};
|
||||
int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
|
||||
return TimePeriod(amounts, len, status);
|
||||
}
|
||||
|
||||
static TimePeriod create19m29s(UErrorCode &status) {
|
||||
TimeUnitAmount minutes(19.0, TimeUnit::UTIMEUNIT_MINUTE, status);
|
||||
TimeUnitAmount seconds(29.0, TimeUnit::UTIMEUNIT_SECOND, status);
|
||||
TimeUnitAmount *amounts[] = {&minutes, &seconds};
|
||||
int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
|
||||
return TimePeriod(amounts, len, status);
|
||||
}
|
||||
|
||||
static TimePeriod create1h23_5s(UErrorCode &status) {
|
||||
TimeUnitAmount hours(1.0, TimeUnit::UTIMEUNIT_HOUR, status);
|
||||
TimeUnitAmount seconds(23.5, TimeUnit::UTIMEUNIT_SECOND, status);
|
||||
TimeUnitAmount *amounts[] = {&hours, &seconds};
|
||||
int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
|
||||
return TimePeriod(amounts, len, status);
|
||||
}
|
||||
|
||||
static TimePeriod create1h23_5m(UErrorCode &status) {
|
||||
TimeUnitAmount hours(1.0, TimeUnit::UTIMEUNIT_HOUR, status);
|
||||
TimeUnitAmount seconds(23.5, TimeUnit::UTIMEUNIT_MINUTE, status);
|
||||
TimeUnitAmount *amounts[] = {&hours, &seconds};
|
||||
int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
|
||||
return TimePeriod(amounts, len, status);
|
||||
}
|
||||
|
||||
static TimePeriod create1h0m23s(UErrorCode &status) {
|
||||
TimeUnitAmount hours(1.0, TimeUnit::UTIMEUNIT_HOUR, status);
|
||||
TimeUnitAmount minutes(0.0, TimeUnit::UTIMEUNIT_MINUTE, status);
|
||||
TimeUnitAmount seconds(23.0, TimeUnit::UTIMEUNIT_SECOND, status);
|
||||
TimeUnitAmount *amounts[] = {&hours, &minutes, &seconds};
|
||||
int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
|
||||
return TimePeriod(amounts, len, status);
|
||||
}
|
||||
|
||||
static TimePeriod create1h23s(UErrorCode &status) {
|
||||
TimeUnitAmount hours(1.0, TimeUnit::UTIMEUNIT_HOUR, status);
|
||||
TimeUnitAmount seconds(23.0, TimeUnit::UTIMEUNIT_SECOND, status);
|
||||
TimeUnitAmount *amounts[] = {&hours, &seconds};
|
||||
int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
|
||||
return TimePeriod(amounts, len, status);
|
||||
}
|
||||
|
||||
static TimePeriod create5h17m(UErrorCode &status) {
|
||||
TimeUnitAmount hours(5.0, TimeUnit::UTIMEUNIT_HOUR, status);
|
||||
TimeUnitAmount minutes(17.0, TimeUnit::UTIMEUNIT_MINUTE, status);
|
||||
TimeUnitAmount *amounts[] = {&hours, &minutes};
|
||||
int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
|
||||
return TimePeriod(amounts, len, status);
|
||||
}
|
||||
|
||||
static TimePeriod create2y5M3w4d(UErrorCode &status) {
|
||||
TimeUnitAmount years(2.0, TimeUnit::UTIMEUNIT_YEAR, status);
|
||||
TimeUnitAmount months(5.0, TimeUnit::UTIMEUNIT_MONTH, status);
|
||||
TimeUnitAmount weeks(3.0, TimeUnit::UTIMEUNIT_WEEK, status);
|
||||
TimeUnitAmount days(4.0, TimeUnit::UTIMEUNIT_DAY, status);
|
||||
TimeUnitAmount *amounts[] = {&years, &months, &weeks, &days};
|
||||
int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
|
||||
return TimePeriod(amounts, len, status);
|
||||
}
|
||||
|
||||
static TimePeriod create0h0m17s(UErrorCode &status) {
|
||||
TimeUnitAmount hours(0.0, TimeUnit::UTIMEUNIT_HOUR, status);
|
||||
TimeUnitAmount minutes(0.0, TimeUnit::UTIMEUNIT_MINUTE, status);
|
||||
TimeUnitAmount seconds(17.0, TimeUnit::UTIMEUNIT_SECOND, status);
|
||||
TimeUnitAmount *amounts[] = {&hours, &minutes, &seconds};
|
||||
int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
|
||||
return TimePeriod(amounts, len, status);
|
||||
}
|
||||
|
||||
static TimePeriod create6h56_92m(UErrorCode &status) {
|
||||
TimeUnitAmount hours(6.0, TimeUnit::UTIMEUNIT_HOUR, status);
|
||||
TimeUnitAmount minutes(56.92, TimeUnit::UTIMEUNIT_MINUTE, status);
|
||||
TimeUnitAmount *amounts[] = {&hours, &minutes};
|
||||
int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
|
||||
return TimePeriod(amounts, len, status);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/********************************************************************
|
||||
* COPYRIGHT:
|
||||
* Copyright (c) 2008-2012, International Business Machines Corporation
|
||||
* Copyright (c) 2008-2013, International Business Machines Corporation
|
||||
* and others. All Rights Reserved.
|
||||
********************************************************************/
|
||||
|
||||
|
@ -14,6 +14,12 @@
|
|||
#include "unicode/locid.h"
|
||||
#include "intltest.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
class TimeUnitFormat;
|
||||
class TimePeriod;
|
||||
U_NAMESPACE_END
|
||||
struct TimePeriodResult;
|
||||
|
||||
/**
|
||||
* Test basic functionality of various API functions
|
||||
**/
|
||||
|
@ -49,6 +55,27 @@ public:
|
|||
* As of CLDR shiped in ICU4.8, Greek is one such language.
|
||||
*/
|
||||
void testGreekWithSanitization();
|
||||
|
||||
void testFormatPeriodEn();
|
||||
|
||||
void testTimePeriodLength();
|
||||
|
||||
void testTimePeriodEquals();
|
||||
|
||||
void testTimePeriodForAmounts();
|
||||
|
||||
void testTimeUnitAmountSubClass();
|
||||
|
||||
void verifyEquals(const TimePeriod&, const TimePeriod&);
|
||||
|
||||
void verifyNotEqual(const TimePeriod&, const TimePeriod&);
|
||||
|
||||
void verifyFormatTimePeriod(
|
||||
const TimeUnitFormat& tuf,
|
||||
const TimePeriodResult* timePeriodResults,
|
||||
int32_t numResults);
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
|
Loading…
Add table
Reference in a new issue