1e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell/* 2e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell******************************************************************************* 3efda5cb6263631175aa2efe46df9322b3c5775eeBrian* Copyright (C) 2007-2014, International Business Machines Corporation and 422144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughes* others. All Rights Reserved. 59ede048127ea71282fd97e01516dedcfb03e2a23Brian******************************************************************************* 622144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughes*/ 7e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell 8e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "unicode/utypes.h" 9e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell 10e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#if !UCONFIG_NO_FORMATTING 11e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell 12e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include <stdlib.h> 1322144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughes 14e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "reldtfmt.h" 15e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "unicode/datefmt.h" 1622144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughes#include "unicode/smpdtfmt.h" 17e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "unicode/msgfmt.h" 18e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "unicode/udisplaycontext.h" 19e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "unicode/uchar.h" 20e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "unicode/brkiter.h" 21e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell 22e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "gregoimp.h" // for CalendarData 23e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "cmemory.h" 24e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "uresimp.h" 25e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell 26bbd287103dad776d8a45c87c4e51fbc26d9b80d5Brian PaulU_NAMESPACE_BEGIN 27bbd287103dad776d8a45c87c4e51fbc26d9b80d5Brian Paul 28bbd287103dad776d8a45c87c4e51fbc26d9b80d5Brian Paul 29bbd287103dad776d8a45c87c4e51fbc26d9b80d5Brian Paul/** 300070d398d13759adc519f9bc764ffd39bc88890eBrian Paul * An array of URelativeString structs is used to store the resource data loaded out of the bundle. 31cd03ed4f54444d96e4e47cdb118a3dfd94d92bb0Keith Whitwell */ 32cd03ed4f54444d96e4e47cdb118a3dfd94d92bb0Keith Whitwellstruct URelativeString { 33b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul int32_t offset; /** offset of this item, such as, the relative date **/ 34b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul int32_t len; /** length of the string **/ 35e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell const UChar* string; /** string, or NULL if not set **/ 36e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell}; 37b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul 38b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paulstatic const char DT_DateTimePatternsTag[]="DateTimePatterns"; 39b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul 40b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul 41f9995b30756140724f41daf963fa06167912be7fKristian HøgsbergUOBJECT_DEFINE_RTTI_IMPLEMENTATION(RelativeDateFormat) 42b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul 43b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian PaulRelativeDateFormat::RelativeDateFormat(const RelativeDateFormat& other) : 44b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul DateFormat(other), fDateTimeFormatter(NULL), fDatePattern(other.fDatePattern), 45e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell fTimePattern(other.fTimePattern), fCombinedFormat(NULL), 46b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul fDateStyle(other.fDateStyle), fLocale(other.fLocale), 47b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul fDayMin(other.fDayMin), fDayMax(other.fDayMax), 48b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul fDatesLen(other.fDatesLen), fDates(NULL), 49b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul fCombinedHasDateAtStart(other.fCombinedHasDateAtStart), 50b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul fCapitalizationInfoSet(other.fCapitalizationInfoSet), 51b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul fCapitalizationOfRelativeUnitsForUIListMenu(other.fCapitalizationOfRelativeUnitsForUIListMenu), 52b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul fCapitalizationOfRelativeUnitsForStandAlone(other.fCapitalizationOfRelativeUnitsForStandAlone), 53b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul fCapitalizationBrkIter(NULL) 54b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul{ 55b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul if(other.fDateTimeFormatter != NULL) { 56b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul fDateTimeFormatter = (SimpleDateFormat*)other.fDateTimeFormatter->clone(); 57e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell } 58e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell if(other.fCombinedFormat != NULL) { 59e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell fCombinedFormat = (MessageFormat*)other.fCombinedFormat->clone(); 60b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul } 61e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell if (fDatesLen > 0) { 62b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*fDatesLen); 63f9995b30756140724f41daf963fa06167912be7fKristian Høgsberg uprv_memcpy(fDates, other.fDates, sizeof(fDates[0])*fDatesLen); 64e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell } 65af2aa8e9cf88a9ee3ec338eddc9a47bf2f142cb7Brian#if !UCONFIG_NO_BREAK_ITERATION 66af2aa8e9cf88a9ee3ec338eddc9a47bf2f142cb7Brian if (other.fCapitalizationBrkIter != NULL) { 67af2aa8e9cf88a9ee3ec338eddc9a47bf2f142cb7Brian fCapitalizationBrkIter = (other.fCapitalizationBrkIter)->clone(); 68af2aa8e9cf88a9ee3ec338eddc9a47bf2f142cb7Brian } 69cd03ed4f54444d96e4e47cdb118a3dfd94d92bb0Keith Whitwell#endif 70b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul} 71e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell 72b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian PaulRelativeDateFormat::RelativeDateFormat( UDateFormatStyle timeStyle, UDateFormatStyle dateStyle, 73b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul const Locale& locale, UErrorCode& status) : 74b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul DateFormat(), fDateTimeFormatter(NULL), fDatePattern(), fTimePattern(), fCombinedFormat(NULL), 75b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul fDateStyle(dateStyle), fLocale(locale), fDayMin(0), fDayMax(0), fDatesLen(0), fDates(NULL), 76b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul fCombinedHasDateAtStart(FALSE), fCapitalizationInfoSet(FALSE), 77b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul fCapitalizationOfRelativeUnitsForUIListMenu(FALSE), fCapitalizationOfRelativeUnitsForStandAlone(FALSE), 7877df88727cb0a423dd5cb41498c2302d9df4fce7Brian Paul fCapitalizationBrkIter(NULL) 79a670c1280b78e6da3b298b61f623e4c733c6be94Brian Paul{ 80a670c1280b78e6da3b298b61f623e4c733c6be94Brian Paul if(U_FAILURE(status) ) { 81b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul return; 82b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul } 83b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul 8477df88727cb0a423dd5cb41498c2302d9df4fce7Brian Paul if (timeStyle < UDAT_NONE || timeStyle > UDAT_SHORT) { 85b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul // don't support other time styles (e.g. relative styles), for now 86b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul status = U_ILLEGAL_ARGUMENT_ERROR; 87b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul return; 8877df88727cb0a423dd5cb41498c2302d9df4fce7Brian Paul } 89b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul UDateFormatStyle baseDateStyle = (dateStyle > UDAT_SHORT)? (UDateFormatStyle)(dateStyle & ~UDAT_RELATIVE): dateStyle; 90e6df80184b0c7e83eba382b3161ba11db497c55dIan Romanick DateFormat * df; 91b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul // Get fDateTimeFormatter from either date or time style (does not matter, we will override the pattern). 92b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul // We do need to get separate patterns for the date & time styles. 93b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul if (baseDateStyle != UDAT_NONE) { 9477df88727cb0a423dd5cb41498c2302d9df4fce7Brian Paul df = createDateInstance((EStyle)baseDateStyle, locale); 95a670c1280b78e6da3b298b61f623e4c733c6be94Brian Paul fDateTimeFormatter=dynamic_cast<SimpleDateFormat *>(df); 96a670c1280b78e6da3b298b61f623e4c733c6be94Brian Paul if (fDateTimeFormatter == NULL) { 97b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul status = U_UNSUPPORTED_ERROR; 98b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul return; 99b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul } 10077df88727cb0a423dd5cb41498c2302d9df4fce7Brian Paul fDateTimeFormatter->toPattern(fDatePattern); 101b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul if (timeStyle != UDAT_NONE) { 102b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul df = createTimeInstance((EStyle)timeStyle, locale); 103b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul SimpleDateFormat *sdf = dynamic_cast<SimpleDateFormat *>(df); 10477df88727cb0a423dd5cb41498c2302d9df4fce7Brian Paul if (sdf != NULL) { 105b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul sdf->toPattern(fTimePattern); 106e6df80184b0c7e83eba382b3161ba11db497c55dIan Romanick delete sdf; 107b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul } 108b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul } 109e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell } else { 110e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell // does not matter whether timeStyle is UDAT_NONE, we need something for fDateTimeFormatter 111e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell df = createTimeInstance((EStyle)timeStyle, locale); 112e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell fDateTimeFormatter=dynamic_cast<SimpleDateFormat *>(df); 113b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul if (fDateTimeFormatter == NULL) { 114b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul status = U_UNSUPPORTED_ERROR; 115b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul return; 116b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul } 11722a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul fDateTimeFormatter->toPattern(fTimePattern); 1189e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian } 11922a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul 12045bc887da226403f2c41077e40ca38b6f60f1359Brian Paul // Initialize the parent fCalendar, so that parse() works correctly. 121e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell initializeCalendar(NULL, locale, status); 122e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell loadDates(status); 123e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell} 12422a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul 1259e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrianRelativeDateFormat::~RelativeDateFormat() { 12622a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul delete fDateTimeFormatter; 12722a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul delete fCombinedFormat; 12822a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul uprv_free(fDates); 12922a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul#if !UCONFIG_NO_BREAK_ITERATION 13022a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul delete fCapitalizationBrkIter; 13122a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul#endif 13222a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul} 133af2aa8e9cf88a9ee3ec338eddc9a47bf2f142cb7Brian 134dc24230de7f913969b52dee3579bb8fa3d50a8c0Karl Schultz 13522a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian PaulFormat* RelativeDateFormat::clone(void) const { 13622a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul return new RelativeDateFormat(*this); 13745bc887da226403f2c41077e40ca38b6f60f1359Brian Paul} 138e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell 139e3a051e0538a605551f4d58294c94f5eb00ed07fKeith WhitwellUBool RelativeDateFormat::operator==(const Format& other) const { 140b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul if(DateFormat::operator==(other)) { 141e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell // The DateFormat::operator== check for fCapitalizationContext equality above 1429e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian // is sufficient to check equality of all derived context-related data. 1439e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian // DateFormat::operator== guarantees following cast is safe 14422a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul RelativeDateFormat* that = (RelativeDateFormat*)&other; 14522a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul return (fDateStyle==that->fDateStyle && 1469ede048127ea71282fd97e01516dedcfb03e2a23Brian fDatePattern==that->fDatePattern && 14722a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul fTimePattern==that->fTimePattern && 14822a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul fLocale==that->fLocale ); 14922a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul } 15022a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul return FALSE; 15122a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul} 152af2aa8e9cf88a9ee3ec338eddc9a47bf2f142cb7Brian 153dc24230de7f913969b52dee3579bb8fa3d50a8c0Karl Schultzstatic const UChar APOSTROPHE = (UChar)0x0027; 15422a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul 15522a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian PaulUnicodeString& RelativeDateFormat::format( Calendar& cal, 156a803b0c891404dcd7c376e91f6a033cd4e42abc3Brian Paul UnicodeString& appendTo, 157e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell FieldPosition& pos) const { 158e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell 159b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul UErrorCode status = U_ZERO_ERROR; 160e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell UnicodeString relativeDayString; 161e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell UDisplayContext capitalizationContext = getContext(UDISPCTX_TYPE_CAPITALIZATION, status); 16222a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul 163f9995b30756140724f41daf963fa06167912be7fKristian Høgsberg // calculate the difference, in days, between 'cal' and now. 1649e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian int dayDiff = dayDifference(cal, status); 16546b0988c673b28e072fd0cbf477632a9ab6f9f18Keith Whitwell 16646b0988c673b28e072fd0cbf477632a9ab6f9f18Keith Whitwell // look up string 16746b0988c673b28e072fd0cbf477632a9ab6f9f18Keith Whitwell int32_t len = 0; 1689e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian const UChar *theString = getStringForDay(dayDiff, len, status); 1699e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian if(U_SUCCESS(status) && (theString!=NULL)) { 1709e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian // found a relative string 1719e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian relativeDayString.setTo(theString, len); 1729e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian } 1739e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian 1749e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian if ( relativeDayString.length() > 0 && !fDatePattern.isEmpty() && 1759e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian (fTimePattern.isEmpty() || fCombinedFormat == NULL || fCombinedHasDateAtStart)) { 1769e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian#if !UCONFIG_NO_BREAK_ITERATION 1779e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian // capitalize relativeDayString according to context for relative, set formatter no context 1789e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian if ( u_islower(relativeDayString.char32At(0)) && fCapitalizationBrkIter!= NULL && 1799e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian ( capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE || 1809e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian (capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && fCapitalizationOfRelativeUnitsForUIListMenu) || 1819e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian (capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_STANDALONE && fCapitalizationOfRelativeUnitsForStandAlone) ) ) { 1829e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian // titlecase first word of relativeDayString 1839e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian relativeDayString.toTitle(fCapitalizationBrkIter, fLocale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT); 1849e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian } 1859e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian#endif 1869e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian fDateTimeFormatter->setContext(UDISPCTX_CAPITALIZATION_NONE, status); 1879e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian } else { 1889e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian // set our context for the formatter 18946b0988c673b28e072fd0cbf477632a9ab6f9f18Keith Whitwell fDateTimeFormatter->setContext(capitalizationContext, status); 1909e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian } 1919e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian 1929e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian if (fDatePattern.isEmpty()) { 19346b0988c673b28e072fd0cbf477632a9ab6f9f18Keith Whitwell fDateTimeFormatter->applyPattern(fTimePattern); 19446b0988c673b28e072fd0cbf477632a9ab6f9f18Keith Whitwell fDateTimeFormatter->format(cal,appendTo,pos); 195e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) { 196e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell if (relativeDayString.length() > 0) { 197233aafbb30594d0193b00705d0532be97060ebd1Brian Paul appendTo.append(relativeDayString); 198233aafbb30594d0193b00705d0532be97060ebd1Brian Paul } else { 199233aafbb30594d0193b00705d0532be97060ebd1Brian Paul fDateTimeFormatter->applyPattern(fDatePattern); 200233aafbb30594d0193b00705d0532be97060ebd1Brian Paul fDateTimeFormatter->format(cal,appendTo,pos); 201233aafbb30594d0193b00705d0532be97060ebd1Brian Paul } 202233aafbb30594d0193b00705d0532be97060ebd1Brian Paul } else { 203233aafbb30594d0193b00705d0532be97060ebd1Brian Paul UnicodeString datePattern; 204233aafbb30594d0193b00705d0532be97060ebd1Brian Paul if (relativeDayString.length() > 0) { 205298be2b028263b2c343a707662c6fbfa18293cb2Kristian Høgsberg // Need to quote the relativeDayString to make it a legal date pattern 206233aafbb30594d0193b00705d0532be97060ebd1Brian Paul relativeDayString.findAndReplace(UNICODE_STRING("'", 1), UNICODE_STRING("''", 2)); // double any existing APOSTROPHE 207233aafbb30594d0193b00705d0532be97060ebd1Brian Paul relativeDayString.insert(0, APOSTROPHE); // add APOSTROPHE at beginning... 208233aafbb30594d0193b00705d0532be97060ebd1Brian Paul relativeDayString.append(APOSTROPHE); // and at end 209233aafbb30594d0193b00705d0532be97060ebd1Brian Paul datePattern.setTo(relativeDayString); 210233aafbb30594d0193b00705d0532be97060ebd1Brian Paul } else { 211c123a9b2edc5852cb50485f344219508254081a8Brian Paul datePattern.setTo(fDatePattern); 212233aafbb30594d0193b00705d0532be97060ebd1Brian Paul } 213233aafbb30594d0193b00705d0532be97060ebd1Brian Paul UnicodeString combinedPattern; 214233aafbb30594d0193b00705d0532be97060ebd1Brian Paul Formattable timeDatePatterns[] = { fTimePattern, datePattern }; 215233aafbb30594d0193b00705d0532be97060ebd1Brian Paul fCombinedFormat->format(timeDatePatterns, 2, combinedPattern, pos, status); // pos is ignored by this 216233aafbb30594d0193b00705d0532be97060ebd1Brian Paul fDateTimeFormatter->applyPattern(combinedPattern); 2179e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian fDateTimeFormatter->format(cal,appendTo,pos); 218e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell } 219e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell 220e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell return appendTo; 221e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell} 222e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell 223e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell 224e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell 225f9995b30756140724f41daf963fa06167912be7fKristian HøgsbergUnicodeString& 226e3a051e0538a605551f4d58294c94f5eb00ed07fKeith WhitwellRelativeDateFormat::format(const Formattable& obj, 227cd03ed4f54444d96e4e47cdb118a3dfd94d92bb0Keith Whitwell UnicodeString& appendTo, 2289e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian FieldPosition& pos, 2299e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian UErrorCode& status) const 2309e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian{ 231e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell // this is just here to get around the hiding problem 232b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul // (the previous format() override would hide the version of 233e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell // format() on DateFormat that this function correspond to, so we 234e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell // have to redefine it here) 2350070d398d13759adc519f9bc764ffd39bc88890eBrian Paul return DateFormat::format(obj, appendTo, pos, status); 23664b4298181373d64ef2226935f70e9062536de8bBrian Paul} 237e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell 238ccea3ff8a9eae16d2ca11b9cedef1318cffe3fb4Brian 2391c0f1dd42a50464eeb81de4aad8eecf24b3d6c89Chad Versacevoid RelativeDateFormat::parse( const UnicodeString& text, 2409e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian Calendar& cal, 2419e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian ParsePosition& pos) const { 2429e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian 243e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell int32_t startIndex = pos.getIndex(); 2449e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian if (fDatePattern.isEmpty()) { 245af2aa8e9cf88a9ee3ec338eddc9a47bf2f142cb7Brian // no date pattern, try parsing as time 24622a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul fDateTimeFormatter->applyPattern(fTimePattern); 24722a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul fDateTimeFormatter->parse(text,cal,pos); 248efda5cb6263631175aa2efe46df9322b3c5775eeBrian } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) { 249e6df80184b0c7e83eba382b3161ba11db497c55dIan Romanick // no time pattern or way to combine, try parsing as date 250efda5cb6263631175aa2efe46df9322b3c5775eeBrian // first check whether text matches a relativeDayString 251e6df80184b0c7e83eba382b3161ba11db497c55dIan Romanick UBool matchedRelative = FALSE; 252efda5cb6263631175aa2efe46df9322b3c5775eeBrian for (int n=0; n < fDatesLen && !matchedRelative; n++) { 25322a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul if (fDates[n].string != NULL && 254e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell text.compare(startIndex, fDates[n].len, fDates[n].string) == 0) { 2559e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian // it matched, handle the relative day string 256af2aa8e9cf88a9ee3ec338eddc9a47bf2f142cb7Brian UErrorCode status = U_ZERO_ERROR; 2579e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian matchedRelative = TRUE; 258e6df80184b0c7e83eba382b3161ba11db497c55dIan Romanick 259e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell // Set the calendar to now+offset 260e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell cal.setTime(Calendar::getNow(),status); 261b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul cal.add(UCAL_DATE,fDates[n].offset, status); 26245bc887da226403f2c41077e40ca38b6f60f1359Brian Paul 263e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell if(U_FAILURE(status)) { 264e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell // failure in setting calendar field, set offset to beginning of rel day string 265b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul pos.setErrorIndex(startIndex); 26645bc887da226403f2c41077e40ca38b6f60f1359Brian Paul } else { 267e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell pos.setIndex(startIndex + fDates[n].len); 268e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell } 269 } 270 } 271 if (!matchedRelative) { 272 // just parse as normal date 273 fDateTimeFormatter->applyPattern(fDatePattern); 274 fDateTimeFormatter->parse(text,cal,pos); 275 } 276 } else { 277 // Here we replace any relativeDayString in text with the equivalent date 278 // formatted per fDatePattern, then parse text normally using the combined pattern. 279 UnicodeString modifiedText(text); 280 FieldPosition fPos; 281 int32_t dateStart = 0, origDateLen = 0, modDateLen = 0; 282 UErrorCode status = U_ZERO_ERROR; 283 for (int n=0; n < fDatesLen; n++) { 284 int32_t relativeStringOffset; 285 if (fDates[n].string != NULL && 286 (relativeStringOffset = modifiedText.indexOf(fDates[n].string, fDates[n].len, startIndex)) >= startIndex) { 287 // it matched, replace the relative date with a real one for parsing 288 UnicodeString dateString; 289 Calendar * tempCal = cal.clone(); 290 291 // Set the calendar to now+offset 292 tempCal->setTime(Calendar::getNow(),status); 293 tempCal->add(UCAL_DATE,fDates[n].offset, status); 294 if(U_FAILURE(status)) { 295 pos.setErrorIndex(startIndex); 296 delete tempCal; 297 return; 298 } 299 300 fDateTimeFormatter->applyPattern(fDatePattern); 301 fDateTimeFormatter->format(*tempCal, dateString, fPos); 302 dateStart = relativeStringOffset; 303 origDateLen = fDates[n].len; 304 modDateLen = dateString.length(); 305 modifiedText.replace(dateStart, origDateLen, dateString); 306 delete tempCal; 307 break; 308 } 309 } 310 UnicodeString combinedPattern; 311 Formattable timeDatePatterns[] = { fTimePattern, fDatePattern }; 312 fCombinedFormat->format(timeDatePatterns, 2, combinedPattern, fPos, status); // pos is ignored by this 313 fDateTimeFormatter->applyPattern(combinedPattern); 314 fDateTimeFormatter->parse(modifiedText,cal,pos); 315 316 // Adjust offsets 317 UBool noError = (pos.getErrorIndex() < 0); 318 int32_t offset = (noError)? pos.getIndex(): pos.getErrorIndex(); 319 if (offset >= dateStart + modDateLen) { 320 // offset at or after the end of the replaced text, 321 // correct by the difference between original and replacement 322 offset -= (modDateLen - origDateLen); 323 } else if (offset >= dateStart) { 324 // offset in the replaced text, set it to the beginning of that text 325 // (i.e. the beginning of the relative day string) 326 offset = dateStart; 327 } 328 if (noError) { 329 pos.setIndex(offset); 330 } else { 331 pos.setErrorIndex(offset); 332 } 333 } 334} 335 336UDate 337RelativeDateFormat::parse( const UnicodeString& text, 338 ParsePosition& pos) const { 339 // redefined here because the other parse() function hides this function's 340 // cunterpart on DateFormat 341 return DateFormat::parse(text, pos); 342} 343 344UDate 345RelativeDateFormat::parse(const UnicodeString& text, UErrorCode& status) const 346{ 347 // redefined here because the other parse() function hides this function's 348 // counterpart on DateFormat 349 return DateFormat::parse(text, status); 350} 351 352 353const UChar *RelativeDateFormat::getStringForDay(int32_t day, int32_t &len, UErrorCode &status) const { 354 if(U_FAILURE(status)) { 355 return NULL; 356 } 357 358 // Is it outside the resource bundle's range? 359 if(day < fDayMin || day > fDayMax) { 360 return NULL; // don't have it. 361 } 362 363 // Linear search the held strings 364 for(int n=0;n<fDatesLen;n++) { 365 if(fDates[n].offset == day) { 366 len = fDates[n].len; 367 return fDates[n].string; 368 } 369 } 370 371 return NULL; // not found. 372} 373 374UnicodeString& 375RelativeDateFormat::toPattern(UnicodeString& result, UErrorCode& status) const 376{ 377 if (!U_FAILURE(status)) { 378 result.remove(); 379 if (fDatePattern.isEmpty()) { 380 result.setTo(fTimePattern); 381 } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) { 382 result.setTo(fDatePattern); 383 } else { 384 Formattable timeDatePatterns[] = { fTimePattern, fDatePattern }; 385 FieldPosition pos; 386 fCombinedFormat->format(timeDatePatterns, 2, result, pos, status); 387 } 388 } 389 return result; 390} 391 392UnicodeString& 393RelativeDateFormat::toPatternDate(UnicodeString& result, UErrorCode& status) const 394{ 395 if (!U_FAILURE(status)) { 396 result.remove(); 397 result.setTo(fDatePattern); 398 } 399 return result; 400} 401 402UnicodeString& 403RelativeDateFormat::toPatternTime(UnicodeString& result, UErrorCode& status) const 404{ 405 if (!U_FAILURE(status)) { 406 result.remove(); 407 result.setTo(fTimePattern); 408 } 409 return result; 410} 411 412void 413RelativeDateFormat::applyPatterns(const UnicodeString& datePattern, const UnicodeString& timePattern, UErrorCode &status) 414{ 415 if (!U_FAILURE(status)) { 416 fDatePattern.setTo(datePattern); 417 fTimePattern.setTo(timePattern); 418 } 419} 420 421const DateFormatSymbols* 422RelativeDateFormat::getDateFormatSymbols() const 423{ 424 return fDateTimeFormatter->getDateFormatSymbols(); 425} 426 427// override the DateFormat implementation in order to 428// lazily initialize relevant items 429void 430RelativeDateFormat::setContext(UDisplayContext value, UErrorCode& status) 431{ 432 DateFormat::setContext(value, status); 433 if (U_SUCCESS(status)) { 434 if (!fCapitalizationInfoSet && 435 (value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU || value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE)) { 436 initCapitalizationContextInfo(fLocale); 437 fCapitalizationInfoSet = TRUE; 438 } 439#if !UCONFIG_NO_BREAK_ITERATION 440 if ( fCapitalizationBrkIter == NULL && (value==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE || 441 (value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && fCapitalizationOfRelativeUnitsForUIListMenu) || 442 (value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE && fCapitalizationOfRelativeUnitsForStandAlone)) ) { 443 UErrorCode status = U_ZERO_ERROR; 444 fCapitalizationBrkIter = BreakIterator::createSentenceInstance(fLocale, status); 445 if (U_FAILURE(status)) { 446 delete fCapitalizationBrkIter; 447 fCapitalizationBrkIter = NULL; 448 } 449 } 450#endif 451 } 452} 453 454void 455RelativeDateFormat::initCapitalizationContextInfo(const Locale& thelocale) 456{ 457#if !UCONFIG_NO_BREAK_ITERATION 458 const char * localeID = (thelocale != NULL)? thelocale.getBaseName(): NULL; 459 UErrorCode status = U_ZERO_ERROR; 460 UResourceBundle *rb = ures_open(NULL, localeID, &status); 461 rb = ures_getByKeyWithFallback(rb, "contextTransforms", rb, &status); 462 rb = ures_getByKeyWithFallback(rb, "relative", rb, &status); 463 if (U_SUCCESS(status) && rb != NULL) { 464 int32_t len = 0; 465 const int32_t * intVector = ures_getIntVector(rb, &len, &status); 466 if (U_SUCCESS(status) && intVector != NULL && len >= 2) { 467 fCapitalizationOfRelativeUnitsForUIListMenu = intVector[0]; 468 fCapitalizationOfRelativeUnitsForStandAlone = intVector[1]; 469 } 470 } 471 ures_close(rb); 472#endif 473} 474 475static const UChar patItem1[] = {0x7B,0x31,0x7D}; // "{1}" 476static const int32_t patItem1Len = 3; 477 478void RelativeDateFormat::loadDates(UErrorCode &status) { 479 CalendarData calData(fLocale, "gregorian", status); 480 481 UErrorCode tempStatus = status; 482 UResourceBundle *dateTimePatterns = calData.getByKey(DT_DateTimePatternsTag, tempStatus); 483 if(U_SUCCESS(tempStatus)) { 484 int32_t patternsSize = ures_getSize(dateTimePatterns); 485 if (patternsSize > kDateTime) { 486 int32_t resStrLen = 0; 487 488 int32_t glueIndex = kDateTime; 489 if (patternsSize >= (DateFormat::kDateTimeOffset + DateFormat::kShort + 1)) { 490 // Get proper date time format 491 switch (fDateStyle) { 492 case kFullRelative: 493 case kFull: 494 glueIndex = kDateTimeOffset + kFull; 495 break; 496 case kLongRelative: 497 case kLong: 498 glueIndex = kDateTimeOffset + kLong; 499 break; 500 case kMediumRelative: 501 case kMedium: 502 glueIndex = kDateTimeOffset + kMedium; 503 break; 504 case kShortRelative: 505 case kShort: 506 glueIndex = kDateTimeOffset + kShort; 507 break; 508 default: 509 break; 510 } 511 } 512 513 const UChar *resStr = ures_getStringByIndex(dateTimePatterns, glueIndex, &resStrLen, &tempStatus); 514 if (U_SUCCESS(tempStatus) && resStrLen >= patItem1Len && u_strncmp(resStr,patItem1,patItem1Len)==0) { 515 fCombinedHasDateAtStart = TRUE; 516 } 517 fCombinedFormat = new MessageFormat(UnicodeString(TRUE, resStr, resStrLen), fLocale, tempStatus); 518 } 519 } 520 521 UResourceBundle *rb = ures_open(NULL, fLocale.getBaseName(), &status); 522 rb = ures_getByKeyWithFallback(rb, "fields", rb, &status); 523 rb = ures_getByKeyWithFallback(rb, "day", rb, &status); 524 rb = ures_getByKeyWithFallback(rb, "relative", rb, &status); 525 // set up min/max 526 fDayMin=-1; 527 fDayMax=1; 528 529 if(U_FAILURE(status)) { 530 fDatesLen=0; 531 ures_close(rb); 532 return; 533 } 534 535 fDatesLen = ures_getSize(rb); 536 fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*fDatesLen); 537 538 // Load in each item into the array... 539 int n = 0; 540 541 UResourceBundle *subString = NULL; 542 543 while(ures_hasNext(rb) && U_SUCCESS(status)) { // iterate over items 544 subString = ures_getNextResource(rb, subString, &status); 545 546 if(U_FAILURE(status) || (subString==NULL)) break; 547 548 // key = offset # 549 const char *key = ures_getKey(subString); 550 551 // load the string and length 552 int32_t aLen; 553 const UChar* aString = ures_getString(subString, &aLen, &status); 554 555 if(U_FAILURE(status) || aString == NULL) break; 556 557 // calculate the offset 558 int32_t offset = atoi(key); 559 560 // set min/max 561 if(offset < fDayMin) { 562 fDayMin = offset; 563 } 564 if(offset > fDayMax) { 565 fDayMax = offset; 566 } 567 568 // copy the string pointer 569 fDates[n].offset = offset; 570 fDates[n].string = aString; 571 fDates[n].len = aLen; 572 573 n++; 574 } 575 ures_close(subString); 576 ures_close(rb); 577 578 // the fDates[] array could be sorted here, for direct access. 579} 580 581//---------------------------------------------------------------------- 582 583// this should to be in DateFormat, instead it was copied from SimpleDateFormat. 584 585Calendar* 586RelativeDateFormat::initializeCalendar(TimeZone* adoptZone, const Locale& locale, UErrorCode& status) 587{ 588 if(!U_FAILURE(status)) { 589 fCalendar = Calendar::createInstance(adoptZone?adoptZone:TimeZone::createDefault(), locale, status); 590 } 591 if (U_SUCCESS(status) && fCalendar == NULL) { 592 status = U_MEMORY_ALLOCATION_ERROR; 593 } 594 return fCalendar; 595} 596 597int32_t RelativeDateFormat::dayDifference(Calendar &cal, UErrorCode &status) { 598 if(U_FAILURE(status)) { 599 return 0; 600 } 601 // TODO: Cache the nowCal to avoid heap allocs? Would be difficult, don't know the calendar type 602 Calendar *nowCal = cal.clone(); 603 nowCal->setTime(Calendar::getNow(), status); 604 605 // For the day difference, we are interested in the difference in the (modified) julian day number 606 // which is midnight to midnight. Using fieldDifference() is NOT correct here, because 607 // 6pm Jan 4th to 10am Jan 5th should be considered "tomorrow". 608 int32_t dayDiff = cal.get(UCAL_JULIAN_DAY, status) - nowCal->get(UCAL_JULIAN_DAY, status); 609 610 delete nowCal; 611 return dayDiff; 612} 613 614U_NAMESPACE_END 615 616#endif 617 618