+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
/*
******************************************************************************
* Copyright (C) 2014-2016, International Business Machines Corporation and
#if !UCONFIG_NO_FORMATTING && !UCONFIG_NO_BREAK_ITERATION
+#include <cmath>
+#include <functional>
#include "unicode/dtfmtsym.h"
+#include "unicode/ucasemap.h"
#include "unicode/ureldatefmt.h"
#include "unicode/udisplaycontext.h"
#include "unicode/unum.h"
#include "sharednumberformat.h"
#include "standardplural.h"
#include "unifiedcache.h"
+#include "util.h"
+#include "formatted_string_builder.h"
+#include "number_utypes.h"
+#include "number_modifiers.h"
+#include "formattedval_impl.h"
+#include "number_utils.h"
// Copied from uscript_props.cpp
-static UMutex gBrkIterMutex = U_MUTEX_INITIALIZER;
-
U_NAMESPACE_BEGIN
// RelativeDateTimeFormatter specific data for a single locale
class RelativeDateTimeCacheData: public SharedObject {
public:
- RelativeDateTimeCacheData() : combinedDateAndTime(NULL) {
+ RelativeDateTimeCacheData() : combinedDateAndTime(nullptr) {
// Initialize the cache arrays
for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
- for (int32_t relUnit = 0; relUnit < UDAT_RELATIVE_UNIT_COUNT; ++relUnit) {
+ for (int32_t relUnit = 0; relUnit < UDAT_REL_UNIT_COUNT; ++relUnit) {
for (int32_t pl = 0; pl < StandardPlural::COUNT; ++pl) {
- relativeUnitsFormatters[style][relUnit][0][pl] = NULL;
- relativeUnitsFormatters[style][relUnit][1][pl] = NULL;
+ relativeUnitsFormatters[style][relUnit][0][pl] = nullptr;
+ relativeUnitsFormatters[style][relUnit][1][pl] = nullptr;
}
}
}
// e.g., Next Tuesday; Yesterday; etc. For third index, 0
// means past, e.g., 5 days ago; 1 means future, e.g., in 5 days.
SimpleFormatter *relativeUnitsFormatters[UDAT_STYLE_COUNT]
- [UDAT_RELATIVE_UNIT_COUNT][2][StandardPlural::COUNT];
+ [UDAT_REL_UNIT_COUNT][2][StandardPlural::COUNT];
const UnicodeString& getAbsoluteUnitString(int32_t fStyle,
UDateAbsoluteUnit unit,
UDateRelativeUnit unit,
int32_t pastFutureIndex,
int32_t pluralUnit) const;
+ const SimpleFormatter* getRelativeDateTimeUnitFormatter(int32_t fStyle,
+ URelativeDateTimeUnit unit,
+ int32_t pastFutureIndex,
+ int32_t pluralUnit) const;
const UnicodeString emptyString;
RelativeDateTimeCacheData::~RelativeDateTimeCacheData() {
// clear out the cache arrays
for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
- for (int32_t relUnit = 0; relUnit < UDAT_RELATIVE_UNIT_COUNT; ++relUnit) {
+ for (int32_t relUnit = 0; relUnit < UDAT_REL_UNIT_COUNT; ++relUnit) {
for (int32_t pl = 0; pl < StandardPlural::COUNT; ++pl) {
delete relativeUnitsFormatters[style][relUnit][0][pl];
delete relativeUnitsFormatters[style][relUnit][1][pl];
return emptyString;
}
- // Use fallback cache for SimpleFormatter relativeUnits.
const SimpleFormatter* RelativeDateTimeCacheData::getRelativeUnitFormatter(
int32_t fStyle,
UDateRelativeUnit unit,
int32_t pastFutureIndex,
int32_t pluralUnit) const {
- int32_t style = fStyle;
- do {
- if (relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit] != NULL) {
- return relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit];
+ URelativeDateTimeUnit rdtunit = UDAT_REL_UNIT_COUNT;
+ switch (unit) {
+ case UDAT_RELATIVE_YEARS: rdtunit = UDAT_REL_UNIT_YEAR; break;
+ case UDAT_RELATIVE_MONTHS: rdtunit = UDAT_REL_UNIT_MONTH; break;
+ case UDAT_RELATIVE_WEEKS: rdtunit = UDAT_REL_UNIT_WEEK; break;
+ case UDAT_RELATIVE_DAYS: rdtunit = UDAT_REL_UNIT_DAY; break;
+ case UDAT_RELATIVE_HOURS: rdtunit = UDAT_REL_UNIT_HOUR; break;
+ case UDAT_RELATIVE_MINUTES: rdtunit = UDAT_REL_UNIT_MINUTE; break;
+ case UDAT_RELATIVE_SECONDS: rdtunit = UDAT_REL_UNIT_SECOND; break;
+ default: // a unit that the above method does not handle
+ return nullptr;
+ }
+
+ return getRelativeDateTimeUnitFormatter(fStyle, rdtunit, pastFutureIndex, pluralUnit);
+ }
+
+ // Use fallback cache for SimpleFormatter relativeUnits.
+ const SimpleFormatter* RelativeDateTimeCacheData::getRelativeDateTimeUnitFormatter(
+ int32_t fStyle,
+ URelativeDateTimeUnit unit,
+ int32_t pastFutureIndex,
+ int32_t pluralUnit) const {
+ while (true) {
+ int32_t style = fStyle;
+ do {
+ if (relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit] != nullptr) {
+ return relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit];
+ }
+ style = fallBackCache[style];
+ } while (style != -1);
+
+ if (pluralUnit == StandardPlural::OTHER) {
+ break;
}
- style = fallBackCache[style];
- } while (style != -1);
- return NULL; // No formatter found.
+ pluralUnit = StandardPlural::OTHER;
+ }
+ return nullptr; // No formatter found.
}
static UBool getStringWithFallback(
/**
* Sink for enumerating all of the measurement unit display names.
- * Contains inner sink classes, each one corresponding to a level of the resource table.
*
* More specific bundles (en_GB) are enumerated before their parents (en_001, en, root):
* Only store a value if it is still missing, that is, it has not been overridden.
- *
- * C++: Each inner sink class has a reference to the main outer sink.
*/
-struct RelDateTimeFmtDataSink : public ResourceTableSink {
+struct RelDateTimeFmtDataSink : public ResourceSink {
/**
* Sink for patterns for relative dates and times. For example,
// Converts the generic units to UDAT_RELATIVE version.
switch (genUnit) {
case SECOND:
- return UDAT_RELATIVE_SECONDS;
+ return UDAT_REL_UNIT_SECOND;
case MINUTE:
- return UDAT_RELATIVE_MINUTES;
+ return UDAT_REL_UNIT_MINUTE;
case HOUR:
- return UDAT_RELATIVE_HOURS;
+ return UDAT_REL_UNIT_HOUR;
case DAY:
- return UDAT_RELATIVE_DAYS;
+ return UDAT_REL_UNIT_DAY;
case WEEK:
- return UDAT_RELATIVE_WEEKS;
+ return UDAT_REL_UNIT_WEEK;
case MONTH:
- return UDAT_RELATIVE_MONTHS;
- /*
- * case QUARTER:
- * return UDATE_RELATIVE_QUARTERS;
- */
+ return UDAT_REL_UNIT_MONTH;
+ case QUARTER:
+ return UDAT_REL_UNIT_QUARTER;
case YEAR:
- return UDAT_RELATIVE_YEARS;
+ return UDAT_REL_UNIT_YEAR;
+ case SUNDAY:
+ return UDAT_REL_UNIT_SUNDAY;
+ case MONDAY:
+ return UDAT_REL_UNIT_MONDAY;
+ case TUESDAY:
+ return UDAT_REL_UNIT_TUESDAY;
+ case WEDNESDAY:
+ return UDAT_REL_UNIT_WEDNESDAY;
+ case THURSDAY:
+ return UDAT_REL_UNIT_THURSDAY;
+ case FRIDAY:
+ return UDAT_REL_UNIT_FRIDAY;
+ case SATURDAY:
+ return UDAT_REL_UNIT_SATURDAY;
default:
return -1;
}
return UDAT_ABSOLUTE_WEEK;
case MONTH:
return UDAT_ABSOLUTE_MONTH;
- /* TODO: Add in QUARTER
- * case QUARTER:
- * return UDAT_ABSOLUTE_QUARTER;
- */
+ case QUARTER:
+ return UDAT_ABSOLUTE_QUARTER;
case YEAR:
return UDAT_ABSOLUTE_YEAR;
case SUNDAY:
return UDAT_ABSOLUTE_FRIDAY;
case SATURDAY:
return UDAT_ABSOLUTE_SATURDAY;
+ case HOUR:
+ return UDAT_ABSOLUTE_HOUR;
+ case MINUTE:
+ return UDAT_ABSOLUTE_MINUTE;
default:
return -1;
}
return -1;
}
- // Sinks for additional levels under /fields/*/relative/ and /fields/*/relativeTime/
-
- /**
- * Make list of simplePatternFmtList, for past and for future.
- * Set a SimpleFormatter for the <style, relative unit, plurality>
- *
- * Fill in values for the particular plural given, e.g., ONE, FEW, OTHER, etc.
- */
- struct RelDateTimeDetailSink : public ResourceTableSink {
- RelDateTimeDetailSink(RelDateTimeFmtDataSink &sink) : outer(sink) {}
- ~RelDateTimeDetailSink();
-
- virtual void put(const char *key, const ResourceValue &value,
- UErrorCode &errorCode) {
- if (U_FAILURE(errorCode)) { return; }
-
- outer.relUnitIndex = relUnitFromGeneric(outer.genericUnit);
- if (outer.relUnitIndex < 0) {
- return;
- }
-
- /* Make two lists of simplePatternFmtList, one for past and one for future.
- * Set a SimpleFormatter pattern for the <style, relative unit, plurality>
- *
- * Fill in values for the particular plural given, e.g., ONE, FEW, OTHER, etc.
- */
- int32_t pluralIndex = StandardPlural::indexOrNegativeFromString(key);
- if (pluralIndex >= 0) {
- SimpleFormatter **patterns =
- outer.outputData.relativeUnitsFormatters[outer.style][outer.relUnitIndex]
- [outer.pastFutureIndex];
- // Only set if not already established.
- if (patterns[pluralIndex] == NULL) {
- patterns[pluralIndex] = new SimpleFormatter(
- value.getUnicodeString(errorCode), 0, 1, errorCode);
- if (patterns[pluralIndex] == NULL) {
- errorCode = U_MEMORY_ALLOCATION_ERROR;
- }
- }
- }
- }
-
- RelDateTimeFmtDataSink &outer;
- } relDateTimeDetailSink;
-
- /*
- * Handles "relativeTime" entries, e.g., under "day", "hour", "minute",
- * "minute-short", etc.
- */
- struct RelativeTimeSink : public ResourceTableSink {
- RelativeTimeSink(RelDateTimeFmtDataSink &sink) : outer(sink) {}
- ~RelativeTimeSink();
-
- virtual ResourceTableSink *getOrCreateTableSink(
- const char *key, int32_t /* initialSize */, UErrorCode& errorCode) {
- if (U_FAILURE(errorCode)) { return NULL; }
- outer.relUnitIndex = relUnitFromGeneric(outer.genericUnit);
- if (outer.relUnitIndex < 0) {
- return NULL;
- }
-
- if (uprv_strcmp(key, "past") == 0) {
- outer.pastFutureIndex = 0;
- } else if (uprv_strcmp(key, "future") == 0) {
- outer.pastFutureIndex = 1;
- } else {
- // Unknown key.
- return NULL;
- }
- return &outer.relDateTimeDetailSink;
- }
-
- RelDateTimeFmtDataSink &outer;
- } relativeTimeSink;
-
- /*
- * Handles "relative" entries, e.g., under "day", "day-short", "fri",
- * "fri-narrow", "fri-short", etc.
- */
- struct RelativeSink : public ResourceTableSink {
- RelativeSink(RelDateTimeFmtDataSink &sink) : outer(sink) {}
- ~RelativeSink();
-
- virtual void put(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
- if (U_FAILURE(errorCode)) { return; }
- int32_t direction = keyToDirection(key);
- if (direction < 0) {
- return;
- }
-
- int32_t relUnitIndex = relUnitFromGeneric(outer.genericUnit);
- if (relUnitIndex == UDAT_RELATIVE_SECONDS &&
- direction == UDAT_DIRECTION_THIS &&
- outer.outputData.absoluteUnits[outer.style][UDAT_ABSOLUTE_NOW]
- [UDAT_DIRECTION_PLAIN].isEmpty()) {
- // Handle "NOW"
- outer.outputData.absoluteUnits[outer.style][UDAT_ABSOLUTE_NOW]
- [UDAT_DIRECTION_PLAIN].fastCopyFrom(value.getUnicodeString(errorCode));
- }
-
- int32_t absUnitIndex = absUnitFromGeneric(outer.genericUnit);
- if (absUnitIndex < 0) {
- return;
- }
- // Only reset if slot is empty.
- if (outer.outputData.absoluteUnits[outer.style][absUnitIndex][direction].isEmpty()) {
- outer.outputData.absoluteUnits[outer.style][absUnitIndex]
- [direction].fastCopyFrom(value.getUnicodeString(errorCode));
- }
- }
-
- RelDateTimeFmtDataSink &outer;
- } relativeSink;
-
- /*
- * Handles entries under "fields", recognizing "relative" and "relativeTime" entries.
- */
- struct UnitSink : public ResourceTableSink {
- UnitSink(RelDateTimeFmtDataSink &sink) : outer(sink) {}
- ~UnitSink();
-
- virtual void put(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
- if (U_FAILURE(errorCode)) { return; }
- if (uprv_strcmp(key, "dn") != 0) {
- return;
- }
-
- // Handle Display Name for PLAIN direction for some units.
- int32_t absUnit = absUnitFromGeneric(outer.genericUnit);
- if (absUnit < 0) {
- return; // Not interesting.
- }
-
- // TODO(Travis Keep): This is a hack to get around CLDR bug 6818.
- UnicodeString displayName = value.getUnicodeString(errorCode);
- if (U_SUCCESS(errorCode)) {
- if (uprv_strcmp("en", outer.sinkLocaleId) == 0) {
- displayName.toLower();
- }
- }
- // end hack
-
- // Store displayname if not set.
- if (outer.outputData.absoluteUnits[outer.style]
- [absUnit][UDAT_DIRECTION_PLAIN].isEmpty()) {
- outer.outputData.absoluteUnits[outer.style]
- [absUnit][UDAT_DIRECTION_PLAIN].fastCopyFrom(displayName);
- return;
- }
- }
-
- virtual ResourceTableSink *getOrCreateTableSink(
- const char *key, int32_t /* initialSize */, UErrorCode &errorCode) {
- if (U_FAILURE(errorCode)) { return NULL; }
- if (uprv_strcmp(key, "relative") == 0) {
- return &outer.relativeSink;
- } else if (uprv_strcmp(key, "relativeTime") == 0) {
- return &outer.relativeTimeSink;
- }
- return NULL;
- }
-
- RelDateTimeFmtDataSink &outer;
- } unitSink;
-
- // For hack for locale "en".
- // TODO(Travis Keep): This is a hack to get around CLDR bug 6818.
- const char* sinkLocaleId;
-
// Values kept between levels of parsing the CLDR data.
int32_t pastFutureIndex; // 0 == past or 1 == future
UDateRelativeDateTimeFormatterStyle style; // {LONG, SHORT, NARROW}
RelAbsUnit genericUnit;
- int32_t relUnitIndex;
- int32_t absUnitIndex;
RelativeDateTimeCacheData &outputData;
// Constructor
- RelDateTimeFmtDataSink(RelativeDateTimeCacheData& cacheData, const char* localeId)
- : relDateTimeDetailSink(*this), relativeTimeSink(*this), relativeSink(*this),
- unitSink(*this), sinkLocaleId(localeId), outputData(cacheData) {
+ RelDateTimeFmtDataSink(RelativeDateTimeCacheData& cacheData)
+ : outputData(cacheData) {
// Clear cacheData.fallBackCache
cacheData.fallBackCache[UDAT_STYLE_LONG] = -1;
cacheData.fallBackCache[UDAT_STYLE_SHORT] = -1;
// Utility functions
static UDateRelativeDateTimeFormatterStyle styleFromString(const char *s) {
- int32_t len = uprv_strlen(s);
+ int32_t len = static_cast<int32_t>(uprv_strlen(s));
if (len >= 7 && uprv_strcmp(s + len - 7, "-narrow") == 0) {
return UDAT_STYLE_NARROW;
}
return INVALID_UNIT;
}
- // Member functions of top level sink.
- virtual void put(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
- // Only handle aliases, storing information about alias fallback.
+ void handlePlainDirection(ResourceValue &value, UErrorCode &errorCode) {
+ // Handle Display Name for PLAIN direction for some units.
+ if (U_FAILURE(errorCode)) { return; }
+
+ int32_t absUnit = absUnitFromGeneric(genericUnit);
+ if (absUnit < 0) {
+ return; // Not interesting.
+ }
+
+ // Store displayname if not set.
+ if (outputData.absoluteUnits[style]
+ [absUnit][UDAT_DIRECTION_PLAIN].isEmpty()) {
+ outputData.absoluteUnits[style]
+ [absUnit][UDAT_DIRECTION_PLAIN].fastCopyFrom(value.getUnicodeString(errorCode));
+ return;
+ }
+ }
+
+ void consumeTableRelative(const char *key, ResourceValue &value, UErrorCode &errorCode) {
+ ResourceTable unitTypesTable = value.getTable(errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+
+ for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
+ if (value.getType() == URES_STRING) {
+ int32_t direction = keyToDirection(key);
+ if (direction < 0) {
+ continue;
+ }
+
+ int32_t relUnitIndex = relUnitFromGeneric(genericUnit);
+ if (relUnitIndex == UDAT_REL_UNIT_SECOND && uprv_strcmp(key, "0") == 0 &&
+ outputData.absoluteUnits[style][UDAT_ABSOLUTE_NOW][UDAT_DIRECTION_PLAIN].isEmpty()) {
+ // Handle "NOW"
+ outputData.absoluteUnits[style][UDAT_ABSOLUTE_NOW]
+ [UDAT_DIRECTION_PLAIN].fastCopyFrom(value.getUnicodeString(errorCode));
+ }
- if (U_SUCCESS(errorCode)) {
- if (value.getType() != URES_ALIAS) {
- return;
+ int32_t absUnitIndex = absUnitFromGeneric(genericUnit);
+ if (absUnitIndex < 0) {
+ continue;
+ }
+ // Only reset if slot is empty.
+ if (outputData.absoluteUnits[style][absUnitIndex][direction].isEmpty()) {
+ outputData.absoluteUnits[style][absUnitIndex]
+ [direction].fastCopyFrom(value.getUnicodeString(errorCode));
+ }
}
- const UnicodeString valueStr = value.getAliasUnicodeString(errorCode);
- if (U_SUCCESS(errorCode)) {
- UDateRelativeDateTimeFormatterStyle sourceStyle= styleFromString(key);
- UDateRelativeDateTimeFormatterStyle targetStyle =
- styleFromAliasUnicodeString(valueStr);
-
- if (sourceStyle == targetStyle) {
- errorCode = U_INVALID_FORMAT_ERROR;
- return;
+ }
+ }
+
+ void consumeTimeDetail(int32_t relUnitIndex,
+ const char *key, ResourceValue &value, UErrorCode &errorCode) {
+ ResourceTable unitTypesTable = value.getTable(errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+
+ for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
+ if (value.getType() == URES_STRING) {
+ int32_t pluralIndex = StandardPlural::indexOrNegativeFromString(key);
+ if (pluralIndex >= 0) {
+ SimpleFormatter **patterns =
+ outputData.relativeUnitsFormatters[style][relUnitIndex]
+ [pastFutureIndex];
+ // Only set if not already established.
+ if (patterns[pluralIndex] == nullptr) {
+ patterns[pluralIndex] = new SimpleFormatter(
+ value.getUnicodeString(errorCode), 0, 1, errorCode);
+ if (patterns[pluralIndex] == nullptr) {
+ errorCode = U_MEMORY_ALLOCATION_ERROR;
+ }
+ }
}
- if (outputData.fallBackCache[sourceStyle] != -1 &&
- outputData.fallBackCache[sourceStyle] != targetStyle) {
- errorCode = U_INVALID_FORMAT_ERROR;
- return;
+ }
+ }
+ }
+
+ void consumeTableRelativeTime(const char *key, ResourceValue &value, UErrorCode &errorCode) {
+ ResourceTable relativeTimeTable = value.getTable(errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+
+ int32_t relUnitIndex = relUnitFromGeneric(genericUnit);
+ if (relUnitIndex < 0) {
+ return;
+ }
+ for (int32_t i = 0; relativeTimeTable.getKeyAndValue(i, key, value); ++i) {
+ if (uprv_strcmp(key, "past") == 0) {
+ pastFutureIndex = 0;
+ } else if (uprv_strcmp(key, "future") == 0) {
+ pastFutureIndex = 1;
+ } else {
+ // Unknown key.
+ continue;
+ }
+ consumeTimeDetail(relUnitIndex, key, value, errorCode);
+ }
+ }
+
+ void consumeAlias(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
+
+ UDateRelativeDateTimeFormatterStyle sourceStyle = styleFromString(key);
+ const UnicodeString valueStr = value.getAliasUnicodeString(errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+
+ UDateRelativeDateTimeFormatterStyle targetStyle =
+ styleFromAliasUnicodeString(valueStr);
+
+ if (sourceStyle == targetStyle) {
+ errorCode = U_INVALID_FORMAT_ERROR;
+ return;
+ }
+ if (outputData.fallBackCache[sourceStyle] != -1 &&
+ outputData.fallBackCache[sourceStyle] != targetStyle) {
+ errorCode = U_INVALID_FORMAT_ERROR;
+ return;
+ }
+ outputData.fallBackCache[sourceStyle] = targetStyle;
+ }
+
+ void consumeTimeUnit(const char *key, ResourceValue &value, UErrorCode &errorCode) {
+ ResourceTable unitTypesTable = value.getTable(errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+
+ for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
+ // Handle display name.
+ if (uprv_strcmp(key, "dn") == 0 && value.getType() == URES_STRING) {
+ handlePlainDirection(value, errorCode);
+ }
+ if (value.getType() == URES_TABLE) {
+ if (uprv_strcmp(key, "relative") == 0) {
+ consumeTableRelative(key, value, errorCode);
+ } else if (uprv_strcmp(key, "relativeTime") == 0) {
+ consumeTableRelativeTime(key, value, errorCode);
}
- outputData.fallBackCache[sourceStyle] = targetStyle;
}
}
- return;
}
- // Top level sink
- virtual ResourceTableSink *getOrCreateTableSink(
- const char *key, int32_t /* initialSize */, UErrorCode& /* errorCode */) {
- style= styleFromString(key);
- int32_t unitSize = uprv_strlen(key) - styleSuffixLength(style);
- genericUnit = unitOrNegativeFromString(key, unitSize);
- if (style < 0 || genericUnit == INVALID_UNIT) {
- return NULL;
- }
- return &unitSink;
+ virtual void put(const char *key, ResourceValue &value,
+ UBool /*noFallback*/, UErrorCode &errorCode) {
+ // Main entry point to sink
+ ResourceTable table = value.getTable(errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+ for (int32_t i = 0; table.getKeyAndValue(i, key, value); ++i) {
+ if (value.getType() == URES_ALIAS) {
+ consumeAlias(key, value, errorCode);
+ } else {
+ style = styleFromString(key);
+ int32_t unitSize = static_cast<int32_t>(uprv_strlen(key)) - styleSuffixLength(style);
+ genericUnit = unitOrNegativeFromString(key, unitSize);
+ if (style >= 0 && genericUnit != INVALID_UNIT) {
+ consumeTimeUnit(key, value, errorCode);
+ }
+ }
+ }
}
+
};
// Virtual destructors must be defined out of line.
-RelDateTimeFmtDataSink::RelDateTimeDetailSink::~RelDateTimeDetailSink() {}
-RelDateTimeFmtDataSink::RelativeTimeSink::~RelativeTimeSink() {}
-RelDateTimeFmtDataSink::RelativeSink::~RelativeSink() {}
-RelDateTimeFmtDataSink::UnitSink::~UnitSink() {}
RelDateTimeFmtDataSink::~RelDateTimeFmtDataSink() {}
-
} // namespace
-DateFormatSymbols::DtWidthType styleToDateFormatSymbolWidth[UDAT_STYLE_COUNT] = {
+static const DateFormatSymbols::DtWidthType styleToDateFormatSymbolWidth[UDAT_STYLE_COUNT] = {
DateFormatSymbols::WIDE, DateFormatSymbols::SHORT, DateFormatSymbols::NARROW
};
[UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT],
const char* localeId,
UErrorCode& status) {
+ if (U_FAILURE(status)) {
+ return;
+ }
Locale locale(localeId);
DateFormatSymbols dfSym(locale, status);
+ if (U_FAILURE(status)) {
+ return;
+ }
for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
DateFormatSymbols::DtWidthType dtfmtWidth = styleToDateFormatSymbolWidth[style];
int32_t count;
RelativeDateTimeCacheData &cacheData,
const char* localeId,
UErrorCode &status) {
- RelDateTimeFmtDataSink sink(cacheData, localeId);
- ures_getAllTableItemsWithFallback(resource, "fields", sink, status);
+
+ RelDateTimeFmtDataSink sink(cacheData);
+
+ ures_getAllItemsWithFallback(resource, "fields", sink, status);
+ if (U_FAILURE(status)) {
+ return false;
+ }
// Get the weekday names from DateFormatSymbols.
loadWeekdayNames(cacheData.absoluteUnits, localeId, status);
.append("/DateTimePatterns", status);
LocalUResourceBundlePointer topLevel(
ures_getByKeyWithFallback(
- resource, pathBuffer.data(), NULL, &status));
+ resource, pathBuffer.data(), nullptr, &status));
if (U_FAILURE(status)) {
return FALSE;
}
template<> U_I18N_API
const RelativeDateTimeCacheData *LocaleCacheKey<RelativeDateTimeCacheData>::createObject(const void * /*unused*/, UErrorCode &status) const {
const char *localeId = fLoc.getName();
- LocalUResourceBundlePointer topLevel(ures_open(NULL, localeId, &status));
+ LocalUResourceBundlePointer topLevel(ures_open(nullptr, localeId, &status));
if (U_FAILURE(status)) {
- return NULL;
+ return nullptr;
}
LocalPointer<RelativeDateTimeCacheData> result(
new RelativeDateTimeCacheData());
if (result.isNull()) {
status = U_MEMORY_ALLOCATION_ERROR;
- return NULL;
+ return nullptr;
}
if (!loadUnitData(
topLevel.getAlias(),
*result,
localeId,
status)) {
- return NULL;
+ return nullptr;
}
UnicodeString dateTimePattern;
if (!getDateTimePattern(topLevel.getAlias(), dateTimePattern, status)) {
- return NULL;
+ return nullptr;
}
result->adoptCombinedDateAndTime(
- new SimpleFormatter(dateTimePattern, 2, 2, status));
+ new SimpleFormatter(dateTimePattern, 2, 2, TRUE, status));
if (U_FAILURE(status)) {
- return NULL;
+ return nullptr;
}
result->addRef();
return result.orphan();
}
+
+
+static constexpr number::impl::Field kRDTNumericField
+ = StringBuilderFieldUtils::compress<UFIELD_CATEGORY_RELATIVE_DATETIME, UDAT_REL_NUMERIC_FIELD>();
+
+static constexpr number::impl::Field kRDTLiteralField
+ = StringBuilderFieldUtils::compress<UFIELD_CATEGORY_RELATIVE_DATETIME, UDAT_REL_LITERAL_FIELD>();
+
+class FormattedRelativeDateTimeData : public FormattedValueStringBuilderImpl {
+public:
+ FormattedRelativeDateTimeData() : FormattedValueStringBuilderImpl(kRDTNumericField) {}
+ virtual ~FormattedRelativeDateTimeData();
+};
+
+FormattedRelativeDateTimeData::~FormattedRelativeDateTimeData() = default;
+
+
+UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedRelativeDateTime)
+
+
RelativeDateTimeFormatter::RelativeDateTimeFormatter(UErrorCode& status) :
- fCache(NULL),
- fNumberFormat(NULL),
- fPluralRules(NULL),
+ fCache(nullptr),
+ fNumberFormat(nullptr),
+ fPluralRules(nullptr),
fStyle(UDAT_STYLE_LONG),
fContext(UDISPCTX_CAPITALIZATION_NONE),
- fOptBreakIterator(NULL) {
- init(NULL, NULL, status);
+ fOptBreakIterator(nullptr) {
+ init(nullptr, nullptr, status);
}
RelativeDateTimeFormatter::RelativeDateTimeFormatter(
const Locale& locale, UErrorCode& status) :
- fCache(NULL),
- fNumberFormat(NULL),
- fPluralRules(NULL),
+ fCache(nullptr),
+ fNumberFormat(nullptr),
+ fPluralRules(nullptr),
fStyle(UDAT_STYLE_LONG),
fContext(UDISPCTX_CAPITALIZATION_NONE),
- fOptBreakIterator(NULL),
+ fOptBreakIterator(nullptr),
fLocale(locale) {
- init(NULL, NULL, status);
+ init(nullptr, nullptr, status);
}
RelativeDateTimeFormatter::RelativeDateTimeFormatter(
const Locale& locale, NumberFormat *nfToAdopt, UErrorCode& status) :
- fCache(NULL),
- fNumberFormat(NULL),
- fPluralRules(NULL),
+ fCache(nullptr),
+ fNumberFormat(nullptr),
+ fPluralRules(nullptr),
fStyle(UDAT_STYLE_LONG),
fContext(UDISPCTX_CAPITALIZATION_NONE),
- fOptBreakIterator(NULL),
+ fOptBreakIterator(nullptr),
fLocale(locale) {
- init(nfToAdopt, NULL, status);
+ init(nfToAdopt, nullptr, status);
}
RelativeDateTimeFormatter::RelativeDateTimeFormatter(
UDateRelativeDateTimeFormatterStyle styl,
UDisplayContext capitalizationContext,
UErrorCode& status) :
- fCache(NULL),
- fNumberFormat(NULL),
- fPluralRules(NULL),
+ fCache(nullptr),
+ fNumberFormat(nullptr),
+ fPluralRules(nullptr),
fStyle(styl),
fContext(capitalizationContext),
- fOptBreakIterator(NULL),
+ fOptBreakIterator(nullptr),
fLocale(locale) {
if (U_FAILURE(status)) {
return;
}
init(nfToAdopt, bi, status);
} else {
- init(nfToAdopt, NULL, status);
+ init(nfToAdopt, nullptr, status);
}
}
fCache->addRef();
fNumberFormat->addRef();
fPluralRules->addRef();
- if (fOptBreakIterator != NULL) {
+ if (fOptBreakIterator != nullptr) {
fOptBreakIterator->addRef();
}
}
}
RelativeDateTimeFormatter::~RelativeDateTimeFormatter() {
- if (fCache != NULL) {
+ if (fCache != nullptr) {
fCache->removeRef();
}
- if (fNumberFormat != NULL) {
+ if (fNumberFormat != nullptr) {
fNumberFormat->removeRef();
}
- if (fPluralRules != NULL) {
+ if (fPluralRules != nullptr) {
fPluralRules->removeRef();
}
- if (fOptBreakIterator != NULL) {
+ if (fOptBreakIterator != nullptr) {
fOptBreakIterator->removeRef();
}
}
return fStyle;
}
-UnicodeString& RelativeDateTimeFormatter::format(
- double quantity, UDateDirection direction, UDateRelativeUnit unit,
- UnicodeString& appendTo, UErrorCode& status) const {
+
+// To reduce boilerplate code, we use a helper function that forwards variadic
+// arguments to the formatImpl function.
+
+template<typename F, typename... Args>
+UnicodeString& RelativeDateTimeFormatter::doFormat(
+ F callback,
+ UnicodeString& appendTo,
+ UErrorCode& status,
+ Args... args) const {
+ FormattedRelativeDateTimeData output;
+ (this->*callback)(std::forward<Args>(args)..., output, status);
if (U_FAILURE(status)) {
return appendTo;
}
+ UnicodeString result = output.getStringRef().toUnicodeString();
+ return appendTo.append(adjustForContext(result));
+}
+
+template<typename F, typename... Args>
+FormattedRelativeDateTime RelativeDateTimeFormatter::doFormatToValue(
+ F callback,
+ UErrorCode& status,
+ Args... args) const {
+ if (!checkNoAdjustForContext(status)) {
+ return FormattedRelativeDateTime(status);
+ }
+ LocalPointer<FormattedRelativeDateTimeData> output(
+ new FormattedRelativeDateTimeData(), status);
+ if (U_FAILURE(status)) {
+ return FormattedRelativeDateTime(status);
+ }
+ (this->*callback)(std::forward<Args>(args)..., *output, status);
+ output->getStringRef().writeTerminator(status);
+ return FormattedRelativeDateTime(output.orphan());
+}
+
+UnicodeString& RelativeDateTimeFormatter::format(
+ double quantity,
+ UDateDirection direction,
+ UDateRelativeUnit unit,
+ UnicodeString& appendTo,
+ UErrorCode& status) const {
+ return doFormat(
+ &RelativeDateTimeFormatter::formatImpl,
+ appendTo,
+ status,
+ quantity,
+ direction,
+ unit);
+}
+
+FormattedRelativeDateTime RelativeDateTimeFormatter::formatToValue(
+ double quantity,
+ UDateDirection direction,
+ UDateRelativeUnit unit,
+ UErrorCode& status) const {
+ return doFormatToValue(
+ &RelativeDateTimeFormatter::formatImpl,
+ status,
+ quantity,
+ direction,
+ unit);
+}
+
+void RelativeDateTimeFormatter::formatImpl(
+ double quantity,
+ UDateDirection direction,
+ UDateRelativeUnit unit,
+ FormattedRelativeDateTimeData& output,
+ UErrorCode& status) const {
+ if (U_FAILURE(status)) {
+ return;
+ }
if (direction != UDAT_DIRECTION_LAST && direction != UDAT_DIRECTION_NEXT) {
status = U_ILLEGAL_ARGUMENT_ERROR;
- return appendTo;
+ return;
}
int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0;
- FieldPosition pos(FieldPosition::DONT_CARE);
- UnicodeString result;
- UnicodeString formattedNumber;
-
- StandardPlural::Form pluralIndex = QuantityFormatter::selectPlural(
- quantity, **fNumberFormat, **fPluralRules, formattedNumber, pos,
+ StandardPlural::Form pluralForm;
+ QuantityFormatter::formatAndSelect(
+ quantity,
+ **fNumberFormat,
+ **fPluralRules,
+ output.getStringRef(),
+ pluralForm,
status);
+ if (U_FAILURE(status)) {
+ return;
+ }
const SimpleFormatter* formatter =
- fCache->getRelativeUnitFormatter(fStyle, unit, bFuture, pluralIndex);
- if (formatter == NULL) {
+ fCache->getRelativeUnitFormatter(fStyle, unit, bFuture, pluralForm);
+ if (formatter == nullptr) {
// TODO: WARN - look at quantity formatter's action with an error.
status = U_INVALID_FORMAT_ERROR;
- return appendTo;
+ return;
}
- formatter->format(formattedNumber, result, status);
- adjustForContext(result);
- return appendTo.append(result);
+
+ number::impl::SimpleModifier modifier(*formatter, kRDTLiteralField, false);
+ modifier.formatAsPrefixSuffix(
+ output.getStringRef(), 0, output.getStringRef().length(), status);
}
UnicodeString& RelativeDateTimeFormatter::formatNumeric(
- double offset, URelativeDateTimeUnit unit,
- UnicodeString& appendTo, UErrorCode& status) const {
+ double offset,
+ URelativeDateTimeUnit unit,
+ UnicodeString& appendTo,
+ UErrorCode& status) const {
+ return doFormat(
+ &RelativeDateTimeFormatter::formatNumericImpl,
+ appendTo,
+ status,
+ offset,
+ unit);
+}
+
+FormattedRelativeDateTime RelativeDateTimeFormatter::formatNumericToValue(
+ double offset,
+ URelativeDateTimeUnit unit,
+ UErrorCode& status) const {
+ return doFormatToValue(
+ &RelativeDateTimeFormatter::formatNumericImpl,
+ status,
+ offset,
+ unit);
+}
+
+void RelativeDateTimeFormatter::formatNumericImpl(
+ double offset,
+ URelativeDateTimeUnit unit,
+ FormattedRelativeDateTimeData& output,
+ UErrorCode& status) const {
if (U_FAILURE(status)) {
- return appendTo;
- }
- // TODO:
- // The full implementation of this depends on CLDR data that is not yet available,
- // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data.
- // In the meantime do a quick bring-up by calling the old format method; this
- // leaves some holes (even for data that is currently available, such as quarter).
- // When the new CLDR data is available, update the data storage accordingly,
- // rewrite this to use it directly, and rewrite the old format method to call this
- // new one; that is covered by http://bugs.icu-project.org/trac/ticket/12171.
- UDateRelativeUnit relunit = UDAT_RELATIVE_UNIT_COUNT;
- switch (unit) {
- case UDAT_REL_UNIT_YEAR: relunit = UDAT_RELATIVE_YEARS; break;
- case UDAT_REL_UNIT_MONTH: relunit = UDAT_RELATIVE_MONTHS; break;
- case UDAT_REL_UNIT_WEEK: relunit = UDAT_RELATIVE_WEEKS; break;
- case UDAT_REL_UNIT_DAY: relunit = UDAT_RELATIVE_DAYS; break;
- case UDAT_REL_UNIT_HOUR: relunit = UDAT_RELATIVE_HOURS; break;
- case UDAT_REL_UNIT_MINUTE: relunit = UDAT_RELATIVE_MINUTES; break;
- case UDAT_REL_UNIT_SECOND: relunit = UDAT_RELATIVE_SECONDS; break;
- default: // a unit that the above method does not handle
- status = U_UNSUPPORTED_ERROR;
- return appendTo;
+ return;
}
UDateDirection direction = UDAT_DIRECTION_NEXT;
- if (offset < 0) {
+ if (std::signbit(offset)) { // needed to handle -0.0
direction = UDAT_DIRECTION_LAST;
offset = -offset;
}
- return format(offset, direction, relunit, appendTo, status);
+ if (direction != UDAT_DIRECTION_LAST && direction != UDAT_DIRECTION_NEXT) {
+ status = U_ILLEGAL_ARGUMENT_ERROR;
+ return;
+ }
+ int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0;
+
+ StandardPlural::Form pluralForm;
+ QuantityFormatter::formatAndSelect(
+ offset,
+ **fNumberFormat,
+ **fPluralRules,
+ output.getStringRef(),
+ pluralForm,
+ status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+
+ const SimpleFormatter* formatter =
+ fCache->getRelativeDateTimeUnitFormatter(fStyle, unit, bFuture, pluralForm);
+ if (formatter == nullptr) {
+ // TODO: WARN - look at quantity formatter's action with an error.
+ status = U_INVALID_FORMAT_ERROR;
+ return;
+ }
+
+ number::impl::SimpleModifier modifier(*formatter, kRDTLiteralField, false);
+ modifier.formatAsPrefixSuffix(
+ output.getStringRef(), 0, output.getStringRef().length(), status);
}
UnicodeString& RelativeDateTimeFormatter::format(
- UDateDirection direction, UDateAbsoluteUnit unit,
- UnicodeString& appendTo, UErrorCode& status) const {
+ UDateDirection direction,
+ UDateAbsoluteUnit unit,
+ UnicodeString& appendTo,
+ UErrorCode& status) const {
+ return doFormat(
+ &RelativeDateTimeFormatter::formatAbsoluteImpl,
+ appendTo,
+ status,
+ direction,
+ unit);
+}
+
+FormattedRelativeDateTime RelativeDateTimeFormatter::formatToValue(
+ UDateDirection direction,
+ UDateAbsoluteUnit unit,
+ UErrorCode& status) const {
+ return doFormatToValue(
+ &RelativeDateTimeFormatter::formatAbsoluteImpl,
+ status,
+ direction,
+ unit);
+}
+
+void RelativeDateTimeFormatter::formatAbsoluteImpl(
+ UDateDirection direction,
+ UDateAbsoluteUnit unit,
+ FormattedRelativeDateTimeData& output,
+ UErrorCode& status) const {
if (U_FAILURE(status)) {
- return appendTo;
+ return;
}
if (unit == UDAT_ABSOLUTE_NOW && direction != UDAT_DIRECTION_PLAIN) {
status = U_ILLEGAL_ARGUMENT_ERROR;
- return appendTo;
+ return;
}
// Get string using fallback.
- UnicodeString result;
- result.fastCopyFrom(fCache->getAbsoluteUnitString(fStyle, unit, direction));
- if (fOptBreakIterator != NULL) {
- adjustForContext(result);
- }
- return appendTo.append(result);
+ output.getStringRef().append(
+ fCache->getAbsoluteUnitString(fStyle, unit, direction),
+ kRDTLiteralField,
+ status);
}
UnicodeString& RelativeDateTimeFormatter::format(
- double offset, URelativeDateTimeUnit unit,
- UnicodeString& appendTo, UErrorCode& status) const {
+ double offset,
+ URelativeDateTimeUnit unit,
+ UnicodeString& appendTo,
+ UErrorCode& status) const {
+ return doFormat(
+ &RelativeDateTimeFormatter::formatRelativeImpl,
+ appendTo,
+ status,
+ offset,
+ unit);
+}
+
+FormattedRelativeDateTime RelativeDateTimeFormatter::formatToValue(
+ double offset,
+ URelativeDateTimeUnit unit,
+ UErrorCode& status) const {
+ return doFormatToValue(
+ &RelativeDateTimeFormatter::formatRelativeImpl,
+ status,
+ offset,
+ unit);
+}
+
+void RelativeDateTimeFormatter::formatRelativeImpl(
+ double offset,
+ URelativeDateTimeUnit unit,
+ FormattedRelativeDateTimeData& output,
+ UErrorCode& status) const {
if (U_FAILURE(status)) {
- return appendTo;
+ return;
}
// TODO:
// The full implementation of this depends on CLDR data that is not yet available,
UDateAbsoluteUnit absunit = UDAT_ABSOLUTE_UNIT_COUNT;
switch (unit) {
case UDAT_REL_UNIT_YEAR: absunit = UDAT_ABSOLUTE_YEAR; break;
+ case UDAT_REL_UNIT_QUARTER: absunit = UDAT_ABSOLUTE_QUARTER; break;
case UDAT_REL_UNIT_MONTH: absunit = UDAT_ABSOLUTE_MONTH; break;
case UDAT_REL_UNIT_WEEK: absunit = UDAT_ABSOLUTE_WEEK; break;
case UDAT_REL_UNIT_DAY: absunit = UDAT_ABSOLUTE_DAY; break;
case UDAT_REL_UNIT_THURSDAY: absunit = UDAT_ABSOLUTE_THURSDAY; break;
case UDAT_REL_UNIT_FRIDAY: absunit = UDAT_ABSOLUTE_FRIDAY; break;
case UDAT_REL_UNIT_SATURDAY: absunit = UDAT_ABSOLUTE_SATURDAY; break;
+ case UDAT_REL_UNIT_HOUR: absunit = UDAT_ABSOLUTE_HOUR; break;
+ case UDAT_REL_UNIT_MINUTE: absunit = UDAT_ABSOLUTE_MINUTE; break;
default: break;
}
if (direction != UDAT_DIRECTION_COUNT && absunit != UDAT_ABSOLUTE_UNIT_COUNT) {
- const UnicodeString &unitFormatString =
- fCache->getAbsoluteUnitString(fStyle, absunit, direction);
- if (!unitFormatString.isEmpty()) {
- if (fOptBreakIterator != NULL) {
- UnicodeString result(unitFormatString);
- adjustForContext(result);
- return appendTo.append(result);
- } else {
- return appendTo.append(unitFormatString);
- }
+ formatAbsoluteImpl(direction, absunit, output, status);
+ if (output.getStringRef().length() != 0) {
+ return;
}
}
// otherwise fallback to formatNumeric
- return formatNumeric(offset, unit, appendTo, status);
+ formatNumericImpl(offset, unit, output, status);
}
UnicodeString& RelativeDateTimeFormatter::combineDateAndTime(
timeString, relativeDateString, appendTo, status);
}
-void RelativeDateTimeFormatter::adjustForContext(UnicodeString &str) const {
- if (fOptBreakIterator == NULL
+UnicodeString& RelativeDateTimeFormatter::adjustForContext(UnicodeString &str) const {
+ if (fOptBreakIterator == nullptr
|| str.length() == 0 || !u_islower(str.char32At(0))) {
- return;
+ return str;
}
// Must guarantee that one thread at a time accesses the shared break
// iterator.
+ static UMutex gBrkIterMutex;
Mutex lock(&gBrkIterMutex);
str.toTitle(
fOptBreakIterator->get(),
fLocale,
U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
+ return str;
+}
+
+UBool RelativeDateTimeFormatter::checkNoAdjustForContext(UErrorCode& status) const {
+ // This is unsupported because it's hard to keep fields in sync with title
+ // casing. The code could be written and tested if there is demand.
+ if (fOptBreakIterator != nullptr) {
+ status = U_UNSUPPORTED_ERROR;
+ return FALSE;
+ }
+ return TRUE;
}
void RelativeDateTimeFormatter::init(
shared->removeRef();
} else {
SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias());
- if (shared == NULL) {
+ if (shared == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR;
return;
}
SharedObject::clearPtr(fOptBreakIterator);
} else {
SharedBreakIterator *shared = new SharedBreakIterator(bi.getAlias());
- if (shared == NULL) {
+ if (shared == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR;
return;
}
U_NAMESPACE_USE
+
+// Magic number: "FRDT" (FormattedRelativeDateTime) in ASCII
+UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL(
+ FormattedRelativeDateTime,
+ UFormattedRelativeDateTime,
+ UFormattedRelativeDateTimeImpl,
+ UFormattedRelativeDateTimeApiHelper,
+ ureldatefmt,
+ 0x46524454)
+
+
U_CAPI URelativeDateTimeFormatter* U_EXPORT2
ureldatefmt_open( const char* locale,
UNumberFormat* nfToAdopt,
UErrorCode* status )
{
if (U_FAILURE(*status)) {
- return NULL;
+ return nullptr;
}
LocalPointer<RelativeDateTimeFormatter> formatter(new RelativeDateTimeFormatter(Locale(locale),
(NumberFormat*)nfToAdopt, width,
capitalizationContext, *status), *status);
if (U_FAILURE(*status)) {
- return NULL;
+ return nullptr;
}
return (URelativeDateTimeFormatter*)formatter.orphan();
}
if (U_FAILURE(*status)) {
return 0;
}
- if (result == NULL ? resultCapacity != 0 : resultCapacity < 0) {
+ if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0) {
*status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
UnicodeString res;
- if (result != NULL) {
- // NULL destination for pure preflighting: empty dummy string
+ if (result != nullptr) {
+ // nullptr destination for pure preflighting: empty dummy string
// otherwise, alias the destination buffer (copied from udat_format)
res.setTo(result, 0, resultCapacity);
}
return res.extract(result, resultCapacity, *status);
}
+U_STABLE void U_EXPORT2
+ureldatefmt_formatNumericToResult(
+ const URelativeDateTimeFormatter* reldatefmt,
+ double offset,
+ URelativeDateTimeUnit unit,
+ UFormattedRelativeDateTime* result,
+ UErrorCode* status) {
+ if (U_FAILURE(*status)) {
+ return;
+ }
+ auto* fmt = reinterpret_cast<const RelativeDateTimeFormatter*>(reldatefmt);
+ auto* resultImpl = UFormattedRelativeDateTimeApiHelper::validate(result, *status);
+ resultImpl->fImpl = fmt->formatNumericToValue(offset, unit, *status);
+}
+
U_CAPI int32_t U_EXPORT2
ureldatefmt_format( const URelativeDateTimeFormatter* reldatefmt,
double offset,
if (U_FAILURE(*status)) {
return 0;
}
- if (result == NULL ? resultCapacity != 0 : resultCapacity < 0) {
+ if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0) {
*status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
UnicodeString res;
- if (result != NULL) {
- // NULL destination for pure preflighting: empty dummy string
+ if (result != nullptr) {
+ // nullptr destination for pure preflighting: empty dummy string
// otherwise, alias the destination buffer (copied from udat_format)
res.setTo(result, 0, resultCapacity);
}
return res.extract(result, resultCapacity, *status);
}
+U_DRAFT void U_EXPORT2
+ureldatefmt_formatToResult(
+ const URelativeDateTimeFormatter* reldatefmt,
+ double offset,
+ URelativeDateTimeUnit unit,
+ UFormattedRelativeDateTime* result,
+ UErrorCode* status) {
+ if (U_FAILURE(*status)) {
+ return;
+ }
+ auto* fmt = reinterpret_cast<const RelativeDateTimeFormatter*>(reldatefmt);
+ auto* resultImpl = UFormattedRelativeDateTimeApiHelper::validate(result, *status);
+ resultImpl->fImpl = fmt->formatToValue(offset, unit, *status);
+}
+
U_CAPI int32_t U_EXPORT2
ureldatefmt_combineDateAndTime( const URelativeDateTimeFormatter* reldatefmt,
const UChar * relativeDateString,
if (U_FAILURE(*status)) {
return 0;
}
- if (result == NULL ? resultCapacity != 0 : resultCapacity < 0 ||
- (relativeDateString == NULL ? relativeDateStringLen != 0 : relativeDateStringLen < -1) ||
- (timeString == NULL ? timeStringLen != 0 : timeStringLen < -1)) {
+ if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0 ||
+ (relativeDateString == nullptr ? relativeDateStringLen != 0 : relativeDateStringLen < -1) ||
+ (timeString == nullptr ? timeStringLen != 0 : timeStringLen < -1)) {
*status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}