1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 ******************************************************************************
5 * Copyright (C) 2014-2016, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 ******************************************************************************
10 ******************************************************************************
13 #include "unicode/reldatefmt.h"
15 #if !UCONFIG_NO_FORMATTING && !UCONFIG_NO_BREAK_ITERATION
19 #include "unicode/dtfmtsym.h"
20 #include "unicode/ucasemap.h"
21 #include "unicode/ureldatefmt.h"
22 #include "unicode/udisplaycontext.h"
23 #include "unicode/unum.h"
24 #include "unicode/localpointer.h"
25 #include "unicode/plurrule.h"
26 #include "unicode/simpleformatter.h"
27 #include "unicode/decimfmt.h"
28 #include "unicode/numfmt.h"
29 #include "unicode/brkiter.h"
30 #include "unicode/simpleformatter.h"
32 #include "unicode/ures.h"
38 #include "quantityformatter.h"
40 #include "sharedbreakiterator.h"
41 #include "sharedpluralrules.h"
42 #include "sharednumberformat.h"
43 #include "standardplural.h"
44 #include "unifiedcache.h"
46 #include "number_stringbuilder.h"
47 #include "number_utypes.h"
48 #include "number_modifiers.h"
49 #include "formattedval_impl.h"
50 #include "number_utils.h"
52 // Copied from uscript_props.cpp
56 // RelativeDateTimeFormatter specific data for a single locale
57 class RelativeDateTimeCacheData
: public SharedObject
{
59 RelativeDateTimeCacheData() : combinedDateAndTime(nullptr) {
60 // Initialize the cache arrays
61 for (int32_t style
= 0; style
< UDAT_STYLE_COUNT
; ++style
) {
62 for (int32_t relUnit
= 0; relUnit
< UDAT_REL_UNIT_COUNT
; ++relUnit
) {
63 for (int32_t pl
= 0; pl
< StandardPlural::COUNT
; ++pl
) {
64 relativeUnitsFormatters
[style
][relUnit
][0][pl
] = nullptr;
65 relativeUnitsFormatters
[style
][relUnit
][1][pl
] = nullptr;
69 for (int32_t i
= 0; i
< UDAT_STYLE_COUNT
; ++i
) {
70 fallBackCache
[i
] = -1;
73 virtual ~RelativeDateTimeCacheData();
75 // no numbers: e.g Next Tuesday; Yesterday; etc.
76 UnicodeString absoluteUnits
[UDAT_STYLE_COUNT
][UDAT_ABSOLUTE_UNIT_COUNT
][UDAT_DIRECTION_COUNT
];
78 // SimpleFormatter pointers for relative unit format,
79 // e.g., Next Tuesday; Yesterday; etc. For third index, 0
80 // means past, e.g., 5 days ago; 1 means future, e.g., in 5 days.
81 SimpleFormatter
*relativeUnitsFormatters
[UDAT_STYLE_COUNT
]
82 [UDAT_REL_UNIT_COUNT
][2][StandardPlural::COUNT
];
84 const UnicodeString
& getAbsoluteUnitString(int32_t fStyle
,
85 UDateAbsoluteUnit unit
,
86 UDateDirection direction
) const;
87 const SimpleFormatter
* getRelativeUnitFormatter(int32_t fStyle
,
88 UDateRelativeUnit unit
,
89 int32_t pastFutureIndex
,
90 int32_t pluralUnit
) const;
91 const SimpleFormatter
* getRelativeDateTimeUnitFormatter(int32_t fStyle
,
92 URelativeDateTimeUnit unit
,
93 int32_t pastFutureIndex
,
94 int32_t pluralUnit
) const;
96 const UnicodeString emptyString
;
98 // Mappping from source to target styles for alias fallback.
99 int32_t fallBackCache
[UDAT_STYLE_COUNT
];
101 void adoptCombinedDateAndTime(SimpleFormatter
*fmtToAdopt
) {
102 delete combinedDateAndTime
;
103 combinedDateAndTime
= fmtToAdopt
;
105 const SimpleFormatter
*getCombinedDateAndTime() const {
106 return combinedDateAndTime
;
110 SimpleFormatter
*combinedDateAndTime
;
111 RelativeDateTimeCacheData(const RelativeDateTimeCacheData
&other
);
112 RelativeDateTimeCacheData
& operator=(
113 const RelativeDateTimeCacheData
&other
);
116 RelativeDateTimeCacheData::~RelativeDateTimeCacheData() {
117 // clear out the cache arrays
118 for (int32_t style
= 0; style
< UDAT_STYLE_COUNT
; ++style
) {
119 for (int32_t relUnit
= 0; relUnit
< UDAT_REL_UNIT_COUNT
; ++relUnit
) {
120 for (int32_t pl
= 0; pl
< StandardPlural::COUNT
; ++pl
) {
121 delete relativeUnitsFormatters
[style
][relUnit
][0][pl
];
122 delete relativeUnitsFormatters
[style
][relUnit
][1][pl
];
126 delete combinedDateAndTime
;
130 // Use fallback cache for absolute units.
131 const UnicodeString
& RelativeDateTimeCacheData::getAbsoluteUnitString(
132 int32_t fStyle
, UDateAbsoluteUnit unit
, UDateDirection direction
) const {
133 int32_t style
= fStyle
;
135 if (!absoluteUnits
[style
][unit
][direction
].isEmpty()) {
136 return absoluteUnits
[style
][unit
][direction
];
138 style
= fallBackCache
[style
];
139 } while (style
!= -1);
143 const SimpleFormatter
* RelativeDateTimeCacheData::getRelativeUnitFormatter(
145 UDateRelativeUnit unit
,
146 int32_t pastFutureIndex
,
147 int32_t pluralUnit
) const {
148 URelativeDateTimeUnit rdtunit
= UDAT_REL_UNIT_COUNT
;
150 case UDAT_RELATIVE_YEARS
: rdtunit
= UDAT_REL_UNIT_YEAR
; break;
151 case UDAT_RELATIVE_MONTHS
: rdtunit
= UDAT_REL_UNIT_MONTH
; break;
152 case UDAT_RELATIVE_WEEKS
: rdtunit
= UDAT_REL_UNIT_WEEK
; break;
153 case UDAT_RELATIVE_DAYS
: rdtunit
= UDAT_REL_UNIT_DAY
; break;
154 case UDAT_RELATIVE_HOURS
: rdtunit
= UDAT_REL_UNIT_HOUR
; break;
155 case UDAT_RELATIVE_MINUTES
: rdtunit
= UDAT_REL_UNIT_MINUTE
; break;
156 case UDAT_RELATIVE_SECONDS
: rdtunit
= UDAT_REL_UNIT_SECOND
; break;
157 default: // a unit that the above method does not handle
161 return getRelativeDateTimeUnitFormatter(fStyle
, rdtunit
, pastFutureIndex
, pluralUnit
);
164 // Use fallback cache for SimpleFormatter relativeUnits.
165 const SimpleFormatter
* RelativeDateTimeCacheData::getRelativeDateTimeUnitFormatter(
167 URelativeDateTimeUnit unit
,
168 int32_t pastFutureIndex
,
169 int32_t pluralUnit
) const {
171 int32_t style
= fStyle
;
173 if (relativeUnitsFormatters
[style
][unit
][pastFutureIndex
][pluralUnit
] != nullptr) {
174 return relativeUnitsFormatters
[style
][unit
][pastFutureIndex
][pluralUnit
];
176 style
= fallBackCache
[style
];
177 } while (style
!= -1);
179 if (pluralUnit
== StandardPlural::OTHER
) {
182 pluralUnit
= StandardPlural::OTHER
;
184 return nullptr; // No formatter found.
187 static UBool
getStringWithFallback(
188 const UResourceBundle
*resource
,
190 UnicodeString
&result
,
191 UErrorCode
&status
) {
193 const UChar
*resStr
= ures_getStringByKeyWithFallback(
194 resource
, key
, &len
, &status
);
195 if (U_FAILURE(status
)) {
198 result
.setTo(TRUE
, resStr
, len
);
203 static UBool
getStringByIndex(
204 const UResourceBundle
*resource
,
206 UnicodeString
&result
,
207 UErrorCode
&status
) {
209 const UChar
*resStr
= ures_getStringByIndex(
210 resource
, idx
, &len
, &status
);
211 if (U_FAILURE(status
)) {
214 result
.setTo(TRUE
, resStr
, len
);
221 * Sink for enumerating all of the measurement unit display names.
223 * More specific bundles (en_GB) are enumerated before their parents (en_001, en, root):
224 * Only store a value if it is still missing, that is, it has not been overridden.
226 struct RelDateTimeFmtDataSink
: public ResourceSink
{
229 * Sink for patterns for relative dates and times. For example,
230 * fields/relative/...
233 // Generic unit enum for storing Unit info.
234 typedef enum RelAbsUnit
{
253 static int32_t relUnitFromGeneric(RelAbsUnit genUnit
) {
254 // Converts the generic units to UDAT_RELATIVE version.
257 return UDAT_REL_UNIT_SECOND
;
259 return UDAT_REL_UNIT_MINUTE
;
261 return UDAT_REL_UNIT_HOUR
;
263 return UDAT_REL_UNIT_DAY
;
265 return UDAT_REL_UNIT_WEEK
;
267 return UDAT_REL_UNIT_MONTH
;
269 return UDAT_REL_UNIT_QUARTER
;
271 return UDAT_REL_UNIT_YEAR
;
273 return UDAT_REL_UNIT_SUNDAY
;
275 return UDAT_REL_UNIT_MONDAY
;
277 return UDAT_REL_UNIT_TUESDAY
;
279 return UDAT_REL_UNIT_WEDNESDAY
;
281 return UDAT_REL_UNIT_THURSDAY
;
283 return UDAT_REL_UNIT_FRIDAY
;
285 return UDAT_REL_UNIT_SATURDAY
;
291 static int32_t absUnitFromGeneric(RelAbsUnit genUnit
) {
292 // Converts the generic units to UDAT_RELATIVE version.
295 return UDAT_ABSOLUTE_DAY
;
297 return UDAT_ABSOLUTE_WEEK
;
299 return UDAT_ABSOLUTE_MONTH
;
301 return UDAT_ABSOLUTE_QUARTER
;
303 return UDAT_ABSOLUTE_YEAR
;
305 return UDAT_ABSOLUTE_SUNDAY
;
307 return UDAT_ABSOLUTE_MONDAY
;
309 return UDAT_ABSOLUTE_TUESDAY
;
311 return UDAT_ABSOLUTE_WEDNESDAY
;
313 return UDAT_ABSOLUTE_THURSDAY
;
315 return UDAT_ABSOLUTE_FRIDAY
;
317 return UDAT_ABSOLUTE_SATURDAY
;
323 static int32_t keyToDirection(const char* key
) {
324 if (uprv_strcmp(key
, "-2") == 0) {
325 return UDAT_DIRECTION_LAST_2
;
327 if (uprv_strcmp(key
, "-1") == 0) {
328 return UDAT_DIRECTION_LAST
;
330 if (uprv_strcmp(key
, "0") == 0) {
331 return UDAT_DIRECTION_THIS
;
333 if (uprv_strcmp(key
, "1") == 0) {
334 return UDAT_DIRECTION_NEXT
;
336 if (uprv_strcmp(key
, "2") == 0) {
337 return UDAT_DIRECTION_NEXT_2
;
342 // Values kept between levels of parsing the CLDR data.
343 int32_t pastFutureIndex
; // 0 == past or 1 == future
344 UDateRelativeDateTimeFormatterStyle style
; // {LONG, SHORT, NARROW}
345 RelAbsUnit genericUnit
;
347 RelativeDateTimeCacheData
&outputData
;
350 RelDateTimeFmtDataSink(RelativeDateTimeCacheData
& cacheData
)
351 : outputData(cacheData
) {
352 // Clear cacheData.fallBackCache
353 cacheData
.fallBackCache
[UDAT_STYLE_LONG
] = -1;
354 cacheData
.fallBackCache
[UDAT_STYLE_SHORT
] = -1;
355 cacheData
.fallBackCache
[UDAT_STYLE_NARROW
] = -1;
358 ~RelDateTimeFmtDataSink();
361 static UDateRelativeDateTimeFormatterStyle
styleFromString(const char *s
) {
362 int32_t len
= static_cast<int32_t>(uprv_strlen(s
));
363 if (len
>= 7 && uprv_strcmp(s
+ len
- 7, "-narrow") == 0) {
364 return UDAT_STYLE_NARROW
;
366 if (len
>= 6 && uprv_strcmp(s
+ len
- 6, "-short") == 0) {
367 return UDAT_STYLE_SHORT
;
369 return UDAT_STYLE_LONG
;
372 static int32_t styleSuffixLength(UDateRelativeDateTimeFormatterStyle style
) {
374 case UDAT_STYLE_NARROW
:
376 case UDAT_STYLE_SHORT
:
384 static UDateRelativeDateTimeFormatterStyle
styleFromAliasUnicodeString(UnicodeString s
) {
385 static const UChar narrow
[7] = {0x002D, 0x006E, 0x0061, 0x0072, 0x0072, 0x006F, 0x0077};
386 static const UChar sshort
[6] = {0x002D, 0x0073, 0x0068, 0x006F, 0x0072, 0x0074,};
387 if (s
.endsWith(narrow
, 7)) {
388 return UDAT_STYLE_NARROW
;
390 if (s
.endsWith(sshort
, 6)) {
391 return UDAT_STYLE_SHORT
;
393 return UDAT_STYLE_LONG
;
396 static RelAbsUnit
unitOrNegativeFromString(const char* keyword
, int32_t length
) {
397 // Quick check from string to enum.
400 if (uprv_strncmp(keyword
, "day", length
) == 0) {
402 } else if (uprv_strncmp(keyword
, "sun", length
) == 0) {
404 } else if (uprv_strncmp(keyword
, "mon", length
) == 0) {
406 } else if (uprv_strncmp(keyword
, "tue", length
) == 0) {
408 } else if (uprv_strncmp(keyword
, "wed", length
) == 0) {
410 } else if (uprv_strncmp(keyword
, "thu", length
) == 0) {
412 } else if (uprv_strncmp(keyword
, "fri", length
) == 0) {
414 } else if (uprv_strncmp(keyword
, "sat", length
) == 0) {
419 if (uprv_strncmp(keyword
, "hour", length
) == 0) {
421 } else if (uprv_strncmp(keyword
, "week", length
) == 0) {
423 } else if (uprv_strncmp(keyword
, "year", length
) == 0) {
428 if (uprv_strncmp(keyword
, "month", length
) == 0) {
433 if (uprv_strncmp(keyword
, "minute", length
) == 0) {
435 } else if (uprv_strncmp(keyword
, "second", length
) == 0) {
440 if (uprv_strncmp(keyword
, "quarter", length
) == 0) {
441 return QUARTER
; // TODO: Check @provisional
450 void handlePlainDirection(ResourceValue
&value
, UErrorCode
&errorCode
) {
451 // Handle Display Name for PLAIN direction for some units.
452 if (U_FAILURE(errorCode
)) { return; }
454 int32_t absUnit
= absUnitFromGeneric(genericUnit
);
456 return; // Not interesting.
459 // Store displayname if not set.
460 if (outputData
.absoluteUnits
[style
]
461 [absUnit
][UDAT_DIRECTION_PLAIN
].isEmpty()) {
462 outputData
.absoluteUnits
[style
]
463 [absUnit
][UDAT_DIRECTION_PLAIN
].fastCopyFrom(value
.getUnicodeString(errorCode
));
468 void consumeTableRelative(const char *key
, ResourceValue
&value
, UErrorCode
&errorCode
) {
469 ResourceTable unitTypesTable
= value
.getTable(errorCode
);
470 if (U_FAILURE(errorCode
)) { return; }
472 for (int32_t i
= 0; unitTypesTable
.getKeyAndValue(i
, key
, value
); ++i
) {
473 if (value
.getType() == URES_STRING
) {
474 int32_t direction
= keyToDirection(key
);
479 int32_t relUnitIndex
= relUnitFromGeneric(genericUnit
);
480 if (relUnitIndex
== UDAT_REL_UNIT_SECOND
&& uprv_strcmp(key
, "0") == 0 &&
481 outputData
.absoluteUnits
[style
][UDAT_ABSOLUTE_NOW
][UDAT_DIRECTION_PLAIN
].isEmpty()) {
483 outputData
.absoluteUnits
[style
][UDAT_ABSOLUTE_NOW
]
484 [UDAT_DIRECTION_PLAIN
].fastCopyFrom(value
.getUnicodeString(errorCode
));
487 int32_t absUnitIndex
= absUnitFromGeneric(genericUnit
);
488 if (absUnitIndex
< 0) {
491 // Only reset if slot is empty.
492 if (outputData
.absoluteUnits
[style
][absUnitIndex
][direction
].isEmpty()) {
493 outputData
.absoluteUnits
[style
][absUnitIndex
]
494 [direction
].fastCopyFrom(value
.getUnicodeString(errorCode
));
500 void consumeTimeDetail(int32_t relUnitIndex
,
501 const char *key
, ResourceValue
&value
, UErrorCode
&errorCode
) {
502 ResourceTable unitTypesTable
= value
.getTable(errorCode
);
503 if (U_FAILURE(errorCode
)) { return; }
505 for (int32_t i
= 0; unitTypesTable
.getKeyAndValue(i
, key
, value
); ++i
) {
506 if (value
.getType() == URES_STRING
) {
507 int32_t pluralIndex
= StandardPlural::indexOrNegativeFromString(key
);
508 if (pluralIndex
>= 0) {
509 SimpleFormatter
**patterns
=
510 outputData
.relativeUnitsFormatters
[style
][relUnitIndex
]
512 // Only set if not already established.
513 if (patterns
[pluralIndex
] == nullptr) {
514 patterns
[pluralIndex
] = new SimpleFormatter(
515 value
.getUnicodeString(errorCode
), 0, 1, errorCode
);
516 if (patterns
[pluralIndex
] == nullptr) {
517 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
525 void consumeTableRelativeTime(const char *key
, ResourceValue
&value
, UErrorCode
&errorCode
) {
526 ResourceTable relativeTimeTable
= value
.getTable(errorCode
);
527 if (U_FAILURE(errorCode
)) { return; }
529 int32_t relUnitIndex
= relUnitFromGeneric(genericUnit
);
530 if (relUnitIndex
< 0) {
533 for (int32_t i
= 0; relativeTimeTable
.getKeyAndValue(i
, key
, value
); ++i
) {
534 if (uprv_strcmp(key
, "past") == 0) {
536 } else if (uprv_strcmp(key
, "future") == 0) {
542 consumeTimeDetail(relUnitIndex
, key
, value
, errorCode
);
546 void consumeAlias(const char *key
, const ResourceValue
&value
, UErrorCode
&errorCode
) {
548 UDateRelativeDateTimeFormatterStyle sourceStyle
= styleFromString(key
);
549 const UnicodeString valueStr
= value
.getAliasUnicodeString(errorCode
);
550 if (U_FAILURE(errorCode
)) { return; }
552 UDateRelativeDateTimeFormatterStyle targetStyle
=
553 styleFromAliasUnicodeString(valueStr
);
555 if (sourceStyle
== targetStyle
) {
556 errorCode
= U_INVALID_FORMAT_ERROR
;
559 if (outputData
.fallBackCache
[sourceStyle
] != -1 &&
560 outputData
.fallBackCache
[sourceStyle
] != targetStyle
) {
561 errorCode
= U_INVALID_FORMAT_ERROR
;
564 outputData
.fallBackCache
[sourceStyle
] = targetStyle
;
567 void consumeTimeUnit(const char *key
, ResourceValue
&value
, UErrorCode
&errorCode
) {
568 ResourceTable unitTypesTable
= value
.getTable(errorCode
);
569 if (U_FAILURE(errorCode
)) { return; }
571 for (int32_t i
= 0; unitTypesTable
.getKeyAndValue(i
, key
, value
); ++i
) {
572 // Handle display name.
573 if (uprv_strcmp(key
, "dn") == 0 && value
.getType() == URES_STRING
) {
574 handlePlainDirection(value
, errorCode
);
576 if (value
.getType() == URES_TABLE
) {
577 if (uprv_strcmp(key
, "relative") == 0) {
578 consumeTableRelative(key
, value
, errorCode
);
579 } else if (uprv_strcmp(key
, "relativeTime") == 0) {
580 consumeTableRelativeTime(key
, value
, errorCode
);
586 virtual void put(const char *key
, ResourceValue
&value
,
587 UBool
/*noFallback*/, UErrorCode
&errorCode
) {
588 // Main entry point to sink
589 ResourceTable table
= value
.getTable(errorCode
);
590 if (U_FAILURE(errorCode
)) { return; }
591 for (int32_t i
= 0; table
.getKeyAndValue(i
, key
, value
); ++i
) {
592 if (value
.getType() == URES_ALIAS
) {
593 consumeAlias(key
, value
, errorCode
);
595 style
= styleFromString(key
);
596 int32_t unitSize
= static_cast<int32_t>(uprv_strlen(key
)) - styleSuffixLength(style
);
597 genericUnit
= unitOrNegativeFromString(key
, unitSize
);
598 if (style
>= 0 && genericUnit
!= INVALID_UNIT
) {
599 consumeTimeUnit(key
, value
, errorCode
);
607 // Virtual destructors must be defined out of line.
608 RelDateTimeFmtDataSink::~RelDateTimeFmtDataSink() {}
611 static const DateFormatSymbols::DtWidthType styleToDateFormatSymbolWidth
[UDAT_STYLE_COUNT
] = {
612 DateFormatSymbols::WIDE
, DateFormatSymbols::SHORT
, DateFormatSymbols::NARROW
615 // Get days of weeks from the DateFormatSymbols class.
616 static void loadWeekdayNames(UnicodeString absoluteUnits
[UDAT_STYLE_COUNT
]
617 [UDAT_ABSOLUTE_UNIT_COUNT
][UDAT_DIRECTION_COUNT
],
618 const char* localeId
,
619 UErrorCode
& status
) {
620 if (U_FAILURE(status
)) {
623 Locale
locale(localeId
);
624 DateFormatSymbols
dfSym(locale
, status
);
625 if (U_FAILURE(status
)) {
628 for (int32_t style
= 0; style
< UDAT_STYLE_COUNT
; ++style
) {
629 DateFormatSymbols::DtWidthType dtfmtWidth
= styleToDateFormatSymbolWidth
[style
];
631 const UnicodeString
* weekdayNames
=
632 dfSym
.getWeekdays(count
, DateFormatSymbols::STANDALONE
, dtfmtWidth
);
633 for (int32_t dayIndex
= UDAT_ABSOLUTE_SUNDAY
;
634 dayIndex
<= UDAT_ABSOLUTE_SATURDAY
; ++ dayIndex
) {
635 int32_t dateSymbolIndex
= (dayIndex
- UDAT_ABSOLUTE_SUNDAY
) + UCAL_SUNDAY
;
636 absoluteUnits
[style
][dayIndex
][UDAT_DIRECTION_PLAIN
].fastCopyFrom(
637 weekdayNames
[dateSymbolIndex
]);
642 static UBool
loadUnitData(
643 const UResourceBundle
*resource
,
644 RelativeDateTimeCacheData
&cacheData
,
645 const char* localeId
,
646 UErrorCode
&status
) {
648 RelDateTimeFmtDataSink
sink(cacheData
);
650 ures_getAllItemsWithFallback(resource
, "fields", sink
, status
);
651 if (U_FAILURE(status
)) {
655 // Get the weekday names from DateFormatSymbols.
656 loadWeekdayNames(cacheData
.absoluteUnits
, localeId
, status
);
657 return U_SUCCESS(status
);
660 static UBool
getDateTimePattern(
661 const UResourceBundle
*resource
,
662 UnicodeString
&result
,
663 UErrorCode
&status
) {
664 UnicodeString defaultCalendarName
;
665 if (!getStringWithFallback(
672 CharString pathBuffer
;
673 pathBuffer
.append("calendar/", status
)
674 .appendInvariantChars(defaultCalendarName
, status
)
675 .append("/DateTimePatterns", status
);
676 LocalUResourceBundlePointer
topLevel(
677 ures_getByKeyWithFallback(
678 resource
, pathBuffer
.data(), nullptr, &status
));
679 if (U_FAILURE(status
)) {
682 int32_t size
= ures_getSize(topLevel
.getAlias());
684 // Oops, size is too small to access the index that we want, fallback
685 // to a hard-coded value.
686 result
= UNICODE_STRING_SIMPLE("{1} {0}");
689 return getStringByIndex(topLevel
.getAlias(), 8, result
, status
);
692 template<> U_I18N_API
693 const RelativeDateTimeCacheData
*LocaleCacheKey
<RelativeDateTimeCacheData
>::createObject(const void * /*unused*/, UErrorCode
&status
) const {
694 const char *localeId
= fLoc
.getName();
695 LocalUResourceBundlePointer
topLevel(ures_open(nullptr, localeId
, &status
));
696 if (U_FAILURE(status
)) {
699 LocalPointer
<RelativeDateTimeCacheData
> result(
700 new RelativeDateTimeCacheData());
701 if (result
.isNull()) {
702 status
= U_MEMORY_ALLOCATION_ERROR
;
712 UnicodeString dateTimePattern
;
713 if (!getDateTimePattern(topLevel
.getAlias(), dateTimePattern
, status
)) {
716 result
->adoptCombinedDateAndTime(
717 new SimpleFormatter(dateTimePattern
, 2, 2, status
));
718 if (U_FAILURE(status
)) {
722 return result
.orphan();
727 static constexpr number::impl::Field kRDTNumericField
728 = number::impl::NumFieldUtils::compress
<UFIELD_CATEGORY_RELATIVE_DATETIME
, UDAT_REL_NUMERIC_FIELD
>();
730 static constexpr number::impl::Field kRDTLiteralField
731 = number::impl::NumFieldUtils::compress
<UFIELD_CATEGORY_RELATIVE_DATETIME
, UDAT_REL_LITERAL_FIELD
>();
733 class FormattedRelativeDateTimeData
: public FormattedValueNumberStringBuilderImpl
{
735 FormattedRelativeDateTimeData() : FormattedValueNumberStringBuilderImpl(kRDTNumericField
) {}
736 virtual ~FormattedRelativeDateTimeData();
739 FormattedRelativeDateTimeData::~FormattedRelativeDateTimeData() = default;
742 UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedRelativeDateTime
)
745 RelativeDateTimeFormatter::RelativeDateTimeFormatter(UErrorCode
& status
) :
747 fNumberFormat(nullptr),
748 fPluralRules(nullptr),
749 fStyle(UDAT_STYLE_LONG
),
750 fContext(UDISPCTX_CAPITALIZATION_NONE
),
751 fOptBreakIterator(nullptr) {
752 init(nullptr, nullptr, status
);
755 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
756 const Locale
& locale
, UErrorCode
& status
) :
758 fNumberFormat(nullptr),
759 fPluralRules(nullptr),
760 fStyle(UDAT_STYLE_LONG
),
761 fContext(UDISPCTX_CAPITALIZATION_NONE
),
762 fOptBreakIterator(nullptr),
764 init(nullptr, nullptr, status
);
767 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
768 const Locale
& locale
, NumberFormat
*nfToAdopt
, UErrorCode
& status
) :
770 fNumberFormat(nullptr),
771 fPluralRules(nullptr),
772 fStyle(UDAT_STYLE_LONG
),
773 fContext(UDISPCTX_CAPITALIZATION_NONE
),
774 fOptBreakIterator(nullptr),
776 init(nfToAdopt
, nullptr, status
);
779 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
780 const Locale
& locale
,
781 NumberFormat
*nfToAdopt
,
782 UDateRelativeDateTimeFormatterStyle styl
,
783 UDisplayContext capitalizationContext
,
784 UErrorCode
& status
) :
786 fNumberFormat(nullptr),
787 fPluralRules(nullptr),
789 fContext(capitalizationContext
),
790 fOptBreakIterator(nullptr),
792 if (U_FAILURE(status
)) {
795 if ((capitalizationContext
>> 8) != UDISPCTX_TYPE_CAPITALIZATION
) {
796 status
= U_ILLEGAL_ARGUMENT_ERROR
;
799 if (capitalizationContext
== UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE
) {
800 BreakIterator
*bi
= BreakIterator::createSentenceInstance(locale
, status
);
801 if (U_FAILURE(status
)) {
804 init(nfToAdopt
, bi
, status
);
806 init(nfToAdopt
, nullptr, status
);
810 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
811 const RelativeDateTimeFormatter
& other
)
813 fCache(other
.fCache
),
814 fNumberFormat(other
.fNumberFormat
),
815 fPluralRules(other
.fPluralRules
),
816 fStyle(other
.fStyle
),
817 fContext(other
.fContext
),
818 fOptBreakIterator(other
.fOptBreakIterator
),
819 fLocale(other
.fLocale
) {
821 fNumberFormat
->addRef();
822 fPluralRules
->addRef();
823 if (fOptBreakIterator
!= nullptr) {
824 fOptBreakIterator
->addRef();
828 RelativeDateTimeFormatter
& RelativeDateTimeFormatter::operator=(
829 const RelativeDateTimeFormatter
& other
) {
830 if (this != &other
) {
831 SharedObject::copyPtr(other
.fCache
, fCache
);
832 SharedObject::copyPtr(other
.fNumberFormat
, fNumberFormat
);
833 SharedObject::copyPtr(other
.fPluralRules
, fPluralRules
);
834 SharedObject::copyPtr(other
.fOptBreakIterator
, fOptBreakIterator
);
835 fStyle
= other
.fStyle
;
836 fContext
= other
.fContext
;
837 fLocale
= other
.fLocale
;
842 RelativeDateTimeFormatter::~RelativeDateTimeFormatter() {
843 if (fCache
!= nullptr) {
846 if (fNumberFormat
!= nullptr) {
847 fNumberFormat
->removeRef();
849 if (fPluralRules
!= nullptr) {
850 fPluralRules
->removeRef();
852 if (fOptBreakIterator
!= nullptr) {
853 fOptBreakIterator
->removeRef();
857 const NumberFormat
& RelativeDateTimeFormatter::getNumberFormat() const {
858 return **fNumberFormat
;
861 UDisplayContext
RelativeDateTimeFormatter::getCapitalizationContext() const {
865 UDateRelativeDateTimeFormatterStyle
RelativeDateTimeFormatter::getFormatStyle() const {
870 // To reduce boilerplate code, we use a helper function that forwards variadic
871 // arguments to the formatImpl function.
873 template<typename F
, typename
... Args
>
874 UnicodeString
& RelativeDateTimeFormatter::doFormat(
876 UnicodeString
& appendTo
,
878 Args
... args
) const {
879 FormattedRelativeDateTimeData output
;
880 (this->*callback
)(std::forward
<Args
>(args
)..., output
, status
);
881 if (U_FAILURE(status
)) {
884 UnicodeString result
= output
.getStringRef().toUnicodeString();
885 return appendTo
.append(adjustForContext(result
));
888 template<typename F
, typename
... Args
>
889 FormattedRelativeDateTime
RelativeDateTimeFormatter::doFormatToValue(
892 Args
... args
) const {
893 if (!checkNoAdjustForContext(status
)) {
894 return FormattedRelativeDateTime(status
);
896 LocalPointer
<FormattedRelativeDateTimeData
> output(
897 new FormattedRelativeDateTimeData(), status
);
898 if (U_FAILURE(status
)) {
899 return FormattedRelativeDateTime(status
);
901 (this->*callback
)(std::forward
<Args
>(args
)..., *output
, status
);
902 output
->getStringRef().writeTerminator(status
);
903 return FormattedRelativeDateTime(output
.orphan());
906 UnicodeString
& RelativeDateTimeFormatter::format(
908 UDateDirection direction
,
909 UDateRelativeUnit unit
,
910 UnicodeString
& appendTo
,
911 UErrorCode
& status
) const {
913 &RelativeDateTimeFormatter::formatImpl
,
921 FormattedRelativeDateTime
RelativeDateTimeFormatter::formatToValue(
923 UDateDirection direction
,
924 UDateRelativeUnit unit
,
925 UErrorCode
& status
) const {
926 return doFormatToValue(
927 &RelativeDateTimeFormatter::formatImpl
,
934 void RelativeDateTimeFormatter::formatImpl(
936 UDateDirection direction
,
937 UDateRelativeUnit unit
,
938 FormattedRelativeDateTimeData
& output
,
939 UErrorCode
& status
) const {
940 if (U_FAILURE(status
)) {
943 if (direction
!= UDAT_DIRECTION_LAST
&& direction
!= UDAT_DIRECTION_NEXT
) {
944 status
= U_ILLEGAL_ARGUMENT_ERROR
;
947 int32_t bFuture
= direction
== UDAT_DIRECTION_NEXT
? 1 : 0;
949 StandardPlural::Form pluralForm
;
950 QuantityFormatter::formatAndSelect(
954 output
.getStringRef(),
957 if (U_FAILURE(status
)) {
961 const SimpleFormatter
* formatter
=
962 fCache
->getRelativeUnitFormatter(fStyle
, unit
, bFuture
, pluralForm
);
963 if (formatter
== nullptr) {
964 // TODO: WARN - look at quantity formatter's action with an error.
965 status
= U_INVALID_FORMAT_ERROR
;
969 number::impl::SimpleModifier
modifier(*formatter
, kRDTLiteralField
, false);
970 modifier
.formatAsPrefixSuffix(
971 output
.getStringRef(), 0, output
.getStringRef().length(), status
);
974 UnicodeString
& RelativeDateTimeFormatter::formatNumeric(
976 URelativeDateTimeUnit unit
,
977 UnicodeString
& appendTo
,
978 UErrorCode
& status
) const {
980 &RelativeDateTimeFormatter::formatNumericImpl
,
987 FormattedRelativeDateTime
RelativeDateTimeFormatter::formatNumericToValue(
989 URelativeDateTimeUnit unit
,
990 UErrorCode
& status
) const {
991 return doFormatToValue(
992 &RelativeDateTimeFormatter::formatNumericImpl
,
998 void RelativeDateTimeFormatter::formatNumericImpl(
1000 URelativeDateTimeUnit unit
,
1001 FormattedRelativeDateTimeData
& output
,
1002 UErrorCode
& status
) const {
1003 if (U_FAILURE(status
)) {
1006 UDateDirection direction
= UDAT_DIRECTION_NEXT
;
1007 if (std::signbit(offset
)) { // needed to handle -0.0
1008 direction
= UDAT_DIRECTION_LAST
;
1011 if (direction
!= UDAT_DIRECTION_LAST
&& direction
!= UDAT_DIRECTION_NEXT
) {
1012 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1015 int32_t bFuture
= direction
== UDAT_DIRECTION_NEXT
? 1 : 0;
1017 StandardPlural::Form pluralForm
;
1018 QuantityFormatter::formatAndSelect(
1022 output
.getStringRef(),
1025 if (U_FAILURE(status
)) {
1029 const SimpleFormatter
* formatter
=
1030 fCache
->getRelativeDateTimeUnitFormatter(fStyle
, unit
, bFuture
, pluralForm
);
1031 if (formatter
== nullptr) {
1032 // TODO: WARN - look at quantity formatter's action with an error.
1033 status
= U_INVALID_FORMAT_ERROR
;
1037 number::impl::SimpleModifier
modifier(*formatter
, kRDTLiteralField
, false);
1038 modifier
.formatAsPrefixSuffix(
1039 output
.getStringRef(), 0, output
.getStringRef().length(), status
);
1042 UnicodeString
& RelativeDateTimeFormatter::format(
1043 UDateDirection direction
,
1044 UDateAbsoluteUnit unit
,
1045 UnicodeString
& appendTo
,
1046 UErrorCode
& status
) const {
1048 &RelativeDateTimeFormatter::formatAbsoluteImpl
,
1055 FormattedRelativeDateTime
RelativeDateTimeFormatter::formatToValue(
1056 UDateDirection direction
,
1057 UDateAbsoluteUnit unit
,
1058 UErrorCode
& status
) const {
1059 return doFormatToValue(
1060 &RelativeDateTimeFormatter::formatAbsoluteImpl
,
1066 void RelativeDateTimeFormatter::formatAbsoluteImpl(
1067 UDateDirection direction
,
1068 UDateAbsoluteUnit unit
,
1069 FormattedRelativeDateTimeData
& output
,
1070 UErrorCode
& status
) const {
1071 if (U_FAILURE(status
)) {
1074 if (unit
== UDAT_ABSOLUTE_NOW
&& direction
!= UDAT_DIRECTION_PLAIN
) {
1075 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1079 // Get string using fallback.
1080 output
.getStringRef().append(
1081 fCache
->getAbsoluteUnitString(fStyle
, unit
, direction
),
1086 UnicodeString
& RelativeDateTimeFormatter::format(
1088 URelativeDateTimeUnit unit
,
1089 UnicodeString
& appendTo
,
1090 UErrorCode
& status
) const {
1092 &RelativeDateTimeFormatter::formatRelativeImpl
,
1099 FormattedRelativeDateTime
RelativeDateTimeFormatter::formatToValue(
1101 URelativeDateTimeUnit unit
,
1102 UErrorCode
& status
) const {
1103 return doFormatToValue(
1104 &RelativeDateTimeFormatter::formatRelativeImpl
,
1110 void RelativeDateTimeFormatter::formatRelativeImpl(
1112 URelativeDateTimeUnit unit
,
1113 FormattedRelativeDateTimeData
& output
,
1114 UErrorCode
& status
) const {
1115 if (U_FAILURE(status
)) {
1119 // The full implementation of this depends on CLDR data that is not yet available,
1120 // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data.
1121 // In the meantime do a quick bring-up by calling the old format method; this
1122 // leaves some holes (even for data that is currently available, such as quarter).
1123 // When the new CLDR data is available, update the data storage accordingly,
1124 // rewrite this to use it directly, and rewrite the old format method to call this
1125 // new one; that is covered by http://bugs.icu-project.org/trac/ticket/12171.
1126 UDateDirection direction
= UDAT_DIRECTION_COUNT
;
1127 if (offset
> -2.1 && offset
< 2.1) {
1128 // Allow a 1% epsilon, so offsets in -1.01..-0.99 map to LAST
1129 double offsetx100
= offset
* 100.0;
1130 int32_t intoffset
= (offsetx100
< 0)? (int32_t)(offsetx100
-0.5) : (int32_t)(offsetx100
+0.5);
1131 switch (intoffset
) {
1132 case -200/*-2*/: direction
= UDAT_DIRECTION_LAST_2
; break;
1133 case -100/*-1*/: direction
= UDAT_DIRECTION_LAST
; break;
1134 case 0/* 0*/: direction
= UDAT_DIRECTION_THIS
; break;
1135 case 100/* 1*/: direction
= UDAT_DIRECTION_NEXT
; break;
1136 case 200/* 2*/: direction
= UDAT_DIRECTION_NEXT_2
; break;
1140 UDateAbsoluteUnit absunit
= UDAT_ABSOLUTE_UNIT_COUNT
;
1142 case UDAT_REL_UNIT_YEAR
: absunit
= UDAT_ABSOLUTE_YEAR
; break;
1143 case UDAT_REL_UNIT_QUARTER
: absunit
= UDAT_ABSOLUTE_QUARTER
; break;
1144 case UDAT_REL_UNIT_MONTH
: absunit
= UDAT_ABSOLUTE_MONTH
; break;
1145 case UDAT_REL_UNIT_WEEK
: absunit
= UDAT_ABSOLUTE_WEEK
; break;
1146 case UDAT_REL_UNIT_DAY
: absunit
= UDAT_ABSOLUTE_DAY
; break;
1147 case UDAT_REL_UNIT_SECOND
:
1148 if (direction
== UDAT_DIRECTION_THIS
) {
1149 absunit
= UDAT_ABSOLUTE_NOW
;
1150 direction
= UDAT_DIRECTION_PLAIN
;
1153 case UDAT_REL_UNIT_SUNDAY
: absunit
= UDAT_ABSOLUTE_SUNDAY
; break;
1154 case UDAT_REL_UNIT_MONDAY
: absunit
= UDAT_ABSOLUTE_MONDAY
; break;
1155 case UDAT_REL_UNIT_TUESDAY
: absunit
= UDAT_ABSOLUTE_TUESDAY
; break;
1156 case UDAT_REL_UNIT_WEDNESDAY
: absunit
= UDAT_ABSOLUTE_WEDNESDAY
; break;
1157 case UDAT_REL_UNIT_THURSDAY
: absunit
= UDAT_ABSOLUTE_THURSDAY
; break;
1158 case UDAT_REL_UNIT_FRIDAY
: absunit
= UDAT_ABSOLUTE_FRIDAY
; break;
1159 case UDAT_REL_UNIT_SATURDAY
: absunit
= UDAT_ABSOLUTE_SATURDAY
; break;
1162 if (direction
!= UDAT_DIRECTION_COUNT
&& absunit
!= UDAT_ABSOLUTE_UNIT_COUNT
) {
1163 formatAbsoluteImpl(direction
, absunit
, output
, status
);
1164 if (output
.getStringRef().length() != 0) {
1168 // otherwise fallback to formatNumeric
1169 formatNumericImpl(offset
, unit
, output
, status
);
1172 UnicodeString
& RelativeDateTimeFormatter::combineDateAndTime(
1173 const UnicodeString
& relativeDateString
, const UnicodeString
& timeString
,
1174 UnicodeString
& appendTo
, UErrorCode
& status
) const {
1175 return fCache
->getCombinedDateAndTime()->format(
1176 timeString
, relativeDateString
, appendTo
, status
);
1179 UnicodeString
& RelativeDateTimeFormatter::adjustForContext(UnicodeString
&str
) const {
1180 if (fOptBreakIterator
== nullptr
1181 || str
.length() == 0 || !u_islower(str
.char32At(0))) {
1185 // Must guarantee that one thread at a time accesses the shared break
1187 static UMutex
*gBrkIterMutex
= STATIC_NEW(UMutex
);
1188 Mutex
lock(gBrkIterMutex
);
1190 fOptBreakIterator
->get(),
1192 U_TITLECASE_NO_LOWERCASE
| U_TITLECASE_NO_BREAK_ADJUSTMENT
);
1196 UBool
RelativeDateTimeFormatter::checkNoAdjustForContext(UErrorCode
& status
) const {
1197 // This is unsupported because it's hard to keep fields in sync with title
1198 // casing. The code could be written and tested if there is demand.
1199 if (fOptBreakIterator
!= nullptr) {
1200 status
= U_UNSUPPORTED_ERROR
;
1206 void RelativeDateTimeFormatter::init(
1207 NumberFormat
*nfToAdopt
,
1208 BreakIterator
*biToAdopt
,
1209 UErrorCode
&status
) {
1210 LocalPointer
<NumberFormat
> nf(nfToAdopt
);
1211 LocalPointer
<BreakIterator
> bi(biToAdopt
);
1212 UnifiedCache::getByLocale(fLocale
, fCache
, status
);
1213 if (U_FAILURE(status
)) {
1216 const SharedPluralRules
*pr
= PluralRules::createSharedInstance(
1217 fLocale
, UPLURAL_TYPE_CARDINAL
, status
);
1218 if (U_FAILURE(status
)) {
1221 SharedObject::copyPtr(pr
, fPluralRules
);
1224 const SharedNumberFormat
*shared
= NumberFormat::createSharedInstance(
1225 fLocale
, UNUM_DECIMAL
, status
);
1226 if (U_FAILURE(status
)) {
1229 SharedObject::copyPtr(shared
, fNumberFormat
);
1230 shared
->removeRef();
1232 SharedNumberFormat
*shared
= new SharedNumberFormat(nf
.getAlias());
1233 if (shared
== nullptr) {
1234 status
= U_MEMORY_ALLOCATION_ERROR
;
1238 SharedObject::copyPtr(shared
, fNumberFormat
);
1241 SharedObject::clearPtr(fOptBreakIterator
);
1243 SharedBreakIterator
*shared
= new SharedBreakIterator(bi
.getAlias());
1244 if (shared
== nullptr) {
1245 status
= U_MEMORY_ALLOCATION_ERROR
;
1249 SharedObject::copyPtr(shared
, fOptBreakIterator
);
1260 // Magic number: "FRDT" (FormattedRelativeDateTime) in ASCII
1261 UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL(
1262 FormattedRelativeDateTime
,
1263 UFormattedRelativeDateTime
,
1264 UFormattedRelativeDateTimeImpl
,
1265 UFormattedRelativeDateTimeApiHelper
,
1270 U_CAPI URelativeDateTimeFormatter
* U_EXPORT2
1271 ureldatefmt_open( const char* locale
,
1272 UNumberFormat
* nfToAdopt
,
1273 UDateRelativeDateTimeFormatterStyle width
,
1274 UDisplayContext capitalizationContext
,
1275 UErrorCode
* status
)
1277 if (U_FAILURE(*status
)) {
1280 LocalPointer
<RelativeDateTimeFormatter
> formatter(new RelativeDateTimeFormatter(Locale(locale
),
1281 (NumberFormat
*)nfToAdopt
, width
,
1282 capitalizationContext
, *status
), *status
);
1283 if (U_FAILURE(*status
)) {
1286 return (URelativeDateTimeFormatter
*)formatter
.orphan();
1289 U_CAPI
void U_EXPORT2
1290 ureldatefmt_close(URelativeDateTimeFormatter
*reldatefmt
)
1292 delete (RelativeDateTimeFormatter
*)reldatefmt
;
1295 U_CAPI
int32_t U_EXPORT2
1296 ureldatefmt_formatNumeric( const URelativeDateTimeFormatter
* reldatefmt
,
1298 URelativeDateTimeUnit unit
,
1300 int32_t resultCapacity
,
1303 if (U_FAILURE(*status
)) {
1306 if (result
== nullptr ? resultCapacity
!= 0 : resultCapacity
< 0) {
1307 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1311 if (result
!= nullptr) {
1312 // nullptr destination for pure preflighting: empty dummy string
1313 // otherwise, alias the destination buffer (copied from udat_format)
1314 res
.setTo(result
, 0, resultCapacity
);
1316 ((RelativeDateTimeFormatter
*)reldatefmt
)->formatNumeric(offset
, unit
, res
, *status
);
1317 if (U_FAILURE(*status
)) {
1320 return res
.extract(result
, resultCapacity
, *status
);
1323 U_STABLE
void U_EXPORT2
1324 ureldatefmt_formatNumericToResult(
1325 const URelativeDateTimeFormatter
* reldatefmt
,
1327 URelativeDateTimeUnit unit
,
1328 UFormattedRelativeDateTime
* result
,
1329 UErrorCode
* status
) {
1330 if (U_FAILURE(*status
)) {
1333 auto* fmt
= reinterpret_cast<const RelativeDateTimeFormatter
*>(reldatefmt
);
1334 auto* resultImpl
= UFormattedRelativeDateTimeApiHelper::validate(result
, *status
);
1335 resultImpl
->fImpl
= fmt
->formatNumericToValue(offset
, unit
, *status
);
1338 U_CAPI
int32_t U_EXPORT2
1339 ureldatefmt_format( const URelativeDateTimeFormatter
* reldatefmt
,
1341 URelativeDateTimeUnit unit
,
1343 int32_t resultCapacity
,
1346 if (U_FAILURE(*status
)) {
1349 if (result
== nullptr ? resultCapacity
!= 0 : resultCapacity
< 0) {
1350 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1354 if (result
!= nullptr) {
1355 // nullptr destination for pure preflighting: empty dummy string
1356 // otherwise, alias the destination buffer (copied from udat_format)
1357 res
.setTo(result
, 0, resultCapacity
);
1359 ((RelativeDateTimeFormatter
*)reldatefmt
)->format(offset
, unit
, res
, *status
);
1360 if (U_FAILURE(*status
)) {
1363 return res
.extract(result
, resultCapacity
, *status
);
1366 U_DRAFT
void U_EXPORT2
1367 ureldatefmt_formatToResult(
1368 const URelativeDateTimeFormatter
* reldatefmt
,
1370 URelativeDateTimeUnit unit
,
1371 UFormattedRelativeDateTime
* result
,
1372 UErrorCode
* status
) {
1373 if (U_FAILURE(*status
)) {
1376 auto* fmt
= reinterpret_cast<const RelativeDateTimeFormatter
*>(reldatefmt
);
1377 auto* resultImpl
= UFormattedRelativeDateTimeApiHelper::validate(result
, *status
);
1378 resultImpl
->fImpl
= fmt
->formatToValue(offset
, unit
, *status
);
1381 U_CAPI
int32_t U_EXPORT2
1382 ureldatefmt_combineDateAndTime( const URelativeDateTimeFormatter
* reldatefmt
,
1383 const UChar
* relativeDateString
,
1384 int32_t relativeDateStringLen
,
1385 const UChar
* timeString
,
1386 int32_t timeStringLen
,
1388 int32_t resultCapacity
,
1389 UErrorCode
* status
)
1391 if (U_FAILURE(*status
)) {
1394 if (result
== nullptr ? resultCapacity
!= 0 : resultCapacity
< 0 ||
1395 (relativeDateString
== nullptr ? relativeDateStringLen
!= 0 : relativeDateStringLen
< -1) ||
1396 (timeString
== nullptr ? timeStringLen
!= 0 : timeStringLen
< -1)) {
1397 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1400 UnicodeString
relDateStr((UBool
)(relativeDateStringLen
== -1), relativeDateString
, relativeDateStringLen
);
1401 UnicodeString
timeStr((UBool
)(timeStringLen
== -1), timeString
, timeStringLen
);
1402 UnicodeString
res(result
, 0, resultCapacity
);
1403 ((RelativeDateTimeFormatter
*)reldatefmt
)->combineDateAndTime(relDateStr
, timeStr
, res
, *status
);
1404 if (U_FAILURE(*status
)) {
1407 return res
.extract(result
, resultCapacity
, *status
);
1410 #endif /* !UCONFIG_NO_FORMATTING */