]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/reldatefmt.cpp
ICU-57166.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / reldatefmt.cpp
CommitLineData
57a6839d
A
1/*
2******************************************************************************
2ca993e8
A
3* Copyright (C) 2014-2016, International Business Machines Corporation and
4* others. All Rights Reserved.
57a6839d 5******************************************************************************
2ca993e8
A
6*
7* File reldatefmt.cpp
57a6839d
A
8******************************************************************************
9*/
10
11#include "unicode/reldatefmt.h"
12
b331163b 13#if !UCONFIG_NO_FORMATTING && !UCONFIG_NO_BREAK_ITERATION
57a6839d 14
2ca993e8
A
15#include "unicode/dtfmtsym.h"
16#include "unicode/ureldatefmt.h"
17#include "unicode/udisplaycontext.h"
18#include "unicode/unum.h"
57a6839d 19#include "unicode/localpointer.h"
57a6839d 20#include "unicode/plurrule.h"
2ca993e8 21#include "unicode/simpleformatter.h"
57a6839d
A
22#include "unicode/decimfmt.h"
23#include "unicode/numfmt.h"
b331163b 24#include "unicode/brkiter.h"
2ca993e8 25#include "unicode/simpleformatter.h"
57a6839d
A
26#include "uresimp.h"
27#include "unicode/ures.h"
28#include "cstring.h"
29#include "ucln_in.h"
30#include "mutex.h"
31#include "charstr.h"
b331163b 32#include "uassert.h"
2ca993e8
A
33#include "quantityformatter.h"
34#include "resource.h"
b331163b 35#include "sharedbreakiterator.h"
57a6839d
A
36#include "sharedpluralrules.h"
37#include "sharednumberformat.h"
2ca993e8 38#include "standardplural.h"
b331163b 39#include "unifiedcache.h"
57a6839d
A
40
41// Copied from uscript_props.cpp
57a6839d 42
b331163b 43static UMutex gBrkIterMutex = U_MUTEX_INITIALIZER;
57a6839d
A
44
45U_NAMESPACE_BEGIN
46
47// RelativeDateTimeFormatter specific data for a single locale
48class RelativeDateTimeCacheData: public SharedObject {
49public:
2ca993e8
A
50 RelativeDateTimeCacheData() : combinedDateAndTime(NULL) {
51 // Initialize the cache arrays
52 for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
53 for (int32_t relUnit = 0; relUnit < UDAT_RELATIVE_UNIT_COUNT; ++relUnit) {
54 for (int32_t pl = 0; pl < StandardPlural::COUNT; ++pl) {
55 relativeUnitsFormatters[style][relUnit][0][pl] = NULL;
56 relativeUnitsFormatters[style][relUnit][1][pl] = NULL;
57 }
58 }
59 }
60 for (int32_t i = 0; i < UDAT_STYLE_COUNT; ++i) {
61 fallBackCache[i] = -1;
62 }
63 }
57a6839d
A
64 virtual ~RelativeDateTimeCacheData();
65
66 // no numbers: e.g Next Tuesday; Yesterday; etc.
b331163b 67 UnicodeString absoluteUnits[UDAT_STYLE_COUNT][UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT];
57a6839d 68
2ca993e8
A
69 // SimpleFormatter pointers for relative unit format,
70 // e.g., Next Tuesday; Yesterday; etc. For third index, 0
71 // means past, e.g., 5 days ago; 1 means future, e.g., in 5 days.
72 SimpleFormatter *relativeUnitsFormatters[UDAT_STYLE_COUNT]
73 [UDAT_RELATIVE_UNIT_COUNT][2][StandardPlural::COUNT];
74
75 const UnicodeString& getAbsoluteUnitString(int32_t fStyle,
76 UDateAbsoluteUnit unit,
77 UDateDirection direction) const;
78 const SimpleFormatter* getRelativeUnitFormatter(int32_t fStyle,
79 UDateRelativeUnit unit,
80 int32_t pastFutureIndex,
81 int32_t pluralUnit) const;
82
83 const UnicodeString emptyString;
84
85 // Mappping from source to target styles for alias fallback.
86 int32_t fallBackCache[UDAT_STYLE_COUNT];
57a6839d 87
2ca993e8 88 void adoptCombinedDateAndTime(SimpleFormatter *fmtToAdopt) {
57a6839d 89 delete combinedDateAndTime;
2ca993e8 90 combinedDateAndTime = fmtToAdopt;
57a6839d 91 }
2ca993e8 92 const SimpleFormatter *getCombinedDateAndTime() const {
57a6839d
A
93 return combinedDateAndTime;
94 }
2ca993e8 95
57a6839d 96private:
2ca993e8 97 SimpleFormatter *combinedDateAndTime;
57a6839d
A
98 RelativeDateTimeCacheData(const RelativeDateTimeCacheData &other);
99 RelativeDateTimeCacheData& operator=(
100 const RelativeDateTimeCacheData &other);
101};
102
103RelativeDateTimeCacheData::~RelativeDateTimeCacheData() {
2ca993e8
A
104 // clear out the cache arrays
105 for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
106 for (int32_t relUnit = 0; relUnit < UDAT_RELATIVE_UNIT_COUNT; ++relUnit) {
107 for (int32_t pl = 0; pl < StandardPlural::COUNT; ++pl) {
108 delete relativeUnitsFormatters[style][relUnit][0][pl];
109 delete relativeUnitsFormatters[style][relUnit][1][pl];
110 }
111 }
112 }
57a6839d
A
113 delete combinedDateAndTime;
114}
115
2ca993e8
A
116
117// Use fallback cache for absolute units.
118const UnicodeString& RelativeDateTimeCacheData::getAbsoluteUnitString(
119 int32_t fStyle, UDateAbsoluteUnit unit, UDateDirection direction) const {
120 int32_t style = fStyle;
121 do {
122 if (!absoluteUnits[style][unit][direction].isEmpty()) {
123 return absoluteUnits[style][unit][direction];
124 }
125 style = fallBackCache[style];
126 } while (style != -1);
127 return emptyString;
128}
129
130 // Use fallback cache for SimpleFormatter relativeUnits.
131 const SimpleFormatter* RelativeDateTimeCacheData::getRelativeUnitFormatter(
132 int32_t fStyle,
133 UDateRelativeUnit unit,
134 int32_t pastFutureIndex,
135 int32_t pluralUnit) const {
136 int32_t style = fStyle;
137 do {
138 if (relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit] != NULL) {
139 return relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit];
140 }
141 style = fallBackCache[style];
142 } while (style != -1);
143 return NULL; // No formatter found.
144 }
145
57a6839d 146static UBool getStringWithFallback(
2ca993e8 147 const UResourceBundle *resource,
57a6839d
A
148 const char *key,
149 UnicodeString &result,
150 UErrorCode &status) {
151 int32_t len = 0;
152 const UChar *resStr = ures_getStringByKeyWithFallback(
153 resource, key, &len, &status);
154 if (U_FAILURE(status)) {
155 return FALSE;
156 }
157 result.setTo(TRUE, resStr, len);
158 return TRUE;
159}
160
57a6839d
A
161
162static UBool getStringByIndex(
2ca993e8 163 const UResourceBundle *resource,
57a6839d
A
164 int32_t idx,
165 UnicodeString &result,
166 UErrorCode &status) {
167 int32_t len = 0;
168 const UChar *resStr = ures_getStringByIndex(
169 resource, idx, &len, &status);
170 if (U_FAILURE(status)) {
171 return FALSE;
172 }
173 result.setTo(TRUE, resStr, len);
174 return TRUE;
175}
176
2ca993e8 177namespace {
57a6839d 178
2ca993e8
A
179/**
180 * Sink for enumerating all of the measurement unit display names.
181 * Contains inner sink classes, each one corresponding to a level of the resource table.
182 *
183 * More specific bundles (en_GB) are enumerated before their parents (en_001, en, root):
184 * Only store a value if it is still missing, that is, it has not been overridden.
185 *
186 * C++: Each inner sink class has a reference to the main outer sink.
187 */
188struct RelDateTimeFmtDataSink : public ResourceTableSink {
189
190 /**
191 * Sink for patterns for relative dates and times. For example,
192 * fields/relative/...
193 */
194
195 // Generic unit enum for storing Unit info.
196 typedef enum RelAbsUnit {
197 INVALID_UNIT = -1,
198 SECOND,
199 MINUTE,
200 HOUR,
201 DAY,
202 WEEK,
203 MONTH,
204 QUARTER,
205 YEAR,
206 SUNDAY,
207 MONDAY,
208 TUESDAY,
209 WEDNESDAY,
210 THURSDAY,
211 FRIDAY,
212 SATURDAY
213 } RelAbsUnit;
214
215 static int32_t relUnitFromGeneric(RelAbsUnit genUnit) {
216 // Converts the generic units to UDAT_RELATIVE version.
217 switch (genUnit) {
218 case SECOND:
219 return UDAT_RELATIVE_SECONDS;
220 case MINUTE:
221 return UDAT_RELATIVE_MINUTES;
222 case HOUR:
223 return UDAT_RELATIVE_HOURS;
224 case DAY:
225 return UDAT_RELATIVE_DAYS;
226 case WEEK:
227 return UDAT_RELATIVE_WEEKS;
228 case MONTH:
229 return UDAT_RELATIVE_MONTHS;
230 /*
231 * case QUARTER:
232 * return UDATE_RELATIVE_QUARTERS;
233 */
234 case YEAR:
235 return UDAT_RELATIVE_YEARS;
236 default:
237 return -1;
238 }
57a6839d 239 }
2ca993e8
A
240
241 static int32_t absUnitFromGeneric(RelAbsUnit genUnit) {
242 // Converts the generic units to UDAT_RELATIVE version.
243 switch (genUnit) {
244 case DAY:
245 return UDAT_ABSOLUTE_DAY;
246 case WEEK:
247 return UDAT_ABSOLUTE_WEEK;
248 case MONTH:
249 return UDAT_ABSOLUTE_MONTH;
250 /* TODO: Add in QUARTER
251 * case QUARTER:
252 * return UDAT_ABSOLUTE_QUARTER;
253 */
254 case YEAR:
255 return UDAT_ABSOLUTE_YEAR;
256 case SUNDAY:
257 return UDAT_ABSOLUTE_SUNDAY;
258 case MONDAY:
259 return UDAT_ABSOLUTE_MONDAY;
260 case TUESDAY:
261 return UDAT_ABSOLUTE_TUESDAY;
262 case WEDNESDAY:
263 return UDAT_ABSOLUTE_WEDNESDAY;
264 case THURSDAY:
265 return UDAT_ABSOLUTE_THURSDAY;
266 case FRIDAY:
267 return UDAT_ABSOLUTE_FRIDAY;
268 case SATURDAY:
269 return UDAT_ABSOLUTE_SATURDAY;
270 default:
271 return -1;
57a6839d 272 }
2ca993e8
A
273 }
274
275 static int32_t keyToDirection(const char* key) {
276 if (uprv_strcmp(key, "-2") == 0) {
277 return UDAT_DIRECTION_LAST_2;
57a6839d 278 }
2ca993e8
A
279 if (uprv_strcmp(key, "-1") == 0) {
280 return UDAT_DIRECTION_LAST;
281 }
282 if (uprv_strcmp(key, "0") == 0) {
283 return UDAT_DIRECTION_THIS;
57a6839d 284 }
2ca993e8
A
285 if (uprv_strcmp(key, "1") == 0) {
286 return UDAT_DIRECTION_NEXT;
287 }
288 if (uprv_strcmp(key, "2") == 0) {
289 return UDAT_DIRECTION_NEXT_2;
290 }
291 return -1;
57a6839d 292 }
57a6839d 293
2ca993e8 294 // Sinks for additional levels under /fields/*/relative/ and /fields/*/relativeTime/
57a6839d 295
2ca993e8
A
296 /**
297 * Make list of simplePatternFmtList, for past and for future.
298 * Set a SimpleFormatter for the <style, relative unit, plurality>
299 *
300 * Fill in values for the particular plural given, e.g., ONE, FEW, OTHER, etc.
301 */
302 struct RelDateTimeDetailSink : public ResourceTableSink {
303 RelDateTimeDetailSink(RelDateTimeFmtDataSink &sink) : outer(sink) {}
304 ~RelDateTimeDetailSink();
57a6839d 305
2ca993e8
A
306 virtual void put(const char *key, const ResourceValue &value,
307 UErrorCode &errorCode) {
308 if (U_FAILURE(errorCode)) { return; }
309
310 outer.relUnitIndex = relUnitFromGeneric(outer.genericUnit);
311 if (outer.relUnitIndex < 0) {
312 return;
313 }
314
315 /* Make two lists of simplePatternFmtList, one for past and one for future.
316 * Set a SimpleFormatter pattern for the <style, relative unit, plurality>
317 *
318 * Fill in values for the particular plural given, e.g., ONE, FEW, OTHER, etc.
319 */
320 int32_t pluralIndex = StandardPlural::indexOrNegativeFromString(key);
321 if (pluralIndex >= 0) {
322 SimpleFormatter **patterns =
323 outer.outputData.relativeUnitsFormatters[outer.style][outer.relUnitIndex]
324 [outer.pastFutureIndex];
325 // Only set if not already established.
326 if (patterns[pluralIndex] == NULL) {
327 patterns[pluralIndex] = new SimpleFormatter(
328 value.getUnicodeString(errorCode), 0, 1, errorCode);
329 if (patterns[pluralIndex] == NULL) {
330 errorCode = U_MEMORY_ALLOCATION_ERROR;
331 }
332 }
333 }
334 }
335
336 RelDateTimeFmtDataSink &outer;
337 } relDateTimeDetailSink;
338
339 /*
340 * Handles "relativeTime" entries, e.g., under "day", "hour", "minute",
341 * "minute-short", etc.
342 */
343 struct RelativeTimeSink : public ResourceTableSink {
344 RelativeTimeSink(RelDateTimeFmtDataSink &sink) : outer(sink) {}
345 ~RelativeTimeSink();
346
347 virtual ResourceTableSink *getOrCreateTableSink(
348 const char *key, int32_t /* initialSize */, UErrorCode& errorCode) {
349 if (U_FAILURE(errorCode)) { return NULL; }
350 outer.relUnitIndex = relUnitFromGeneric(outer.genericUnit);
351 if (outer.relUnitIndex < 0) {
352 return NULL;
353 }
354
355 if (uprv_strcmp(key, "past") == 0) {
356 outer.pastFutureIndex = 0;
357 } else if (uprv_strcmp(key, "future") == 0) {
358 outer.pastFutureIndex = 1;
359 } else {
360 // Unknown key.
361 return NULL;
362 }
363 return &outer.relDateTimeDetailSink;
364 }
365
366 RelDateTimeFmtDataSink &outer;
367 } relativeTimeSink;
368
369 /*
370 * Handles "relative" entries, e.g., under "day", "day-short", "fri",
371 * "fri-narrow", "fri-short", etc.
372 */
373 struct RelativeSink : public ResourceTableSink {
374 RelativeSink(RelDateTimeFmtDataSink &sink) : outer(sink) {}
375 ~RelativeSink();
376
377 virtual void put(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
378 if (U_FAILURE(errorCode)) { return; }
379 int32_t direction = keyToDirection(key);
380 if (direction < 0) {
381 return;
382 }
383
384 int32_t relUnitIndex = relUnitFromGeneric(outer.genericUnit);
385 if (relUnitIndex == UDAT_RELATIVE_SECONDS &&
386 direction == UDAT_DIRECTION_THIS &&
387 outer.outputData.absoluteUnits[outer.style][UDAT_ABSOLUTE_NOW]
388 [UDAT_DIRECTION_PLAIN].isEmpty()) {
389 // Handle "NOW"
390 outer.outputData.absoluteUnits[outer.style][UDAT_ABSOLUTE_NOW]
391 [UDAT_DIRECTION_PLAIN].fastCopyFrom(value.getUnicodeString(errorCode));
392 }
393
394 int32_t absUnitIndex = absUnitFromGeneric(outer.genericUnit);
395 if (absUnitIndex < 0) {
396 return;
397 }
398 // Only reset if slot is empty.
399 if (outer.outputData.absoluteUnits[outer.style][absUnitIndex][direction].isEmpty()) {
400 outer.outputData.absoluteUnits[outer.style][absUnitIndex]
401 [direction].fastCopyFrom(value.getUnicodeString(errorCode));
402 }
403 }
404
405 RelDateTimeFmtDataSink &outer;
406 } relativeSink;
407
408 /*
409 * Handles entries under "fields", recognizing "relative" and "relativeTime" entries.
410 */
411 struct UnitSink : public ResourceTableSink {
412 UnitSink(RelDateTimeFmtDataSink &sink) : outer(sink) {}
413 ~UnitSink();
414
415 virtual void put(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
416 if (U_FAILURE(errorCode)) { return; }
417 if (uprv_strcmp(key, "dn") != 0) {
418 return;
419 }
420
421 // Handle Display Name for PLAIN direction for some units.
422 int32_t absUnit = absUnitFromGeneric(outer.genericUnit);
423 if (absUnit < 0) {
424 return; // Not interesting.
425 }
426
427 // TODO(Travis Keep): This is a hack to get around CLDR bug 6818.
428 UnicodeString displayName = value.getUnicodeString(errorCode);
429 if (U_SUCCESS(errorCode)) {
430 if (uprv_strcmp("en", outer.sinkLocaleId) == 0) {
431 displayName.toLower();
432 }
433 }
434 // end hack
435
436 // Store displayname if not set.
437 if (outer.outputData.absoluteUnits[outer.style]
438 [absUnit][UDAT_DIRECTION_PLAIN].isEmpty()) {
439 outer.outputData.absoluteUnits[outer.style]
440 [absUnit][UDAT_DIRECTION_PLAIN].fastCopyFrom(displayName);
441 return;
442 }
443 }
444
445 virtual ResourceTableSink *getOrCreateTableSink(
446 const char *key, int32_t /* initialSize */, UErrorCode &errorCode) {
447 if (U_FAILURE(errorCode)) { return NULL; }
448 if (uprv_strcmp(key, "relative") == 0) {
449 return &outer.relativeSink;
450 } else if (uprv_strcmp(key, "relativeTime") == 0) {
451 return &outer.relativeTimeSink;
452 }
453 return NULL;
454 }
455
456 RelDateTimeFmtDataSink &outer;
457 } unitSink;
458
459 // For hack for locale "en".
57a6839d 460 // TODO(Travis Keep): This is a hack to get around CLDR bug 6818.
2ca993e8 461 const char* sinkLocaleId;
57a6839d 462
2ca993e8
A
463 // Values kept between levels of parsing the CLDR data.
464 int32_t pastFutureIndex; // 0 == past or 1 == future
465 UDateRelativeDateTimeFormatterStyle style; // {LONG, SHORT, NARROW}
466 RelAbsUnit genericUnit;
467 int32_t relUnitIndex;
468 int32_t absUnitIndex;
469
470 RelativeDateTimeCacheData &outputData;
471
472 // Constructor
473 RelDateTimeFmtDataSink(RelativeDateTimeCacheData& cacheData, const char* localeId)
474 : relDateTimeDetailSink(*this), relativeTimeSink(*this), relativeSink(*this),
475 unitSink(*this), sinkLocaleId(localeId), outputData(cacheData) {
476 // Clear cacheData.fallBackCache
477 cacheData.fallBackCache[UDAT_STYLE_LONG] = -1;
478 cacheData.fallBackCache[UDAT_STYLE_SHORT] = -1;
479 cacheData.fallBackCache[UDAT_STYLE_NARROW] = -1;
57a6839d 480 }
2ca993e8
A
481
482 ~RelDateTimeFmtDataSink();
483
484 // Utility functions
485 static UDateRelativeDateTimeFormatterStyle styleFromString(const char *s) {
486 int32_t len = uprv_strlen(s);
487 if (len >= 7 && uprv_strcmp(s + len - 7, "-narrow") == 0) {
488 return UDAT_STYLE_NARROW;
57a6839d 489 }
2ca993e8
A
490 if (len >= 6 && uprv_strcmp(s + len - 6, "-short") == 0) {
491 return UDAT_STYLE_SHORT;
492 }
493 return UDAT_STYLE_LONG;
57a6839d 494 }
57a6839d 495
2ca993e8
A
496 static int32_t styleSuffixLength(UDateRelativeDateTimeFormatterStyle style) {
497 switch (style) {
498 case UDAT_STYLE_NARROW:
499 return 7;
500 case UDAT_STYLE_SHORT:
501 return 6;
502 default:
503 return 0;
504 }
57a6839d 505 }
57a6839d 506
2ca993e8
A
507 // Utility functions
508 static UDateRelativeDateTimeFormatterStyle styleFromAliasUnicodeString(UnicodeString s) {
509 static const UChar narrow[7] = {0x002D, 0x006E, 0x0061, 0x0072, 0x0072, 0x006F, 0x0077};
510 static const UChar sshort[6] = {0x002D, 0x0073, 0x0068, 0x006F, 0x0072, 0x0074,};
511 if (s.endsWith(narrow, 7)) {
512 return UDAT_STYLE_NARROW;
513 }
514 if (s.endsWith(sshort, 6)) {
515 return UDAT_STYLE_SHORT;
516 }
517 return UDAT_STYLE_LONG;
b331163b 518 }
2ca993e8
A
519
520 static RelAbsUnit unitOrNegativeFromString(const char* keyword, int32_t length) {
521 // Quick check from string to enum.
522 switch (length) {
523 case 3:
524 if (uprv_strncmp(keyword, "day", length) == 0) {
525 return DAY;
526 } else if (uprv_strncmp(keyword, "sun", length) == 0) {
527 return SUNDAY;
528 } else if (uprv_strncmp(keyword, "mon", length) == 0) {
529 return MONDAY;
530 } else if (uprv_strncmp(keyword, "tue", length) == 0) {
531 return TUESDAY;
532 } else if (uprv_strncmp(keyword, "wed", length) == 0) {
533 return WEDNESDAY;
534 } else if (uprv_strncmp(keyword, "thu", length) == 0) {
535 return THURSDAY;
536 } else if (uprv_strncmp(keyword, "fri", length) == 0) {
537 return FRIDAY;
538 } else if (uprv_strncmp(keyword, "sat", length) == 0) {
539 return SATURDAY;
540 }
541 break;
542 case 4:
543 if (uprv_strncmp(keyword, "hour", length) == 0) {
544 return HOUR;
545 } else if (uprv_strncmp(keyword, "week", length) == 0) {
546 return WEEK;
547 } else if (uprv_strncmp(keyword, "year", length) == 0) {
548 return YEAR;
549 }
550 break;
551 case 5:
552 if (uprv_strncmp(keyword, "month", length) == 0) {
553 return MONTH;
554 }
555 break;
556 case 6:
557 if (uprv_strncmp(keyword, "minute", length) == 0) {
558 return MINUTE;
559 } else if (uprv_strncmp(keyword, "second", length) == 0) {
560 return SECOND;
561 }
562 break;
563 case 7:
564 if (uprv_strncmp(keyword, "quarter", length) == 0) {
565 return QUARTER; // TODO: Check @provisional
566 }
567 break;
568 default:
569 break;
570 }
571 return INVALID_UNIT;
b331163b 572 }
b331163b 573
2ca993e8
A
574 // Member functions of top level sink.
575 virtual void put(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
576 // Only handle aliases, storing information about alias fallback.
577
578 if (U_SUCCESS(errorCode)) {
579 if (value.getType() != URES_ALIAS) {
580 return;
581 }
582 const UnicodeString valueStr = value.getAliasUnicodeString(errorCode);
583 if (U_SUCCESS(errorCode)) {
584 UDateRelativeDateTimeFormatterStyle sourceStyle= styleFromString(key);
585 UDateRelativeDateTimeFormatterStyle targetStyle =
586 styleFromAliasUnicodeString(valueStr);
587
588 if (sourceStyle == targetStyle) {
589 errorCode = U_INVALID_FORMAT_ERROR;
590 return;
591 }
592 if (outputData.fallBackCache[sourceStyle] != -1 &&
593 outputData.fallBackCache[sourceStyle] != targetStyle) {
594 errorCode = U_INVALID_FORMAT_ERROR;
595 return;
596 }
597 outputData.fallBackCache[sourceStyle] = targetStyle;
598 }
599 }
b331163b
A
600 return;
601 }
b331163b 602
2ca993e8
A
603 // Top level sink
604 virtual ResourceTableSink *getOrCreateTableSink(
605 const char *key, int32_t /* initialSize */, UErrorCode& /* errorCode */) {
606 style= styleFromString(key);
607 int32_t unitSize = uprv_strlen(key) - styleSuffixLength(style);
608 genericUnit = unitOrNegativeFromString(key, unitSize);
609 if (style < 0 || genericUnit == INVALID_UNIT) {
610 return NULL;
611 }
612 return &unitSink;
b331163b 613 }
2ca993e8
A
614};
615
616// Virtual destructors must be defined out of line.
617RelDateTimeFmtDataSink::RelDateTimeDetailSink::~RelDateTimeDetailSink() {}
618RelDateTimeFmtDataSink::RelativeTimeSink::~RelativeTimeSink() {}
619RelDateTimeFmtDataSink::RelativeSink::~RelativeSink() {}
620RelDateTimeFmtDataSink::UnitSink::~UnitSink() {}
621RelDateTimeFmtDataSink::~RelDateTimeFmtDataSink() {}
622
623} // namespace
624
625DateFormatSymbols::DtWidthType styleToDateFormatSymbolWidth[UDAT_STYLE_COUNT] = {
626 DateFormatSymbols::WIDE, DateFormatSymbols::SHORT, DateFormatSymbols::NARROW
627};
628
629// Get days of weeks from the DateFormatSymbols class.
630static void loadWeekdayNames(UnicodeString absoluteUnits[UDAT_STYLE_COUNT]
631 [UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT],
632 const char* localeId,
633 UErrorCode& status) {
634 Locale locale(localeId);
635 DateFormatSymbols dfSym(locale, status);
636 for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
637 DateFormatSymbols::DtWidthType dtfmtWidth = styleToDateFormatSymbolWidth[style];
638 int32_t count;
639 const UnicodeString* weekdayNames =
640 dfSym.getWeekdays(count, DateFormatSymbols::STANDALONE, dtfmtWidth);
641 for (int32_t dayIndex = UDAT_ABSOLUTE_SUNDAY;
642 dayIndex <= UDAT_ABSOLUTE_SATURDAY; ++ dayIndex) {
643 int32_t dateSymbolIndex = (dayIndex - UDAT_ABSOLUTE_SUNDAY) + UCAL_SUNDAY;
644 absoluteUnits[style][dayIndex][UDAT_DIRECTION_PLAIN].fastCopyFrom(
645 weekdayNames[dateSymbolIndex]);
646 }
b331163b
A
647 }
648}
649
650static UBool loadUnitData(
651 const UResourceBundle *resource,
652 RelativeDateTimeCacheData &cacheData,
2ca993e8 653 const char* localeId,
b331163b 654 UErrorCode &status) {
2ca993e8
A
655 RelDateTimeFmtDataSink sink(cacheData, localeId);
656 ures_getAllTableItemsWithFallback(resource, "fields", sink, status);
657
658 // Get the weekday names from DateFormatSymbols.
659 loadWeekdayNames(cacheData.absoluteUnits, localeId, status);
57a6839d
A
660 return U_SUCCESS(status);
661}
662
663static UBool getDateTimePattern(
664 const UResourceBundle *resource,
665 UnicodeString &result,
666 UErrorCode &status) {
667 UnicodeString defaultCalendarName;
668 if (!getStringWithFallback(
669 resource,
670 "calendar/default",
671 defaultCalendarName,
672 status)) {
673 return FALSE;
674 }
675 CharString pathBuffer;
676 pathBuffer.append("calendar/", status)
677 .appendInvariantChars(defaultCalendarName, status)
678 .append("/DateTimePatterns", status);
679 LocalUResourceBundlePointer topLevel(
680 ures_getByKeyWithFallback(
681 resource, pathBuffer.data(), NULL, &status));
682 if (U_FAILURE(status)) {
683 return FALSE;
684 }
685 int32_t size = ures_getSize(topLevel.getAlias());
686 if (size <= 8) {
2ca993e8 687 // Oops, size is too small to access the index that we want, fallback
57a6839d
A
688 // to a hard-coded value.
689 result = UNICODE_STRING_SIMPLE("{1} {0}");
690 return TRUE;
691 }
692 return getStringByIndex(topLevel.getAlias(), 8, result, status);
693}
694
b331163b
A
695template<> U_I18N_API
696const RelativeDateTimeCacheData *LocaleCacheKey<RelativeDateTimeCacheData>::createObject(const void * /*unused*/, UErrorCode &status) const {
697 const char *localeId = fLoc.getName();
57a6839d
A
698 LocalUResourceBundlePointer topLevel(ures_open(NULL, localeId, &status));
699 if (U_FAILURE(status)) {
700 return NULL;
701 }
702 LocalPointer<RelativeDateTimeCacheData> result(
703 new RelativeDateTimeCacheData());
704 if (result.isNull()) {
705 status = U_MEMORY_ALLOCATION_ERROR;
706 return NULL;
707 }
708 if (!loadUnitData(
709 topLevel.getAlias(),
710 *result,
2ca993e8 711 localeId,
57a6839d
A
712 status)) {
713 return NULL;
714 }
715 UnicodeString dateTimePattern;
716 if (!getDateTimePattern(topLevel.getAlias(), dateTimePattern, status)) {
717 return NULL;
718 }
719 result->adoptCombinedDateAndTime(
2ca993e8 720 new SimpleFormatter(dateTimePattern, 2, 2, status));
57a6839d
A
721 if (U_FAILURE(status)) {
722 return NULL;
723 }
b331163b 724 result->addRef();
57a6839d
A
725 return result.orphan();
726}
727
b331163b
A
728RelativeDateTimeFormatter::RelativeDateTimeFormatter(UErrorCode& status) :
729 fCache(NULL),
730 fNumberFormat(NULL),
731 fPluralRules(NULL),
732 fStyle(UDAT_STYLE_LONG),
733 fContext(UDISPCTX_CAPITALIZATION_NONE),
734 fOptBreakIterator(NULL) {
735 init(NULL, NULL, status);
57a6839d
A
736}
737
b331163b
A
738RelativeDateTimeFormatter::RelativeDateTimeFormatter(
739 const Locale& locale, UErrorCode& status) :
740 fCache(NULL),
741 fNumberFormat(NULL),
742 fPluralRules(NULL),
743 fStyle(UDAT_STYLE_LONG),
744 fContext(UDISPCTX_CAPITALIZATION_NONE),
745 fOptBreakIterator(NULL),
746 fLocale(locale) {
747 init(NULL, NULL, status);
57a6839d
A
748}
749
750RelativeDateTimeFormatter::RelativeDateTimeFormatter(
b331163b
A
751 const Locale& locale, NumberFormat *nfToAdopt, UErrorCode& status) :
752 fCache(NULL),
753 fNumberFormat(NULL),
754 fPluralRules(NULL),
755 fStyle(UDAT_STYLE_LONG),
756 fContext(UDISPCTX_CAPITALIZATION_NONE),
757 fOptBreakIterator(NULL),
758 fLocale(locale) {
759 init(nfToAdopt, NULL, status);
57a6839d
A
760}
761
762RelativeDateTimeFormatter::RelativeDateTimeFormatter(
b331163b
A
763 const Locale& locale,
764 NumberFormat *nfToAdopt,
765 UDateRelativeDateTimeFormatterStyle styl,
766 UDisplayContext capitalizationContext,
767 UErrorCode& status) :
768 fCache(NULL),
769 fNumberFormat(NULL),
770 fPluralRules(NULL),
771 fStyle(styl),
772 fContext(capitalizationContext),
773 fOptBreakIterator(NULL),
774 fLocale(locale) {
775 if (U_FAILURE(status)) {
776 return;
777 }
778 if ((capitalizationContext >> 8) != UDISPCTX_TYPE_CAPITALIZATION) {
779 status = U_ILLEGAL_ARGUMENT_ERROR;
780 return;
781 }
782 if (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE) {
783 BreakIterator *bi = BreakIterator::createSentenceInstance(locale, status);
784 if (U_FAILURE(status)) {
785 return;
786 }
787 init(nfToAdopt, bi, status);
788 } else {
789 init(nfToAdopt, NULL, status);
790 }
57a6839d
A
791}
792
793RelativeDateTimeFormatter::RelativeDateTimeFormatter(
794 const RelativeDateTimeFormatter& other)
b331163b
A
795 : UObject(other),
796 fCache(other.fCache),
797 fNumberFormat(other.fNumberFormat),
798 fPluralRules(other.fPluralRules),
799 fStyle(other.fStyle),
800 fContext(other.fContext),
801 fOptBreakIterator(other.fOptBreakIterator),
802 fLocale(other.fLocale) {
803 fCache->addRef();
804 fNumberFormat->addRef();
805 fPluralRules->addRef();
806 if (fOptBreakIterator != NULL) {
807 fOptBreakIterator->addRef();
808 }
57a6839d
A
809}
810
811RelativeDateTimeFormatter& RelativeDateTimeFormatter::operator=(
812 const RelativeDateTimeFormatter& other) {
813 if (this != &other) {
b331163b
A
814 SharedObject::copyPtr(other.fCache, fCache);
815 SharedObject::copyPtr(other.fNumberFormat, fNumberFormat);
816 SharedObject::copyPtr(other.fPluralRules, fPluralRules);
817 SharedObject::copyPtr(other.fOptBreakIterator, fOptBreakIterator);
818 fStyle = other.fStyle;
819 fContext = other.fContext;
820 fLocale = other.fLocale;
57a6839d
A
821 }
822 return *this;
823}
824
825RelativeDateTimeFormatter::~RelativeDateTimeFormatter() {
b331163b
A
826 if (fCache != NULL) {
827 fCache->removeRef();
828 }
829 if (fNumberFormat != NULL) {
830 fNumberFormat->removeRef();
57a6839d 831 }
b331163b
A
832 if (fPluralRules != NULL) {
833 fPluralRules->removeRef();
57a6839d 834 }
b331163b
A
835 if (fOptBreakIterator != NULL) {
836 fOptBreakIterator->removeRef();
57a6839d
A
837 }
838}
839
840const NumberFormat& RelativeDateTimeFormatter::getNumberFormat() const {
b331163b
A
841 return **fNumberFormat;
842}
843
844UDisplayContext RelativeDateTimeFormatter::getCapitalizationContext() const {
845 return fContext;
846}
847
848UDateRelativeDateTimeFormatterStyle RelativeDateTimeFormatter::getFormatStyle() const {
849 return fStyle;
57a6839d
A
850}
851
852UnicodeString& RelativeDateTimeFormatter::format(
853 double quantity, UDateDirection direction, UDateRelativeUnit unit,
854 UnicodeString& appendTo, UErrorCode& status) const {
855 if (U_FAILURE(status)) {
856 return appendTo;
857 }
858 if (direction != UDAT_DIRECTION_LAST && direction != UDAT_DIRECTION_NEXT) {
859 status = U_ILLEGAL_ARGUMENT_ERROR;
860 return appendTo;
861 }
862 int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0;
863 FieldPosition pos(FieldPosition::DONT_CARE);
2ca993e8 864
b331163b 865 UnicodeString result;
2ca993e8
A
866 UnicodeString formattedNumber;
867
868 StandardPlural::Form pluralIndex = QuantityFormatter::selectPlural(
869 quantity, **fNumberFormat, **fPluralRules, formattedNumber, pos,
870 status);
871
872 const SimpleFormatter* formatter =
873 fCache->getRelativeUnitFormatter(fStyle, unit, bFuture, pluralIndex);
874 if (formatter == NULL) {
875 // TODO: WARN - look at quantity formatter's action with an error.
876 status = U_INVALID_FORMAT_ERROR;
877 return appendTo;
878 }
879 formatter->format(formattedNumber, result, status);
b331163b
A
880 adjustForContext(result);
881 return appendTo.append(result);
57a6839d
A
882}
883
2ca993e8
A
884UnicodeString& RelativeDateTimeFormatter::formatNumeric(
885 double offset, URelativeDateTimeUnit unit,
886 UnicodeString& appendTo, UErrorCode& status) const {
887 if (U_FAILURE(status)) {
888 return appendTo;
889 }
890 // TODO:
891 // The full implementation of this depends on CLDR data that is not yet available,
892 // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data.
893 // In the meantime do a quick bring-up by calling the old format method; this
894 // leaves some holes (even for data that is currently available, such as quarter).
895 // When the new CLDR data is available, update the data storage accordingly,
896 // rewrite this to use it directly, and rewrite the old format method to call this
897 // new one; that is covered by http://bugs.icu-project.org/trac/ticket/12171.
898 UDateRelativeUnit relunit = UDAT_RELATIVE_UNIT_COUNT;
899 switch (unit) {
900 case UDAT_REL_UNIT_YEAR: relunit = UDAT_RELATIVE_YEARS; break;
901 case UDAT_REL_UNIT_MONTH: relunit = UDAT_RELATIVE_MONTHS; break;
902 case UDAT_REL_UNIT_WEEK: relunit = UDAT_RELATIVE_WEEKS; break;
903 case UDAT_REL_UNIT_DAY: relunit = UDAT_RELATIVE_DAYS; break;
904 case UDAT_REL_UNIT_HOUR: relunit = UDAT_RELATIVE_HOURS; break;
905 case UDAT_REL_UNIT_MINUTE: relunit = UDAT_RELATIVE_MINUTES; break;
906 case UDAT_REL_UNIT_SECOND: relunit = UDAT_RELATIVE_SECONDS; break;
907 default: // a unit that the above method does not handle
908 status = U_UNSUPPORTED_ERROR;
909 return appendTo;
910 }
911 UDateDirection direction = UDAT_DIRECTION_NEXT;
912 if (offset < 0) {
913 direction = UDAT_DIRECTION_LAST;
914 offset = -offset;
915 }
916 return format(offset, direction, relunit, appendTo, status);
917}
918
57a6839d
A
919UnicodeString& RelativeDateTimeFormatter::format(
920 UDateDirection direction, UDateAbsoluteUnit unit,
921 UnicodeString& appendTo, UErrorCode& status) const {
922 if (U_FAILURE(status)) {
923 return appendTo;
924 }
925 if (unit == UDAT_ABSOLUTE_NOW && direction != UDAT_DIRECTION_PLAIN) {
926 status = U_ILLEGAL_ARGUMENT_ERROR;
927 return appendTo;
928 }
2ca993e8
A
929
930 // Get string using fallback.
931 UnicodeString result;
932 result.fastCopyFrom(fCache->getAbsoluteUnitString(fStyle, unit, direction));
933 if (fOptBreakIterator != NULL) {
934 adjustForContext(result);
b331163b 935 }
b331163b 936 return appendTo.append(result);
57a6839d
A
937}
938
2ca993e8
A
939UnicodeString& RelativeDateTimeFormatter::format(
940 double offset, URelativeDateTimeUnit unit,
941 UnicodeString& appendTo, UErrorCode& status) const {
942 if (U_FAILURE(status)) {
943 return appendTo;
944 }
945 // TODO:
946 // The full implementation of this depends on CLDR data that is not yet available,
947 // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data.
948 // In the meantime do a quick bring-up by calling the old format method; this
949 // leaves some holes (even for data that is currently available, such as quarter).
950 // When the new CLDR data is available, update the data storage accordingly,
951 // rewrite this to use it directly, and rewrite the old format method to call this
952 // new one; that is covered by http://bugs.icu-project.org/trac/ticket/12171.
953 UDateDirection direction = UDAT_DIRECTION_COUNT;
954 if (offset > -2.1 && offset < 2.1) {
955 // Allow a 1% epsilon, so offsets in -1.01..-0.99 map to LAST
956 double offsetx100 = offset * 100.0;
957 int32_t intoffset = (offsetx100 < 0)? (int32_t)(offsetx100-0.5) : (int32_t)(offsetx100+0.5);
958 switch (intoffset) {
959 case -200/*-2*/: direction = UDAT_DIRECTION_LAST_2; break;
960 case -100/*-1*/: direction = UDAT_DIRECTION_LAST; break;
961 case 0/* 0*/: direction = UDAT_DIRECTION_THIS; break;
962 case 100/* 1*/: direction = UDAT_DIRECTION_NEXT; break;
963 case 200/* 2*/: direction = UDAT_DIRECTION_NEXT_2; break;
964 default: break;
965 }
966 }
967 UDateAbsoluteUnit absunit = UDAT_ABSOLUTE_UNIT_COUNT;
968 switch (unit) {
969 case UDAT_REL_UNIT_YEAR: absunit = UDAT_ABSOLUTE_YEAR; break;
970 case UDAT_REL_UNIT_MONTH: absunit = UDAT_ABSOLUTE_MONTH; break;
971 case UDAT_REL_UNIT_WEEK: absunit = UDAT_ABSOLUTE_WEEK; break;
972 case UDAT_REL_UNIT_DAY: absunit = UDAT_ABSOLUTE_DAY; break;
973 case UDAT_REL_UNIT_SECOND:
974 if (direction == UDAT_DIRECTION_THIS) {
975 absunit = UDAT_ABSOLUTE_NOW;
976 direction = UDAT_DIRECTION_PLAIN;
977 }
978 break;
979 case UDAT_REL_UNIT_SUNDAY: absunit = UDAT_ABSOLUTE_SUNDAY; break;
980 case UDAT_REL_UNIT_MONDAY: absunit = UDAT_ABSOLUTE_MONDAY; break;
981 case UDAT_REL_UNIT_TUESDAY: absunit = UDAT_ABSOLUTE_TUESDAY; break;
982 case UDAT_REL_UNIT_WEDNESDAY: absunit = UDAT_ABSOLUTE_WEDNESDAY; break;
983 case UDAT_REL_UNIT_THURSDAY: absunit = UDAT_ABSOLUTE_THURSDAY; break;
984 case UDAT_REL_UNIT_FRIDAY: absunit = UDAT_ABSOLUTE_FRIDAY; break;
985 case UDAT_REL_UNIT_SATURDAY: absunit = UDAT_ABSOLUTE_SATURDAY; break;
986 default: break;
987 }
988 if (direction != UDAT_DIRECTION_COUNT && absunit != UDAT_ABSOLUTE_UNIT_COUNT) {
989 const UnicodeString &unitFormatString =
990 fCache->getAbsoluteUnitString(fStyle, absunit, direction);
991 if (!unitFormatString.isEmpty()) {
992 if (fOptBreakIterator != NULL) {
993 UnicodeString result(unitFormatString);
994 adjustForContext(result);
995 return appendTo.append(result);
996 } else {
997 return appendTo.append(unitFormatString);
998 }
999 }
1000 }
1001 // otherwise fallback to formatNumeric
1002 return formatNumeric(offset, unit, appendTo, status);
1003}
1004
57a6839d 1005UnicodeString& RelativeDateTimeFormatter::combineDateAndTime(
2ca993e8
A
1006 const UnicodeString& relativeDateString, const UnicodeString& timeString,
1007 UnicodeString& appendTo, UErrorCode& status) const {
b331163b 1008 return fCache->getCombinedDateAndTime()->format(
2ca993e8 1009 timeString, relativeDateString, appendTo, status);
57a6839d
A
1010}
1011
b331163b
A
1012void RelativeDateTimeFormatter::adjustForContext(UnicodeString &str) const {
1013 if (fOptBreakIterator == NULL
1014 || str.length() == 0 || !u_islower(str.char32At(0))) {
1015 return;
1016 }
1017
1018 // Must guarantee that one thread at a time accesses the shared break
1019 // iterator.
1020 Mutex lock(&gBrkIterMutex);
1021 str.toTitle(
1022 fOptBreakIterator->get(),
1023 fLocale,
1024 U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
1025}
1026
57a6839d 1027void RelativeDateTimeFormatter::init(
b331163b
A
1028 NumberFormat *nfToAdopt,
1029 BreakIterator *biToAdopt,
1030 UErrorCode &status) {
57a6839d 1031 LocalPointer<NumberFormat> nf(nfToAdopt);
b331163b
A
1032 LocalPointer<BreakIterator> bi(biToAdopt);
1033 UnifiedCache::getByLocale(fLocale, fCache, status);
1034 if (U_FAILURE(status)) {
57a6839d
A
1035 return;
1036 }
b331163b
A
1037 const SharedPluralRules *pr = PluralRules::createSharedInstance(
1038 fLocale, UPLURAL_TYPE_CARDINAL, status);
57a6839d
A
1039 if (U_FAILURE(status)) {
1040 return;
1041 }
b331163b
A
1042 SharedObject::copyPtr(pr, fPluralRules);
1043 pr->removeRef();
57a6839d 1044 if (nf.isNull()) {
b331163b
A
1045 const SharedNumberFormat *shared = NumberFormat::createSharedInstance(
1046 fLocale, UNUM_DECIMAL, status);
57a6839d
A
1047 if (U_FAILURE(status)) {
1048 return;
1049 }
b331163b
A
1050 SharedObject::copyPtr(shared, fNumberFormat);
1051 shared->removeRef();
57a6839d
A
1052 } else {
1053 SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias());
1054 if (shared == NULL) {
1055 status = U_MEMORY_ALLOCATION_ERROR;
1056 return;
1057 }
1058 nf.orphan();
b331163b
A
1059 SharedObject::copyPtr(shared, fNumberFormat);
1060 }
1061 if (bi.isNull()) {
1062 SharedObject::clearPtr(fOptBreakIterator);
1063 } else {
1064 SharedBreakIterator *shared = new SharedBreakIterator(bi.getAlias());
1065 if (shared == NULL) {
1066 status = U_MEMORY_ALLOCATION_ERROR;
1067 return;
1068 }
1069 bi.orphan();
1070 SharedObject::copyPtr(shared, fOptBreakIterator);
57a6839d
A
1071 }
1072}
1073
57a6839d
A
1074U_NAMESPACE_END
1075
2ca993e8
A
1076// Plain C API
1077
1078U_NAMESPACE_USE
1079
1080U_CAPI URelativeDateTimeFormatter* U_EXPORT2
1081ureldatefmt_open( const char* locale,
1082 UNumberFormat* nfToAdopt,
1083 UDateRelativeDateTimeFormatterStyle width,
1084 UDisplayContext capitalizationContext,
1085 UErrorCode* status )
1086{
1087 if (U_FAILURE(*status)) {
1088 return NULL;
1089 }
1090 LocalPointer<RelativeDateTimeFormatter> formatter(new RelativeDateTimeFormatter(Locale(locale),
1091 (NumberFormat*)nfToAdopt, width,
1092 capitalizationContext, *status), *status);
1093 if (U_FAILURE(*status)) {
1094 return NULL;
1095 }
1096 return (URelativeDateTimeFormatter*)formatter.orphan();
1097}
1098
1099U_CAPI void U_EXPORT2
1100ureldatefmt_close(URelativeDateTimeFormatter *reldatefmt)
1101{
1102 delete (RelativeDateTimeFormatter*)reldatefmt;
1103}
1104
1105U_CAPI int32_t U_EXPORT2
1106ureldatefmt_formatNumeric( const URelativeDateTimeFormatter* reldatefmt,
1107 double offset,
1108 URelativeDateTimeUnit unit,
1109 UChar* result,
1110 int32_t resultCapacity,
1111 UErrorCode* status)
1112{
1113 if (U_FAILURE(*status)) {
1114 return 0;
1115 }
1116 if (result == NULL ? resultCapacity != 0 : resultCapacity < 0) {
1117 *status = U_ILLEGAL_ARGUMENT_ERROR;
1118 return 0;
1119 }
1120 UnicodeString res;
1121 if (result != NULL) {
1122 // NULL destination for pure preflighting: empty dummy string
1123 // otherwise, alias the destination buffer (copied from udat_format)
1124 res.setTo(result, 0, resultCapacity);
1125 }
1126 ((RelativeDateTimeFormatter*)reldatefmt)->formatNumeric(offset, unit, res, *status);
1127 if (U_FAILURE(*status)) {
1128 return 0;
1129 }
1130 return res.extract(result, resultCapacity, *status);
1131}
57a6839d 1132
2ca993e8
A
1133U_CAPI int32_t U_EXPORT2
1134ureldatefmt_format( const URelativeDateTimeFormatter* reldatefmt,
1135 double offset,
1136 URelativeDateTimeUnit unit,
1137 UChar* result,
1138 int32_t resultCapacity,
1139 UErrorCode* status)
1140{
1141 if (U_FAILURE(*status)) {
1142 return 0;
1143 }
1144 if (result == NULL ? resultCapacity != 0 : resultCapacity < 0) {
1145 *status = U_ILLEGAL_ARGUMENT_ERROR;
1146 return 0;
1147 }
1148 UnicodeString res;
1149 if (result != NULL) {
1150 // NULL destination for pure preflighting: empty dummy string
1151 // otherwise, alias the destination buffer (copied from udat_format)
1152 res.setTo(result, 0, resultCapacity);
1153 }
1154 ((RelativeDateTimeFormatter*)reldatefmt)->format(offset, unit, res, *status);
1155 if (U_FAILURE(*status)) {
1156 return 0;
1157 }
1158 return res.extract(result, resultCapacity, *status);
1159}
1160
1161U_CAPI int32_t U_EXPORT2
1162ureldatefmt_combineDateAndTime( const URelativeDateTimeFormatter* reldatefmt,
1163 const UChar * relativeDateString,
1164 int32_t relativeDateStringLen,
1165 const UChar * timeString,
1166 int32_t timeStringLen,
1167 UChar* result,
1168 int32_t resultCapacity,
1169 UErrorCode* status )
1170{
1171 if (U_FAILURE(*status)) {
1172 return 0;
1173 }
1174 if (result == NULL ? resultCapacity != 0 : resultCapacity < 0 ||
1175 (relativeDateString == NULL ? relativeDateStringLen != 0 : relativeDateStringLen < -1) ||
1176 (timeString == NULL ? timeStringLen != 0 : timeStringLen < -1)) {
1177 *status = U_ILLEGAL_ARGUMENT_ERROR;
1178 return 0;
1179 }
1180 UnicodeString relDateStr((UBool)(relativeDateStringLen == -1), relativeDateString, relativeDateStringLen);
1181 UnicodeString timeStr((UBool)(timeStringLen == -1), timeString, timeStringLen);
1182 UnicodeString res(result, 0, resultCapacity);
1183 ((RelativeDateTimeFormatter*)reldatefmt)->combineDateAndTime(relDateStr, timeStr, res, *status);
1184 if (U_FAILURE(*status)) {
1185 return 0;
1186 }
1187 return res.extract(result, resultCapacity, *status);
1188}
1189
1190#endif /* !UCONFIG_NO_FORMATTING */