X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/2ca993e82fb37b597a3c73ecd1586a139a6579c5..340931cb2e044a2141d11567dd0f782524e32994:/icuSources/i18n/reldatefmt.cpp diff --git a/icuSources/i18n/reldatefmt.cpp b/icuSources/i18n/reldatefmt.cpp index ad4eb188..fa3c27e1 100644 --- a/icuSources/i18n/reldatefmt.cpp +++ b/icuSources/i18n/reldatefmt.cpp @@ -1,3 +1,5 @@ +// © 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 @@ -12,7 +14,10 @@ #if !UCONFIG_NO_FORMATTING && !UCONFIG_NO_BREAK_ITERATION +#include +#include #include "unicode/dtfmtsym.h" +#include "unicode/ucasemap.h" #include "unicode/ureldatefmt.h" #include "unicode/udisplaycontext.h" #include "unicode/unum.h" @@ -37,23 +42,27 @@ #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; } } } @@ -70,7 +79,7 @@ public: // 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, @@ -79,6 +88,10 @@ public: 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; @@ -103,7 +116,7 @@ private: 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]; @@ -127,20 +140,48 @@ const UnicodeString& RelativeDateTimeCacheData::getAbsoluteUnitString( 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( @@ -178,14 +219,11 @@ namespace { /** * 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, @@ -216,23 +254,35 @@ struct RelDateTimeFmtDataSink : public ResourceTableSink { // 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; } @@ -247,10 +297,8 @@ struct RelDateTimeFmtDataSink : public ResourceTableSink { 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: @@ -267,6 +315,10 @@ struct RelDateTimeFmtDataSink : public ResourceTableSink { 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; } @@ -291,188 +343,16 @@ struct RelDateTimeFmtDataSink : public ResourceTableSink { 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 - * - * 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 - * - * 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; @@ -483,7 +363,7 @@ struct RelDateTimeFmtDataSink : public ResourceTableSink { // Utility functions static UDateRelativeDateTimeFormatterStyle styleFromString(const char *s) { - int32_t len = uprv_strlen(s); + int32_t len = static_cast(uprv_strlen(s)); if (len >= 7 && uprv_strcmp(s + len - 7, "-narrow") == 0) { return UDAT_STYLE_NARROW; } @@ -571,58 +451,168 @@ struct RelDateTimeFmtDataSink : public ResourceTableSink { 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(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 }; @@ -631,8 +621,14 @@ static void loadWeekdayNames(UnicodeString absoluteUnits[UDAT_STYLE_COUNT] [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; @@ -652,8 +648,13 @@ static UBool loadUnitData( 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); @@ -678,7 +679,7 @@ static UBool getDateTimePattern( .append("/DateTimePatterns", status); LocalUResourceBundlePointer topLevel( ures_getByKeyWithFallback( - resource, pathBuffer.data(), NULL, &status)); + resource, pathBuffer.data(), nullptr, &status)); if (U_FAILURE(status)) { return FALSE; } @@ -695,68 +696,88 @@ static UBool getDateTimePattern( template<> U_I18N_API const RelativeDateTimeCacheData *LocaleCacheKey::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 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(); + +static constexpr number::impl::Field kRDTLiteralField + = StringBuilderFieldUtils::compress(); + +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( @@ -765,12 +786,12 @@ 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; @@ -786,7 +807,7 @@ RelativeDateTimeFormatter::RelativeDateTimeFormatter( } init(nfToAdopt, bi, status); } else { - init(nfToAdopt, NULL, status); + init(nfToAdopt, nullptr, status); } } @@ -803,7 +824,7 @@ RelativeDateTimeFormatter::RelativeDateTimeFormatter( fCache->addRef(); fNumberFormat->addRef(); fPluralRules->addRef(); - if (fOptBreakIterator != NULL) { + if (fOptBreakIterator != nullptr) { fOptBreakIterator->addRef(); } } @@ -823,16 +844,16 @@ RelativeDateTimeFormatter& RelativeDateTimeFormatter::operator=( } 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(); } } @@ -849,98 +870,254 @@ UDateRelativeDateTimeFormatterStyle RelativeDateTimeFormatter::getFormatStyle() 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 +UnicodeString& RelativeDateTimeFormatter::doFormat( + F callback, + UnicodeString& appendTo, + UErrorCode& status, + Args... args) const { + FormattedRelativeDateTimeData output; + (this->*callback)(std::forward(args)..., output, status); if (U_FAILURE(status)) { return appendTo; } + UnicodeString result = output.getStringRef().toUnicodeString(); + return appendTo.append(adjustForContext(result)); +} + +template +FormattedRelativeDateTime RelativeDateTimeFormatter::doFormatToValue( + F callback, + UErrorCode& status, + Args... args) const { + if (!checkNoAdjustForContext(status)) { + return FormattedRelativeDateTime(status); + } + LocalPointer output( + new FormattedRelativeDateTimeData(), status); + if (U_FAILURE(status)) { + return FormattedRelativeDateTime(status); + } + (this->*callback)(std::forward(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, @@ -967,6 +1144,7 @@ UnicodeString& RelativeDateTimeFormatter::format( 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; @@ -983,23 +1161,18 @@ UnicodeString& RelativeDateTimeFormatter::format( 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( @@ -1009,19 +1182,31 @@ 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( @@ -1051,7 +1236,7 @@ void RelativeDateTimeFormatter::init( shared->removeRef(); } else { SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias()); - if (shared == NULL) { + if (shared == nullptr) { status = U_MEMORY_ALLOCATION_ERROR; return; } @@ -1062,7 +1247,7 @@ void RelativeDateTimeFormatter::init( SharedObject::clearPtr(fOptBreakIterator); } else { SharedBreakIterator *shared = new SharedBreakIterator(bi.getAlias()); - if (shared == NULL) { + if (shared == nullptr) { status = U_MEMORY_ALLOCATION_ERROR; return; } @@ -1077,6 +1262,17 @@ U_NAMESPACE_END 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, @@ -1085,13 +1281,13 @@ ureldatefmt_open( const char* locale, UErrorCode* status ) { if (U_FAILURE(*status)) { - return NULL; + return nullptr; } LocalPointer formatter(new RelativeDateTimeFormatter(Locale(locale), (NumberFormat*)nfToAdopt, width, capitalizationContext, *status), *status); if (U_FAILURE(*status)) { - return NULL; + return nullptr; } return (URelativeDateTimeFormatter*)formatter.orphan(); } @@ -1113,13 +1309,13 @@ ureldatefmt_formatNumeric( const URelativeDateTimeFormatter* reldatefmt, 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); } @@ -1130,6 +1326,21 @@ ureldatefmt_formatNumeric( const URelativeDateTimeFormatter* reldatefmt, 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(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, @@ -1141,13 +1352,13 @@ ureldatefmt_format( const URelativeDateTimeFormatter* reldatefmt, 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); } @@ -1158,6 +1369,21 @@ ureldatefmt_format( const URelativeDateTimeFormatter* reldatefmt, 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(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, @@ -1171,9 +1397,9 @@ ureldatefmt_combineDateAndTime( const URelativeDateTimeFormatter* reldatefmt, 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; }