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 "formatted_string_builder.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
;
319 return UDAT_ABSOLUTE_HOUR
;
321 return UDAT_ABSOLUTE_MINUTE
;
327 static int32_t keyToDirection(const char* key
) {
328 if (uprv_strcmp(key
, "-2") == 0) {
329 return UDAT_DIRECTION_LAST_2
;
331 if (uprv_strcmp(key
, "-1") == 0) {
332 return UDAT_DIRECTION_LAST
;
334 if (uprv_strcmp(key
, "0") == 0) {
335 return UDAT_DIRECTION_THIS
;
337 if (uprv_strcmp(key
, "1") == 0) {
338 return UDAT_DIRECTION_NEXT
;
340 if (uprv_strcmp(key
, "2") == 0) {
341 return UDAT_DIRECTION_NEXT_2
;
346 // Values kept between levels of parsing the CLDR data.
347 int32_t pastFutureIndex
; // 0 == past or 1 == future
348 UDateRelativeDateTimeFormatterStyle style
; // {LONG, SHORT, NARROW}
349 RelAbsUnit genericUnit
;
351 RelativeDateTimeCacheData
&outputData
;
354 RelDateTimeFmtDataSink(RelativeDateTimeCacheData
& cacheData
)
355 : outputData(cacheData
) {
356 // Clear cacheData.fallBackCache
357 cacheData
.fallBackCache
[UDAT_STYLE_LONG
] = -1;
358 cacheData
.fallBackCache
[UDAT_STYLE_SHORT
] = -1;
359 cacheData
.fallBackCache
[UDAT_STYLE_NARROW
] = -1;
362 ~RelDateTimeFmtDataSink();
365 static UDateRelativeDateTimeFormatterStyle
styleFromString(const char *s
) {
366 int32_t len
= static_cast<int32_t>(uprv_strlen(s
));
367 if (len
>= 7 && uprv_strcmp(s
+ len
- 7, "-narrow") == 0) {
368 return UDAT_STYLE_NARROW
;
370 if (len
>= 6 && uprv_strcmp(s
+ len
- 6, "-short") == 0) {
371 return UDAT_STYLE_SHORT
;
373 return UDAT_STYLE_LONG
;
376 static int32_t styleSuffixLength(UDateRelativeDateTimeFormatterStyle style
) {
378 case UDAT_STYLE_NARROW
:
380 case UDAT_STYLE_SHORT
:
388 static UDateRelativeDateTimeFormatterStyle
styleFromAliasUnicodeString(UnicodeString s
) {
389 static const UChar narrow
[7] = {0x002D, 0x006E, 0x0061, 0x0072, 0x0072, 0x006F, 0x0077};
390 static const UChar sshort
[6] = {0x002D, 0x0073, 0x0068, 0x006F, 0x0072, 0x0074,};
391 if (s
.endsWith(narrow
, 7)) {
392 return UDAT_STYLE_NARROW
;
394 if (s
.endsWith(sshort
, 6)) {
395 return UDAT_STYLE_SHORT
;
397 return UDAT_STYLE_LONG
;
400 static RelAbsUnit
unitOrNegativeFromString(const char* keyword
, int32_t length
) {
401 // Quick check from string to enum.
404 if (uprv_strncmp(keyword
, "day", length
) == 0) {
406 } else if (uprv_strncmp(keyword
, "sun", length
) == 0) {
408 } else if (uprv_strncmp(keyword
, "mon", length
) == 0) {
410 } else if (uprv_strncmp(keyword
, "tue", length
) == 0) {
412 } else if (uprv_strncmp(keyword
, "wed", length
) == 0) {
414 } else if (uprv_strncmp(keyword
, "thu", length
) == 0) {
416 } else if (uprv_strncmp(keyword
, "fri", length
) == 0) {
418 } else if (uprv_strncmp(keyword
, "sat", length
) == 0) {
423 if (uprv_strncmp(keyword
, "hour", length
) == 0) {
425 } else if (uprv_strncmp(keyword
, "week", length
) == 0) {
427 } else if (uprv_strncmp(keyword
, "year", length
) == 0) {
432 if (uprv_strncmp(keyword
, "month", length
) == 0) {
437 if (uprv_strncmp(keyword
, "minute", length
) == 0) {
439 } else if (uprv_strncmp(keyword
, "second", length
) == 0) {
444 if (uprv_strncmp(keyword
, "quarter", length
) == 0) {
445 return QUARTER
; // TODO: Check @provisional
454 void handlePlainDirection(ResourceValue
&value
, UErrorCode
&errorCode
) {
455 // Handle Display Name for PLAIN direction for some units.
456 if (U_FAILURE(errorCode
)) { return; }
458 int32_t absUnit
= absUnitFromGeneric(genericUnit
);
460 return; // Not interesting.
463 // Store displayname if not set.
464 if (outputData
.absoluteUnits
[style
]
465 [absUnit
][UDAT_DIRECTION_PLAIN
].isEmpty()) {
466 outputData
.absoluteUnits
[style
]
467 [absUnit
][UDAT_DIRECTION_PLAIN
].fastCopyFrom(value
.getUnicodeString(errorCode
));
472 void consumeTableRelative(const char *key
, ResourceValue
&value
, UErrorCode
&errorCode
) {
473 ResourceTable unitTypesTable
= value
.getTable(errorCode
);
474 if (U_FAILURE(errorCode
)) { return; }
476 for (int32_t i
= 0; unitTypesTable
.getKeyAndValue(i
, key
, value
); ++i
) {
477 if (value
.getType() == URES_STRING
) {
478 int32_t direction
= keyToDirection(key
);
483 int32_t relUnitIndex
= relUnitFromGeneric(genericUnit
);
484 if (relUnitIndex
== UDAT_REL_UNIT_SECOND
&& uprv_strcmp(key
, "0") == 0 &&
485 outputData
.absoluteUnits
[style
][UDAT_ABSOLUTE_NOW
][UDAT_DIRECTION_PLAIN
].isEmpty()) {
487 outputData
.absoluteUnits
[style
][UDAT_ABSOLUTE_NOW
]
488 [UDAT_DIRECTION_PLAIN
].fastCopyFrom(value
.getUnicodeString(errorCode
));
491 int32_t absUnitIndex
= absUnitFromGeneric(genericUnit
);
492 if (absUnitIndex
< 0) {
495 // Only reset if slot is empty.
496 if (outputData
.absoluteUnits
[style
][absUnitIndex
][direction
].isEmpty()) {
497 outputData
.absoluteUnits
[style
][absUnitIndex
]
498 [direction
].fastCopyFrom(value
.getUnicodeString(errorCode
));
504 void consumeTimeDetail(int32_t relUnitIndex
,
505 const char *key
, ResourceValue
&value
, UErrorCode
&errorCode
) {
506 ResourceTable unitTypesTable
= value
.getTable(errorCode
);
507 if (U_FAILURE(errorCode
)) { return; }
509 for (int32_t i
= 0; unitTypesTable
.getKeyAndValue(i
, key
, value
); ++i
) {
510 if (value
.getType() == URES_STRING
) {
511 int32_t pluralIndex
= StandardPlural::indexOrNegativeFromString(key
);
512 if (pluralIndex
>= 0) {
513 SimpleFormatter
**patterns
=
514 outputData
.relativeUnitsFormatters
[style
][relUnitIndex
]
516 // Only set if not already established.
517 if (patterns
[pluralIndex
] == nullptr) {
518 patterns
[pluralIndex
] = new SimpleFormatter(
519 value
.getUnicodeString(errorCode
), 0, 1, errorCode
);
520 if (patterns
[pluralIndex
] == nullptr) {
521 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
529 void consumeTableRelativeTime(const char *key
, ResourceValue
&value
, UErrorCode
&errorCode
) {
530 ResourceTable relativeTimeTable
= value
.getTable(errorCode
);
531 if (U_FAILURE(errorCode
)) { return; }
533 int32_t relUnitIndex
= relUnitFromGeneric(genericUnit
);
534 if (relUnitIndex
< 0) {
537 for (int32_t i
= 0; relativeTimeTable
.getKeyAndValue(i
, key
, value
); ++i
) {
538 if (uprv_strcmp(key
, "past") == 0) {
540 } else if (uprv_strcmp(key
, "future") == 0) {
546 consumeTimeDetail(relUnitIndex
, key
, value
, errorCode
);
550 void consumeAlias(const char *key
, const ResourceValue
&value
, UErrorCode
&errorCode
) {
552 UDateRelativeDateTimeFormatterStyle sourceStyle
= styleFromString(key
);
553 const UnicodeString valueStr
= value
.getAliasUnicodeString(errorCode
);
554 if (U_FAILURE(errorCode
)) { return; }
556 UDateRelativeDateTimeFormatterStyle targetStyle
=
557 styleFromAliasUnicodeString(valueStr
);
559 if (sourceStyle
== targetStyle
) {
560 errorCode
= U_INVALID_FORMAT_ERROR
;
563 if (outputData
.fallBackCache
[sourceStyle
] != -1 &&
564 outputData
.fallBackCache
[sourceStyle
] != targetStyle
) {
565 errorCode
= U_INVALID_FORMAT_ERROR
;
568 outputData
.fallBackCache
[sourceStyle
] = targetStyle
;
571 void consumeTimeUnit(const char *key
, ResourceValue
&value
, UErrorCode
&errorCode
) {
572 ResourceTable unitTypesTable
= value
.getTable(errorCode
);
573 if (U_FAILURE(errorCode
)) { return; }
575 for (int32_t i
= 0; unitTypesTable
.getKeyAndValue(i
, key
, value
); ++i
) {
576 // Handle display name.
577 if (uprv_strcmp(key
, "dn") == 0 && value
.getType() == URES_STRING
) {
578 handlePlainDirection(value
, errorCode
);
580 if (value
.getType() == URES_TABLE
) {
581 if (uprv_strcmp(key
, "relative") == 0) {
582 consumeTableRelative(key
, value
, errorCode
);
583 } else if (uprv_strcmp(key
, "relativeTime") == 0) {
584 consumeTableRelativeTime(key
, value
, errorCode
);
590 virtual void put(const char *key
, ResourceValue
&value
,
591 UBool
/*noFallback*/, UErrorCode
&errorCode
) {
592 // Main entry point to sink
593 ResourceTable table
= value
.getTable(errorCode
);
594 if (U_FAILURE(errorCode
)) { return; }
595 for (int32_t i
= 0; table
.getKeyAndValue(i
, key
, value
); ++i
) {
596 if (value
.getType() == URES_ALIAS
) {
597 consumeAlias(key
, value
, errorCode
);
599 style
= styleFromString(key
);
600 int32_t unitSize
= static_cast<int32_t>(uprv_strlen(key
)) - styleSuffixLength(style
);
601 genericUnit
= unitOrNegativeFromString(key
, unitSize
);
602 if (style
>= 0 && genericUnit
!= INVALID_UNIT
) {
603 consumeTimeUnit(key
, value
, errorCode
);
611 // Virtual destructors must be defined out of line.
612 RelDateTimeFmtDataSink::~RelDateTimeFmtDataSink() {}
615 static const DateFormatSymbols::DtWidthType styleToDateFormatSymbolWidth
[UDAT_STYLE_COUNT
] = {
616 DateFormatSymbols::WIDE
, DateFormatSymbols::SHORT
, DateFormatSymbols::NARROW
619 // Get days of weeks from the DateFormatSymbols class.
620 static void loadWeekdayNames(UnicodeString absoluteUnits
[UDAT_STYLE_COUNT
]
621 [UDAT_ABSOLUTE_UNIT_COUNT
][UDAT_DIRECTION_COUNT
],
622 const char* localeId
,
623 UErrorCode
& status
) {
624 if (U_FAILURE(status
)) {
627 Locale
locale(localeId
);
628 DateFormatSymbols
dfSym(locale
, status
);
629 if (U_FAILURE(status
)) {
632 for (int32_t style
= 0; style
< UDAT_STYLE_COUNT
; ++style
) {
633 DateFormatSymbols::DtWidthType dtfmtWidth
= styleToDateFormatSymbolWidth
[style
];
635 const UnicodeString
* weekdayNames
=
636 dfSym
.getWeekdays(count
, DateFormatSymbols::STANDALONE
, dtfmtWidth
);
637 for (int32_t dayIndex
= UDAT_ABSOLUTE_SUNDAY
;
638 dayIndex
<= UDAT_ABSOLUTE_SATURDAY
; ++ dayIndex
) {
639 int32_t dateSymbolIndex
= (dayIndex
- UDAT_ABSOLUTE_SUNDAY
) + UCAL_SUNDAY
;
640 absoluteUnits
[style
][dayIndex
][UDAT_DIRECTION_PLAIN
].fastCopyFrom(
641 weekdayNames
[dateSymbolIndex
]);
646 static UBool
loadUnitData(
647 const UResourceBundle
*resource
,
648 RelativeDateTimeCacheData
&cacheData
,
649 const char* localeId
,
650 UErrorCode
&status
) {
652 RelDateTimeFmtDataSink
sink(cacheData
);
654 ures_getAllItemsWithFallback(resource
, "fields", sink
, status
);
655 if (U_FAILURE(status
)) {
659 // Get the weekday names from DateFormatSymbols.
660 loadWeekdayNames(cacheData
.absoluteUnits
, localeId
, status
);
661 return U_SUCCESS(status
);
664 static UBool
getDateTimePattern(
665 const UResourceBundle
*resource
,
666 UnicodeString
&result
,
667 UErrorCode
&status
) {
668 UnicodeString defaultCalendarName
;
669 if (!getStringWithFallback(
676 CharString pathBuffer
;
677 pathBuffer
.append("calendar/", status
)
678 .appendInvariantChars(defaultCalendarName
, status
)
679 .append("/DateTimePatterns", status
);
680 LocalUResourceBundlePointer
topLevel(
681 ures_getByKeyWithFallback(
682 resource
, pathBuffer
.data(), nullptr, &status
));
683 if (U_FAILURE(status
)) {
686 int32_t size
= ures_getSize(topLevel
.getAlias());
688 // Oops, size is too small to access the index that we want, fallback
689 // to a hard-coded value.
690 result
= UNICODE_STRING_SIMPLE("{1} {0}");
693 return getStringByIndex(topLevel
.getAlias(), 8, result
, status
);
696 template<> U_I18N_API
697 const RelativeDateTimeCacheData
*LocaleCacheKey
<RelativeDateTimeCacheData
>::createObject(const void * /*unused*/, UErrorCode
&status
) const {
698 const char *localeId
= fLoc
.getName();
699 LocalUResourceBundlePointer
topLevel(ures_open(nullptr, localeId
, &status
));
700 if (U_FAILURE(status
)) {
703 LocalPointer
<RelativeDateTimeCacheData
> result(
704 new RelativeDateTimeCacheData());
705 if (result
.isNull()) {
706 status
= U_MEMORY_ALLOCATION_ERROR
;
716 UnicodeString dateTimePattern
;
717 if (!getDateTimePattern(topLevel
.getAlias(), dateTimePattern
, status
)) {
720 result
->adoptCombinedDateAndTime(
721 new SimpleFormatter(dateTimePattern
, 2, 2, TRUE
, status
));
722 if (U_FAILURE(status
)) {
726 return result
.orphan();
731 static constexpr number::impl::Field kRDTNumericField
732 = StringBuilderFieldUtils::compress
<UFIELD_CATEGORY_RELATIVE_DATETIME
, UDAT_REL_NUMERIC_FIELD
>();
734 static constexpr number::impl::Field kRDTLiteralField
735 = StringBuilderFieldUtils::compress
<UFIELD_CATEGORY_RELATIVE_DATETIME
, UDAT_REL_LITERAL_FIELD
>();
737 class FormattedRelativeDateTimeData
: public FormattedValueStringBuilderImpl
{
739 FormattedRelativeDateTimeData() : FormattedValueStringBuilderImpl(kRDTNumericField
) {}
740 virtual ~FormattedRelativeDateTimeData();
743 FormattedRelativeDateTimeData::~FormattedRelativeDateTimeData() = default;
746 UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedRelativeDateTime
)
749 RelativeDateTimeFormatter::RelativeDateTimeFormatter(UErrorCode
& status
) :
751 fNumberFormat(nullptr),
752 fPluralRules(nullptr),
753 fStyle(UDAT_STYLE_LONG
),
754 fContext(UDISPCTX_CAPITALIZATION_NONE
),
755 fOptBreakIterator(nullptr) {
756 init(nullptr, nullptr, status
);
759 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
760 const Locale
& locale
, UErrorCode
& status
) :
762 fNumberFormat(nullptr),
763 fPluralRules(nullptr),
764 fStyle(UDAT_STYLE_LONG
),
765 fContext(UDISPCTX_CAPITALIZATION_NONE
),
766 fOptBreakIterator(nullptr),
768 init(nullptr, nullptr, status
);
771 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
772 const Locale
& locale
, NumberFormat
*nfToAdopt
, UErrorCode
& status
) :
774 fNumberFormat(nullptr),
775 fPluralRules(nullptr),
776 fStyle(UDAT_STYLE_LONG
),
777 fContext(UDISPCTX_CAPITALIZATION_NONE
),
778 fOptBreakIterator(nullptr),
780 init(nfToAdopt
, nullptr, status
);
783 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
784 const Locale
& locale
,
785 NumberFormat
*nfToAdopt
,
786 UDateRelativeDateTimeFormatterStyle styl
,
787 UDisplayContext capitalizationContext
,
788 UErrorCode
& status
) :
790 fNumberFormat(nullptr),
791 fPluralRules(nullptr),
793 fContext(capitalizationContext
),
794 fOptBreakIterator(nullptr),
796 if (U_FAILURE(status
)) {
799 if ((capitalizationContext
>> 8) != UDISPCTX_TYPE_CAPITALIZATION
) {
800 status
= U_ILLEGAL_ARGUMENT_ERROR
;
803 if (capitalizationContext
== UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE
) {
804 BreakIterator
*bi
= BreakIterator::createSentenceInstance(locale
, status
);
805 if (U_FAILURE(status
)) {
808 init(nfToAdopt
, bi
, status
);
810 init(nfToAdopt
, nullptr, status
);
814 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
815 const RelativeDateTimeFormatter
& other
)
817 fCache(other
.fCache
),
818 fNumberFormat(other
.fNumberFormat
),
819 fPluralRules(other
.fPluralRules
),
820 fStyle(other
.fStyle
),
821 fContext(other
.fContext
),
822 fOptBreakIterator(other
.fOptBreakIterator
),
823 fLocale(other
.fLocale
) {
825 fNumberFormat
->addRef();
826 fPluralRules
->addRef();
827 if (fOptBreakIterator
!= nullptr) {
828 fOptBreakIterator
->addRef();
832 RelativeDateTimeFormatter
& RelativeDateTimeFormatter::operator=(
833 const RelativeDateTimeFormatter
& other
) {
834 if (this != &other
) {
835 SharedObject::copyPtr(other
.fCache
, fCache
);
836 SharedObject::copyPtr(other
.fNumberFormat
, fNumberFormat
);
837 SharedObject::copyPtr(other
.fPluralRules
, fPluralRules
);
838 SharedObject::copyPtr(other
.fOptBreakIterator
, fOptBreakIterator
);
839 fStyle
= other
.fStyle
;
840 fContext
= other
.fContext
;
841 fLocale
= other
.fLocale
;
846 RelativeDateTimeFormatter::~RelativeDateTimeFormatter() {
847 if (fCache
!= nullptr) {
850 if (fNumberFormat
!= nullptr) {
851 fNumberFormat
->removeRef();
853 if (fPluralRules
!= nullptr) {
854 fPluralRules
->removeRef();
856 if (fOptBreakIterator
!= nullptr) {
857 fOptBreakIterator
->removeRef();
861 const NumberFormat
& RelativeDateTimeFormatter::getNumberFormat() const {
862 return **fNumberFormat
;
865 UDisplayContext
RelativeDateTimeFormatter::getCapitalizationContext() const {
869 UDateRelativeDateTimeFormatterStyle
RelativeDateTimeFormatter::getFormatStyle() const {
874 // To reduce boilerplate code, we use a helper function that forwards variadic
875 // arguments to the formatImpl function.
877 template<typename F
, typename
... Args
>
878 UnicodeString
& RelativeDateTimeFormatter::doFormat(
880 UnicodeString
& appendTo
,
882 Args
... args
) const {
883 FormattedRelativeDateTimeData output
;
884 (this->*callback
)(std::forward
<Args
>(args
)..., output
, status
);
885 if (U_FAILURE(status
)) {
888 UnicodeString result
= output
.getStringRef().toUnicodeString();
889 return appendTo
.append(adjustForContext(result
));
892 template<typename F
, typename
... Args
>
893 FormattedRelativeDateTime
RelativeDateTimeFormatter::doFormatToValue(
896 Args
... args
) const {
897 if (!checkNoAdjustForContext(status
)) {
898 return FormattedRelativeDateTime(status
);
900 LocalPointer
<FormattedRelativeDateTimeData
> output(
901 new FormattedRelativeDateTimeData(), status
);
902 if (U_FAILURE(status
)) {
903 return FormattedRelativeDateTime(status
);
905 (this->*callback
)(std::forward
<Args
>(args
)..., *output
, status
);
906 output
->getStringRef().writeTerminator(status
);
907 return FormattedRelativeDateTime(output
.orphan());
910 UnicodeString
& RelativeDateTimeFormatter::format(
912 UDateDirection direction
,
913 UDateRelativeUnit unit
,
914 UnicodeString
& appendTo
,
915 UErrorCode
& status
) const {
917 &RelativeDateTimeFormatter::formatImpl
,
925 FormattedRelativeDateTime
RelativeDateTimeFormatter::formatToValue(
927 UDateDirection direction
,
928 UDateRelativeUnit unit
,
929 UErrorCode
& status
) const {
930 return doFormatToValue(
931 &RelativeDateTimeFormatter::formatImpl
,
938 void RelativeDateTimeFormatter::formatImpl(
940 UDateDirection direction
,
941 UDateRelativeUnit unit
,
942 FormattedRelativeDateTimeData
& output
,
943 UErrorCode
& status
) const {
944 if (U_FAILURE(status
)) {
947 if (direction
!= UDAT_DIRECTION_LAST
&& direction
!= UDAT_DIRECTION_NEXT
) {
948 status
= U_ILLEGAL_ARGUMENT_ERROR
;
951 int32_t bFuture
= direction
== UDAT_DIRECTION_NEXT
? 1 : 0;
953 StandardPlural::Form pluralForm
;
954 QuantityFormatter::formatAndSelect(
958 output
.getStringRef(),
961 if (U_FAILURE(status
)) {
965 const SimpleFormatter
* formatter
=
966 fCache
->getRelativeUnitFormatter(fStyle
, unit
, bFuture
, pluralForm
);
967 if (formatter
== nullptr) {
968 // TODO: WARN - look at quantity formatter's action with an error.
969 status
= U_INVALID_FORMAT_ERROR
;
973 number::impl::SimpleModifier
modifier(*formatter
, kRDTLiteralField
, false);
974 modifier
.formatAsPrefixSuffix(
975 output
.getStringRef(), 0, output
.getStringRef().length(), status
);
978 UnicodeString
& RelativeDateTimeFormatter::formatNumeric(
980 URelativeDateTimeUnit unit
,
981 UnicodeString
& appendTo
,
982 UErrorCode
& status
) const {
984 &RelativeDateTimeFormatter::formatNumericImpl
,
991 FormattedRelativeDateTime
RelativeDateTimeFormatter::formatNumericToValue(
993 URelativeDateTimeUnit unit
,
994 UErrorCode
& status
) const {
995 return doFormatToValue(
996 &RelativeDateTimeFormatter::formatNumericImpl
,
1002 void RelativeDateTimeFormatter::formatNumericImpl(
1004 URelativeDateTimeUnit unit
,
1005 FormattedRelativeDateTimeData
& output
,
1006 UErrorCode
& status
) const {
1007 if (U_FAILURE(status
)) {
1010 UDateDirection direction
= UDAT_DIRECTION_NEXT
;
1011 if (std::signbit(offset
)) { // needed to handle -0.0
1012 direction
= UDAT_DIRECTION_LAST
;
1015 if (direction
!= UDAT_DIRECTION_LAST
&& direction
!= UDAT_DIRECTION_NEXT
) {
1016 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1019 int32_t bFuture
= direction
== UDAT_DIRECTION_NEXT
? 1 : 0;
1021 StandardPlural::Form pluralForm
;
1022 QuantityFormatter::formatAndSelect(
1026 output
.getStringRef(),
1029 if (U_FAILURE(status
)) {
1033 const SimpleFormatter
* formatter
=
1034 fCache
->getRelativeDateTimeUnitFormatter(fStyle
, unit
, bFuture
, pluralForm
);
1035 if (formatter
== nullptr) {
1036 // TODO: WARN - look at quantity formatter's action with an error.
1037 status
= U_INVALID_FORMAT_ERROR
;
1041 number::impl::SimpleModifier
modifier(*formatter
, kRDTLiteralField
, false);
1042 modifier
.formatAsPrefixSuffix(
1043 output
.getStringRef(), 0, output
.getStringRef().length(), status
);
1046 UnicodeString
& RelativeDateTimeFormatter::format(
1047 UDateDirection direction
,
1048 UDateAbsoluteUnit unit
,
1049 UnicodeString
& appendTo
,
1050 UErrorCode
& status
) const {
1052 &RelativeDateTimeFormatter::formatAbsoluteImpl
,
1059 FormattedRelativeDateTime
RelativeDateTimeFormatter::formatToValue(
1060 UDateDirection direction
,
1061 UDateAbsoluteUnit unit
,
1062 UErrorCode
& status
) const {
1063 return doFormatToValue(
1064 &RelativeDateTimeFormatter::formatAbsoluteImpl
,
1070 void RelativeDateTimeFormatter::formatAbsoluteImpl(
1071 UDateDirection direction
,
1072 UDateAbsoluteUnit unit
,
1073 FormattedRelativeDateTimeData
& output
,
1074 UErrorCode
& status
) const {
1075 if (U_FAILURE(status
)) {
1078 if (unit
== UDAT_ABSOLUTE_NOW
&& direction
!= UDAT_DIRECTION_PLAIN
) {
1079 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1083 // Get string using fallback.
1084 output
.getStringRef().append(
1085 fCache
->getAbsoluteUnitString(fStyle
, unit
, direction
),
1090 UnicodeString
& RelativeDateTimeFormatter::format(
1092 URelativeDateTimeUnit unit
,
1093 UnicodeString
& appendTo
,
1094 UErrorCode
& status
) const {
1096 &RelativeDateTimeFormatter::formatRelativeImpl
,
1103 FormattedRelativeDateTime
RelativeDateTimeFormatter::formatToValue(
1105 URelativeDateTimeUnit unit
,
1106 UErrorCode
& status
) const {
1107 return doFormatToValue(
1108 &RelativeDateTimeFormatter::formatRelativeImpl
,
1114 void RelativeDateTimeFormatter::formatRelativeImpl(
1116 URelativeDateTimeUnit unit
,
1117 FormattedRelativeDateTimeData
& output
,
1118 UErrorCode
& status
) const {
1119 if (U_FAILURE(status
)) {
1123 // The full implementation of this depends on CLDR data that is not yet available,
1124 // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data.
1125 // In the meantime do a quick bring-up by calling the old format method; this
1126 // leaves some holes (even for data that is currently available, such as quarter).
1127 // When the new CLDR data is available, update the data storage accordingly,
1128 // rewrite this to use it directly, and rewrite the old format method to call this
1129 // new one; that is covered by http://bugs.icu-project.org/trac/ticket/12171.
1130 UDateDirection direction
= UDAT_DIRECTION_COUNT
;
1131 if (offset
> -2.1 && offset
< 2.1) {
1132 // Allow a 1% epsilon, so offsets in -1.01..-0.99 map to LAST
1133 double offsetx100
= offset
* 100.0;
1134 int32_t intoffset
= (offsetx100
< 0)? (int32_t)(offsetx100
-0.5) : (int32_t)(offsetx100
+0.5);
1135 switch (intoffset
) {
1136 case -200/*-2*/: direction
= UDAT_DIRECTION_LAST_2
; break;
1137 case -100/*-1*/: direction
= UDAT_DIRECTION_LAST
; break;
1138 case 0/* 0*/: direction
= UDAT_DIRECTION_THIS
; break;
1139 case 100/* 1*/: direction
= UDAT_DIRECTION_NEXT
; break;
1140 case 200/* 2*/: direction
= UDAT_DIRECTION_NEXT_2
; break;
1144 UDateAbsoluteUnit absunit
= UDAT_ABSOLUTE_UNIT_COUNT
;
1146 case UDAT_REL_UNIT_YEAR
: absunit
= UDAT_ABSOLUTE_YEAR
; break;
1147 case UDAT_REL_UNIT_QUARTER
: absunit
= UDAT_ABSOLUTE_QUARTER
; break;
1148 case UDAT_REL_UNIT_MONTH
: absunit
= UDAT_ABSOLUTE_MONTH
; break;
1149 case UDAT_REL_UNIT_WEEK
: absunit
= UDAT_ABSOLUTE_WEEK
; break;
1150 case UDAT_REL_UNIT_DAY
: absunit
= UDAT_ABSOLUTE_DAY
; break;
1151 case UDAT_REL_UNIT_SECOND
:
1152 if (direction
== UDAT_DIRECTION_THIS
) {
1153 absunit
= UDAT_ABSOLUTE_NOW
;
1154 direction
= UDAT_DIRECTION_PLAIN
;
1157 case UDAT_REL_UNIT_SUNDAY
: absunit
= UDAT_ABSOLUTE_SUNDAY
; break;
1158 case UDAT_REL_UNIT_MONDAY
: absunit
= UDAT_ABSOLUTE_MONDAY
; break;
1159 case UDAT_REL_UNIT_TUESDAY
: absunit
= UDAT_ABSOLUTE_TUESDAY
; break;
1160 case UDAT_REL_UNIT_WEDNESDAY
: absunit
= UDAT_ABSOLUTE_WEDNESDAY
; break;
1161 case UDAT_REL_UNIT_THURSDAY
: absunit
= UDAT_ABSOLUTE_THURSDAY
; break;
1162 case UDAT_REL_UNIT_FRIDAY
: absunit
= UDAT_ABSOLUTE_FRIDAY
; break;
1163 case UDAT_REL_UNIT_SATURDAY
: absunit
= UDAT_ABSOLUTE_SATURDAY
; break;
1164 case UDAT_REL_UNIT_HOUR
: absunit
= UDAT_ABSOLUTE_HOUR
; break;
1165 case UDAT_REL_UNIT_MINUTE
: absunit
= UDAT_ABSOLUTE_MINUTE
; break;
1168 if (direction
!= UDAT_DIRECTION_COUNT
&& absunit
!= UDAT_ABSOLUTE_UNIT_COUNT
) {
1169 formatAbsoluteImpl(direction
, absunit
, output
, status
);
1170 if (output
.getStringRef().length() != 0) {
1174 // otherwise fallback to formatNumeric
1175 formatNumericImpl(offset
, unit
, output
, status
);
1178 UnicodeString
& RelativeDateTimeFormatter::combineDateAndTime(
1179 const UnicodeString
& relativeDateString
, const UnicodeString
& timeString
,
1180 UnicodeString
& appendTo
, UErrorCode
& status
) const {
1181 return fCache
->getCombinedDateAndTime()->format(
1182 timeString
, relativeDateString
, appendTo
, status
);
1185 UnicodeString
& RelativeDateTimeFormatter::adjustForContext(UnicodeString
&str
) const {
1186 if (fOptBreakIterator
== nullptr
1187 || str
.length() == 0 || !u_islower(str
.char32At(0))) {
1191 // Must guarantee that one thread at a time accesses the shared break
1193 static UMutex gBrkIterMutex
;
1194 Mutex
lock(&gBrkIterMutex
);
1196 fOptBreakIterator
->get(),
1198 U_TITLECASE_NO_LOWERCASE
| U_TITLECASE_NO_BREAK_ADJUSTMENT
);
1202 UBool
RelativeDateTimeFormatter::checkNoAdjustForContext(UErrorCode
& status
) const {
1203 // This is unsupported because it's hard to keep fields in sync with title
1204 // casing. The code could be written and tested if there is demand.
1205 if (fOptBreakIterator
!= nullptr) {
1206 status
= U_UNSUPPORTED_ERROR
;
1212 void RelativeDateTimeFormatter::init(
1213 NumberFormat
*nfToAdopt
,
1214 BreakIterator
*biToAdopt
,
1215 UErrorCode
&status
) {
1216 LocalPointer
<NumberFormat
> nf(nfToAdopt
);
1217 LocalPointer
<BreakIterator
> bi(biToAdopt
);
1218 UnifiedCache::getByLocale(fLocale
, fCache
, status
);
1219 if (U_FAILURE(status
)) {
1222 const SharedPluralRules
*pr
= PluralRules::createSharedInstance(
1223 fLocale
, UPLURAL_TYPE_CARDINAL
, status
);
1224 if (U_FAILURE(status
)) {
1227 SharedObject::copyPtr(pr
, fPluralRules
);
1230 const SharedNumberFormat
*shared
= NumberFormat::createSharedInstance(
1231 fLocale
, UNUM_DECIMAL
, status
);
1232 if (U_FAILURE(status
)) {
1235 SharedObject::copyPtr(shared
, fNumberFormat
);
1236 shared
->removeRef();
1238 SharedNumberFormat
*shared
= new SharedNumberFormat(nf
.getAlias());
1239 if (shared
== nullptr) {
1240 status
= U_MEMORY_ALLOCATION_ERROR
;
1244 SharedObject::copyPtr(shared
, fNumberFormat
);
1247 SharedObject::clearPtr(fOptBreakIterator
);
1249 SharedBreakIterator
*shared
= new SharedBreakIterator(bi
.getAlias());
1250 if (shared
== nullptr) {
1251 status
= U_MEMORY_ALLOCATION_ERROR
;
1255 SharedObject::copyPtr(shared
, fOptBreakIterator
);
1266 // Magic number: "FRDT" (FormattedRelativeDateTime) in ASCII
1267 UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL(
1268 FormattedRelativeDateTime
,
1269 UFormattedRelativeDateTime
,
1270 UFormattedRelativeDateTimeImpl
,
1271 UFormattedRelativeDateTimeApiHelper
,
1276 U_CAPI URelativeDateTimeFormatter
* U_EXPORT2
1277 ureldatefmt_open( const char* locale
,
1278 UNumberFormat
* nfToAdopt
,
1279 UDateRelativeDateTimeFormatterStyle width
,
1280 UDisplayContext capitalizationContext
,
1281 UErrorCode
* status
)
1283 if (U_FAILURE(*status
)) {
1286 LocalPointer
<RelativeDateTimeFormatter
> formatter(new RelativeDateTimeFormatter(Locale(locale
),
1287 (NumberFormat
*)nfToAdopt
, width
,
1288 capitalizationContext
, *status
), *status
);
1289 if (U_FAILURE(*status
)) {
1292 return (URelativeDateTimeFormatter
*)formatter
.orphan();
1295 U_CAPI
void U_EXPORT2
1296 ureldatefmt_close(URelativeDateTimeFormatter
*reldatefmt
)
1298 delete (RelativeDateTimeFormatter
*)reldatefmt
;
1301 U_CAPI
int32_t U_EXPORT2
1302 ureldatefmt_formatNumeric( const URelativeDateTimeFormatter
* reldatefmt
,
1304 URelativeDateTimeUnit unit
,
1306 int32_t resultCapacity
,
1309 if (U_FAILURE(*status
)) {
1312 if (result
== nullptr ? resultCapacity
!= 0 : resultCapacity
< 0) {
1313 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1317 if (result
!= nullptr) {
1318 // nullptr destination for pure preflighting: empty dummy string
1319 // otherwise, alias the destination buffer (copied from udat_format)
1320 res
.setTo(result
, 0, resultCapacity
);
1322 ((RelativeDateTimeFormatter
*)reldatefmt
)->formatNumeric(offset
, unit
, res
, *status
);
1323 if (U_FAILURE(*status
)) {
1326 return res
.extract(result
, resultCapacity
, *status
);
1329 U_STABLE
void U_EXPORT2
1330 ureldatefmt_formatNumericToResult(
1331 const URelativeDateTimeFormatter
* reldatefmt
,
1333 URelativeDateTimeUnit unit
,
1334 UFormattedRelativeDateTime
* result
,
1335 UErrorCode
* status
) {
1336 if (U_FAILURE(*status
)) {
1339 auto* fmt
= reinterpret_cast<const RelativeDateTimeFormatter
*>(reldatefmt
);
1340 auto* resultImpl
= UFormattedRelativeDateTimeApiHelper::validate(result
, *status
);
1341 resultImpl
->fImpl
= fmt
->formatNumericToValue(offset
, unit
, *status
);
1344 U_CAPI
int32_t U_EXPORT2
1345 ureldatefmt_format( const URelativeDateTimeFormatter
* reldatefmt
,
1347 URelativeDateTimeUnit unit
,
1349 int32_t resultCapacity
,
1352 if (U_FAILURE(*status
)) {
1355 if (result
== nullptr ? resultCapacity
!= 0 : resultCapacity
< 0) {
1356 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1360 if (result
!= nullptr) {
1361 // nullptr destination for pure preflighting: empty dummy string
1362 // otherwise, alias the destination buffer (copied from udat_format)
1363 res
.setTo(result
, 0, resultCapacity
);
1365 ((RelativeDateTimeFormatter
*)reldatefmt
)->format(offset
, unit
, res
, *status
);
1366 if (U_FAILURE(*status
)) {
1369 return res
.extract(result
, resultCapacity
, *status
);
1372 U_DRAFT
void U_EXPORT2
1373 ureldatefmt_formatToResult(
1374 const URelativeDateTimeFormatter
* reldatefmt
,
1376 URelativeDateTimeUnit unit
,
1377 UFormattedRelativeDateTime
* result
,
1378 UErrorCode
* status
) {
1379 if (U_FAILURE(*status
)) {
1382 auto* fmt
= reinterpret_cast<const RelativeDateTimeFormatter
*>(reldatefmt
);
1383 auto* resultImpl
= UFormattedRelativeDateTimeApiHelper::validate(result
, *status
);
1384 resultImpl
->fImpl
= fmt
->formatToValue(offset
, unit
, *status
);
1387 U_CAPI
int32_t U_EXPORT2
1388 ureldatefmt_combineDateAndTime( const URelativeDateTimeFormatter
* reldatefmt
,
1389 const UChar
* relativeDateString
,
1390 int32_t relativeDateStringLen
,
1391 const UChar
* timeString
,
1392 int32_t timeStringLen
,
1394 int32_t resultCapacity
,
1395 UErrorCode
* status
)
1397 if (U_FAILURE(*status
)) {
1400 if (result
== nullptr ? resultCapacity
!= 0 : resultCapacity
< 0 ||
1401 (relativeDateString
== nullptr ? relativeDateStringLen
!= 0 : relativeDateStringLen
< -1) ||
1402 (timeString
== nullptr ? timeStringLen
!= 0 : timeStringLen
< -1)) {
1403 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1406 UnicodeString
relDateStr((UBool
)(relativeDateStringLen
== -1), relativeDateString
, relativeDateStringLen
);
1407 UnicodeString
timeStr((UBool
)(timeStringLen
== -1), timeString
, timeStringLen
);
1408 UnicodeString
res(result
, 0, resultCapacity
);
1409 ((RelativeDateTimeFormatter
*)reldatefmt
)->combineDateAndTime(relDateStr
, timeStr
, res
, *status
);
1410 if (U_FAILURE(*status
)) {
1413 return res
.extract(result
, resultCapacity
, *status
);
1416 #endif /* !UCONFIG_NO_FORMATTING */