mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-14 17:24:01 +00:00
ICU-20342 Adding FormattedDateInterval in C and C++.
- Adds first "span" field category - Re-implements DateIntervalFormat#fallbackFormat to use FieldPositionHandler - New temporary wiring in SimpleFormatter
This commit is contained in:
parent
f4f899a63b
commit
e2ad4f8113
17 changed files with 787 additions and 130 deletions
|
@ -246,15 +246,24 @@ UnicodeString &SimpleFormatter::formatAndReplace(
|
|||
}
|
||||
|
||||
UnicodeString SimpleFormatter::getTextWithNoArguments(
|
||||
const UChar *compiledPattern, int32_t compiledPatternLength) {
|
||||
const UChar *compiledPattern,
|
||||
int32_t compiledPatternLength,
|
||||
int32_t* offsets,
|
||||
int32_t offsetsLength) {
|
||||
for (int32_t i = 0; i < offsetsLength; i++) {
|
||||
offsets[i] = -1;
|
||||
}
|
||||
int32_t capacity = compiledPatternLength - 1 -
|
||||
getArgumentLimit(compiledPattern, compiledPatternLength);
|
||||
UnicodeString sb(capacity, 0, 0); // Java: StringBuilder
|
||||
for (int32_t i = 1; i < compiledPatternLength;) {
|
||||
int32_t segmentLength = compiledPattern[i++] - ARG_NUM_LIMIT;
|
||||
if (segmentLength > 0) {
|
||||
sb.append(compiledPattern + i, segmentLength);
|
||||
i += segmentLength;
|
||||
int32_t n = compiledPattern[i++];
|
||||
if (n > ARG_NUM_LIMIT) {
|
||||
n -= ARG_NUM_LIMIT;
|
||||
sb.append(compiledPattern + i, n);
|
||||
i += n;
|
||||
} else if (n < offsetsLength) {
|
||||
offsets[n] = sb.length();
|
||||
}
|
||||
}
|
||||
return sb;
|
||||
|
|
|
@ -265,9 +265,38 @@ public:
|
|||
* @stable ICU 57
|
||||
*/
|
||||
UnicodeString getTextWithNoArguments() const {
|
||||
return getTextWithNoArguments(compiledPattern.getBuffer(), compiledPattern.length());
|
||||
return getTextWithNoArguments(
|
||||
compiledPattern.getBuffer(),
|
||||
compiledPattern.length(),
|
||||
nullptr,
|
||||
0);
|
||||
}
|
||||
|
||||
#ifndef U_HIDE_INTERNAL_API
|
||||
/**
|
||||
* Returns the pattern text with none of the arguments.
|
||||
* Like formatting with all-empty string values.
|
||||
*
|
||||
* TODO(ICU-20406): Replace this with an Iterator interface.
|
||||
*
|
||||
* @param offsets offsets[i] receives the offset of where {i} was located
|
||||
* before it was replaced by an empty string.
|
||||
* For example, "a{0}b{1}" produces offset 1 for i=0 and 2 for i=1.
|
||||
* Can be nullptr if offsetsLength==0.
|
||||
* If there is no {i} in the pattern, then offsets[i] is set to -1.
|
||||
* @param offsetsLength The length of the offsets array.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
UnicodeString getTextWithNoArguments(int32_t *offsets, int32_t offsetsLength) const {
|
||||
return getTextWithNoArguments(
|
||||
compiledPattern.getBuffer(),
|
||||
compiledPattern.length(),
|
||||
offsets,
|
||||
offsetsLength);
|
||||
}
|
||||
#endif // U_HIDE_INTERNAL_API
|
||||
|
||||
private:
|
||||
/**
|
||||
* Binary representation of the compiled pattern.
|
||||
|
@ -285,7 +314,11 @@ private:
|
|||
return compiledPatternLength == 0 ? 0 : compiledPattern[0];
|
||||
}
|
||||
|
||||
static UnicodeString getTextWithNoArguments(const char16_t *compiledPattern, int32_t compiledPatternLength);
|
||||
static UnicodeString getTextWithNoArguments(
|
||||
const char16_t *compiledPattern,
|
||||
int32_t compiledPatternLength,
|
||||
int32_t *offsets,
|
||||
int32_t offsetsLength);
|
||||
|
||||
static UnicodeString &format(
|
||||
const char16_t *compiledPattern, int32_t compiledPatternLength,
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "dtitv_impl.h"
|
||||
#include "mutex.h"
|
||||
#include "uresimp.h"
|
||||
#include "formattedval_impl.h"
|
||||
|
||||
#ifdef DTITVFMT_DEBUG
|
||||
#include <iostream>
|
||||
|
@ -65,6 +66,17 @@ static const UChar gLaterFirstPrefix[] = {LOW_L, LOW_A, LOW_T, LOW_E, LOW_S,LOW_
|
|||
static const UChar gEarlierFirstPrefix[] = {LOW_E, LOW_A, LOW_R, LOW_L, LOW_I, LOW_E, LOW_S, LOW_T, CAP_F, LOW_I, LOW_R, LOW_S, LOW_T, COLON};
|
||||
|
||||
|
||||
class FormattedDateIntervalData : public FormattedValueFieldPositionIteratorImpl {
|
||||
public:
|
||||
FormattedDateIntervalData(UErrorCode& status) : FormattedValueFieldPositionIteratorImpl(5, status) {}
|
||||
virtual ~FormattedDateIntervalData();
|
||||
};
|
||||
|
||||
FormattedDateIntervalData::~FormattedDateIntervalData() = default;
|
||||
|
||||
UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedDateInterval)
|
||||
|
||||
|
||||
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalFormat)
|
||||
|
||||
// Mutex, protects access to fDateFormat, fFromCalendar and fToCalendar.
|
||||
|
@ -271,15 +283,51 @@ DateIntervalFormat::format(const DateInterval* dtInterval,
|
|||
if ( U_FAILURE(status) ) {
|
||||
return appendTo;
|
||||
}
|
||||
if (fFromCalendar == NULL || fToCalendar == NULL || fDateFormat == NULL || fInfo == NULL) {
|
||||
if (fDateFormat == NULL || fInfo == NULL) {
|
||||
status = U_INVALID_STATE_ERROR;
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
FieldPositionOnlyHandler handler(fieldPosition);
|
||||
handler.setAcceptFirstOnly(TRUE);
|
||||
int8_t ignore;
|
||||
|
||||
Mutex lock(&gFormatterMutex);
|
||||
fFromCalendar->setTime(dtInterval->getFromDate(), status);
|
||||
fToCalendar->setTime(dtInterval->getToDate(), status);
|
||||
return formatImpl(*fFromCalendar, *fToCalendar, appendTo,fieldPosition, status);
|
||||
return formatIntervalImpl(*dtInterval, appendTo, ignore, handler, status);
|
||||
}
|
||||
|
||||
|
||||
FormattedDateInterval DateIntervalFormat::formatToValue(
|
||||
const DateInterval& dtInterval,
|
||||
UErrorCode& status) const {
|
||||
LocalPointer<FormattedDateIntervalData> result(new FormattedDateIntervalData(status), status);
|
||||
if (U_FAILURE(status)) {
|
||||
return FormattedDateInterval(status);
|
||||
}
|
||||
UnicodeString string;
|
||||
int8_t firstIndex;
|
||||
auto handler = result->getHandler(status);
|
||||
handler.setCategory(UFIELD_CATEGORY_DATE);
|
||||
{
|
||||
Mutex lock(&gFormatterMutex);
|
||||
formatIntervalImpl(dtInterval, string, firstIndex, handler, status);
|
||||
}
|
||||
handler.getError(status);
|
||||
result->appendString(string, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return FormattedDateInterval(status);
|
||||
}
|
||||
|
||||
// Compute the span fields and sort them into place:
|
||||
if (firstIndex != -1) {
|
||||
result->addOverlapSpans(UFIELD_CATEGORY_DATE_INTERVAL_SPAN, firstIndex, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return FormattedDateInterval(status);
|
||||
}
|
||||
result->sort();
|
||||
}
|
||||
|
||||
return FormattedDateInterval(result.orphan());
|
||||
}
|
||||
|
||||
|
||||
|
@ -289,8 +337,63 @@ DateIntervalFormat::format(Calendar& fromCalendar,
|
|||
UnicodeString& appendTo,
|
||||
FieldPosition& pos,
|
||||
UErrorCode& status) const {
|
||||
FieldPositionOnlyHandler handler(pos);
|
||||
handler.setAcceptFirstOnly(TRUE);
|
||||
int8_t ignore;
|
||||
|
||||
Mutex lock(&gFormatterMutex);
|
||||
return formatImpl(fromCalendar, toCalendar, appendTo, pos, status);
|
||||
return formatImpl(fromCalendar, toCalendar, appendTo, ignore, handler, status);
|
||||
}
|
||||
|
||||
|
||||
FormattedDateInterval DateIntervalFormat::formatToValue(
|
||||
Calendar& fromCalendar,
|
||||
Calendar& toCalendar,
|
||||
UErrorCode& status) const {
|
||||
LocalPointer<FormattedDateIntervalData> result(new FormattedDateIntervalData(status), status);
|
||||
if (U_FAILURE(status)) {
|
||||
return FormattedDateInterval(status);
|
||||
}
|
||||
UnicodeString string;
|
||||
int8_t firstIndex;
|
||||
auto handler = result->getHandler(status);
|
||||
handler.setCategory(UFIELD_CATEGORY_DATE);
|
||||
{
|
||||
Mutex lock(&gFormatterMutex);
|
||||
formatImpl(fromCalendar, toCalendar, string, firstIndex, handler, status);
|
||||
}
|
||||
handler.getError(status);
|
||||
result->appendString(string, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return FormattedDateInterval(status);
|
||||
}
|
||||
|
||||
// Compute the span fields and sort them into place:
|
||||
if (firstIndex != -1) {
|
||||
result->addOverlapSpans(UFIELD_CATEGORY_DATE_INTERVAL_SPAN, firstIndex, status);
|
||||
result->sort();
|
||||
}
|
||||
|
||||
return FormattedDateInterval(result.orphan());
|
||||
}
|
||||
|
||||
|
||||
UnicodeString& DateIntervalFormat::formatIntervalImpl(
|
||||
const DateInterval& dtInterval,
|
||||
UnicodeString& appendTo,
|
||||
int8_t& firstIndex,
|
||||
FieldPositionHandler& fphandler,
|
||||
UErrorCode& status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return appendTo;
|
||||
}
|
||||
if (fFromCalendar == nullptr || fToCalendar == nullptr) {
|
||||
status = U_INVALID_STATE_ERROR;
|
||||
return appendTo;
|
||||
}
|
||||
fFromCalendar->setTime(dtInterval.getFromDate(), status);
|
||||
fToCalendar->setTime(dtInterval.getToDate(), status);
|
||||
return formatImpl(*fFromCalendar, *fToCalendar, appendTo, firstIndex, fphandler, status);
|
||||
}
|
||||
|
||||
|
||||
|
@ -298,12 +401,16 @@ UnicodeString&
|
|||
DateIntervalFormat::formatImpl(Calendar& fromCalendar,
|
||||
Calendar& toCalendar,
|
||||
UnicodeString& appendTo,
|
||||
FieldPosition& pos,
|
||||
int8_t& firstIndex,
|
||||
FieldPositionHandler& fphandler,
|
||||
UErrorCode& status) const {
|
||||
if ( U_FAILURE(status) ) {
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
// Initialize firstIndex to -1 (single date, no range)
|
||||
firstIndex = -1;
|
||||
|
||||
// not support different calendar types and time zones
|
||||
//if ( fromCalendar.getType() != toCalendar.getType() ) {
|
||||
if ( !fromCalendar.isEquivalentTo(toCalendar) ) {
|
||||
|
@ -346,7 +453,7 @@ DateIntervalFormat::formatImpl(Calendar& fromCalendar,
|
|||
/* ignore the millisecond etc. small fields' difference.
|
||||
* use single date when all the above are the same.
|
||||
*/
|
||||
return fDateFormat->format(fromCalendar, appendTo, pos);
|
||||
return fDateFormat->_format(fromCalendar, appendTo, fphandler, status);
|
||||
}
|
||||
UBool fromToOnSameDay = (field==UCAL_AM_PM || field==UCAL_HOUR || field==UCAL_MINUTE || field==UCAL_SECOND);
|
||||
|
||||
|
@ -363,9 +470,9 @@ DateIntervalFormat::formatImpl(Calendar& fromCalendar,
|
|||
* the smallest calendar field in pattern,
|
||||
* return single date format.
|
||||
*/
|
||||
return fDateFormat->format(fromCalendar, appendTo, pos);
|
||||
return fDateFormat->_format(fromCalendar, appendTo, fphandler, status);
|
||||
}
|
||||
return fallbackFormat(fromCalendar, toCalendar, fromToOnSameDay, appendTo, pos, status);
|
||||
return fallbackFormat(fromCalendar, toCalendar, fromToOnSameDay, appendTo, firstIndex, fphandler, status);
|
||||
}
|
||||
// If the first part in interval pattern is empty,
|
||||
// the 2nd part of it saves the full-pattern used in fall-back.
|
||||
|
@ -375,7 +482,7 @@ DateIntervalFormat::formatImpl(Calendar& fromCalendar,
|
|||
UnicodeString originalPattern;
|
||||
fDateFormat->toPattern(originalPattern);
|
||||
fDateFormat->applyPattern(intervalPattern.secondPart);
|
||||
appendTo = fallbackFormat(fromCalendar, toCalendar, fromToOnSameDay, appendTo, pos, status);
|
||||
appendTo = fallbackFormat(fromCalendar, toCalendar, fromToOnSameDay, appendTo, firstIndex, fphandler, status);
|
||||
fDateFormat->applyPattern(originalPattern);
|
||||
return appendTo;
|
||||
}
|
||||
|
@ -384,24 +491,22 @@ DateIntervalFormat::formatImpl(Calendar& fromCalendar,
|
|||
if ( intervalPattern.laterDateFirst ) {
|
||||
firstCal = &toCalendar;
|
||||
secondCal = &fromCalendar;
|
||||
firstIndex = 1;
|
||||
} else {
|
||||
firstCal = &fromCalendar;
|
||||
secondCal = &toCalendar;
|
||||
firstIndex = 0;
|
||||
}
|
||||
// break the interval pattern into 2 parts,
|
||||
// first part should not be empty,
|
||||
UnicodeString originalPattern;
|
||||
fDateFormat->toPattern(originalPattern);
|
||||
fDateFormat->applyPattern(intervalPattern.firstPart);
|
||||
fDateFormat->format(*firstCal, appendTo, pos);
|
||||
fDateFormat->_format(*firstCal, appendTo, fphandler, status);
|
||||
|
||||
if ( !intervalPattern.secondPart.isEmpty() ) {
|
||||
fDateFormat->applyPattern(intervalPattern.secondPart);
|
||||
FieldPosition otherPos;
|
||||
otherPos.setField(pos.getField());
|
||||
fDateFormat->format(*secondCal, appendTo, otherPos);
|
||||
if (pos.getEndIndex() == 0 && otherPos.getEndIndex() > 0) {
|
||||
pos = otherPos;
|
||||
}
|
||||
fDateFormat->_format(*secondCal, appendTo, fphandler, status);
|
||||
}
|
||||
fDateFormat->applyPattern(originalPattern);
|
||||
return appendTo;
|
||||
|
@ -1294,40 +1399,37 @@ DateIntervalFormat::splitPatternInto2Part(const UnicodeString& intervalPattern)
|
|||
return (i - count);
|
||||
}
|
||||
|
||||
static const UChar bracketedZero[] = {0x7B,0x30,0x7D};
|
||||
static const UChar bracketedOne[] = {0x7B,0x31,0x7D};
|
||||
|
||||
void
|
||||
DateIntervalFormat::adjustPosition(UnicodeString& combiningPattern, // has {0} and {1} in it
|
||||
UnicodeString& pat0, FieldPosition& pos0, // pattern and pos corresponding to {0}
|
||||
UnicodeString& pat1, FieldPosition& pos1, // pattern and pos corresponding to {1}
|
||||
FieldPosition& posResult) {
|
||||
int32_t index0 = combiningPattern.indexOf(bracketedZero, 3, 0);
|
||||
int32_t index1 = combiningPattern.indexOf(bracketedOne, 3, 0);
|
||||
if (index0 < 0 || index1 < 0) {
|
||||
void DateIntervalFormat::fallbackFormatRange(
|
||||
Calendar& fromCalendar,
|
||||
Calendar& toCalendar,
|
||||
UnicodeString& appendTo,
|
||||
int8_t& firstIndex,
|
||||
FieldPositionHandler& fphandler,
|
||||
UErrorCode& status) const {
|
||||
UnicodeString fallbackPattern;
|
||||
fInfo->getFallbackIntervalPattern(fallbackPattern);
|
||||
SimpleFormatter sf(fallbackPattern, 2, 2, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
int32_t placeholderLen = 3; // length of "{0}" or "{1}"
|
||||
if (index0 < index1) {
|
||||
if (pos0.getEndIndex() > 0) {
|
||||
posResult.setBeginIndex(pos0.getBeginIndex() + index0);
|
||||
posResult.setEndIndex(pos0.getEndIndex() + index0);
|
||||
} else if (pos1.getEndIndex() > 0) {
|
||||
// here index1 >= 3
|
||||
index1 += pat0.length() - placeholderLen; // adjust for pat0 replacing {0}
|
||||
posResult.setBeginIndex(pos1.getBeginIndex() + index1);
|
||||
posResult.setEndIndex(pos1.getEndIndex() + index1);
|
||||
}
|
||||
int32_t offsets[2];
|
||||
UnicodeString patternBody = sf.getTextWithNoArguments(offsets, 2);
|
||||
|
||||
// TODO(ICU-20406): Use SimpleFormatter Iterator interface when available.
|
||||
if (offsets[0] < offsets[1]) {
|
||||
firstIndex = 0;
|
||||
appendTo.append(patternBody.tempSubStringBetween(0, offsets[0]));
|
||||
fDateFormat->_format(fromCalendar, appendTo, fphandler, status);
|
||||
appendTo.append(patternBody.tempSubStringBetween(offsets[0], offsets[1]));
|
||||
fDateFormat->_format(toCalendar, appendTo, fphandler, status);
|
||||
appendTo.append(patternBody.tempSubStringBetween(offsets[1]));
|
||||
} else {
|
||||
if (pos1.getEndIndex() > 0) {
|
||||
posResult.setBeginIndex(pos1.getBeginIndex() + index1);
|
||||
posResult.setEndIndex(pos1.getEndIndex() + index1);
|
||||
} else if (pos0.getEndIndex() > 0) {
|
||||
// here index0 >= 3
|
||||
index0 += pat1.length() - placeholderLen; // adjust for pat1 replacing {1}
|
||||
posResult.setBeginIndex(pos0.getBeginIndex() + index0);
|
||||
posResult.setEndIndex(pos0.getEndIndex() + index0);
|
||||
}
|
||||
firstIndex = 1;
|
||||
appendTo.append(patternBody.tempSubStringBetween(0, offsets[1]));
|
||||
fDateFormat->_format(toCalendar, appendTo, fphandler, status);
|
||||
appendTo.append(patternBody.tempSubStringBetween(offsets[1], offsets[0]));
|
||||
fDateFormat->_format(fromCalendar, appendTo, fphandler, status);
|
||||
appendTo.append(patternBody.tempSubStringBetween(offsets[0]));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1336,51 +1438,50 @@ DateIntervalFormat::fallbackFormat(Calendar& fromCalendar,
|
|||
Calendar& toCalendar,
|
||||
UBool fromToOnSameDay, // new
|
||||
UnicodeString& appendTo,
|
||||
FieldPosition& pos,
|
||||
int8_t& firstIndex,
|
||||
FieldPositionHandler& fphandler,
|
||||
UErrorCode& status) const {
|
||||
if ( U_FAILURE(status) ) {
|
||||
return appendTo;
|
||||
}
|
||||
UnicodeString fullPattern; // for saving the pattern in fDateFormat
|
||||
|
||||
UBool formatDatePlusTimeRange = (fromToOnSameDay && fDatePattern && fTimePattern);
|
||||
// the fall back
|
||||
if (formatDatePlusTimeRange) {
|
||||
SimpleFormatter sf(*fDateTimeFormat, 2, 2, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return appendTo;
|
||||
}
|
||||
int32_t offsets[2];
|
||||
UnicodeString patternBody = sf.getTextWithNoArguments(offsets, 2);
|
||||
|
||||
UnicodeString fullPattern; // for saving the pattern in fDateFormat
|
||||
fDateFormat->toPattern(fullPattern); // save current pattern, restore later
|
||||
fDateFormat->applyPattern(*fTimePattern);
|
||||
}
|
||||
FieldPosition otherPos;
|
||||
otherPos.setField(pos.getField());
|
||||
UnicodeString earlierDate;
|
||||
fDateFormat->format(fromCalendar, earlierDate, pos);
|
||||
UnicodeString laterDate;
|
||||
fDateFormat->format(toCalendar, laterDate, otherPos);
|
||||
UnicodeString fallbackPattern;
|
||||
fInfo->getFallbackIntervalPattern(fallbackPattern);
|
||||
adjustPosition(fallbackPattern, earlierDate, pos, laterDate, otherPos, pos);
|
||||
UnicodeString fallbackRange;
|
||||
SimpleFormatter(fallbackPattern, 2, 2, status).
|
||||
format(earlierDate, laterDate, fallbackRange, status);
|
||||
if ( U_SUCCESS(status) && formatDatePlusTimeRange ) {
|
||||
// fallbackRange has just the time range, need to format the date part and combine that
|
||||
fDateFormat->applyPattern(*fDatePattern);
|
||||
UnicodeString datePortion;
|
||||
otherPos.setBeginIndex(0);
|
||||
otherPos.setEndIndex(0);
|
||||
fDateFormat->format(fromCalendar, datePortion, otherPos);
|
||||
adjustPosition(*fDateTimeFormat, fallbackRange, pos, datePortion, otherPos, pos);
|
||||
const UnicodeString *values[2] = {
|
||||
&fallbackRange, // {0} is time range
|
||||
&datePortion, // {1} is single date portion
|
||||
};
|
||||
SimpleFormatter(*fDateTimeFormat, 2, 2, status).
|
||||
formatAndReplace(values, 2, fallbackRange, NULL, 0, status);
|
||||
}
|
||||
if ( U_SUCCESS(status) ) {
|
||||
appendTo.append(fallbackRange);
|
||||
}
|
||||
if (formatDatePlusTimeRange) {
|
||||
|
||||
// {0} is time range
|
||||
// {1} is single date portion
|
||||
// TODO(ICU-20406): Use SimpleFormatter Iterator interface when available.
|
||||
if (offsets[0] < offsets[1]) {
|
||||
appendTo.append(patternBody.tempSubStringBetween(0, offsets[0]));
|
||||
fDateFormat->applyPattern(*fTimePattern);
|
||||
fallbackFormatRange(fromCalendar, toCalendar, appendTo, firstIndex, fphandler, status);
|
||||
appendTo.append(patternBody.tempSubStringBetween(offsets[0], offsets[1]));
|
||||
fDateFormat->applyPattern(*fDatePattern);
|
||||
fDateFormat->_format(fromCalendar, appendTo, fphandler, status);
|
||||
appendTo.append(patternBody.tempSubStringBetween(offsets[1]));
|
||||
} else {
|
||||
appendTo.append(patternBody.tempSubStringBetween(0, offsets[1]));
|
||||
fDateFormat->applyPattern(*fDatePattern);
|
||||
fDateFormat->_format(fromCalendar, appendTo, fphandler, status);
|
||||
appendTo.append(patternBody.tempSubStringBetween(offsets[1], offsets[0]));
|
||||
fDateFormat->applyPattern(*fTimePattern);
|
||||
fallbackFormatRange(fromCalendar, toCalendar, appendTo, firstIndex, fphandler, status);
|
||||
appendTo.append(patternBody.tempSubStringBetween(offsets[0]));
|
||||
}
|
||||
|
||||
// restore full pattern
|
||||
fDateFormat->applyPattern(fullPattern);
|
||||
} else {
|
||||
fallbackFormatRange(fromCalendar, toCalendar, appendTo, firstIndex, fphandler, status);
|
||||
}
|
||||
return appendTo;
|
||||
}
|
||||
|
@ -1552,6 +1653,7 @@ DateIntervalFormat::fgCalendarFieldToPatternLetter[] =
|
|||
};
|
||||
|
||||
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
|
|
@ -44,6 +44,24 @@ public:
|
|||
FieldPositionIteratorHandler getHandler(UErrorCode& status);
|
||||
void appendString(UnicodeString string, UErrorCode& status);
|
||||
|
||||
/**
|
||||
* Computes the spans for duplicated values.
|
||||
* For example, if the string has fields:
|
||||
*
|
||||
* ...aa..[b.cc]..d.[bb.e.c]..a..
|
||||
*
|
||||
* then the spans will be the bracketed regions.
|
||||
*
|
||||
* Assumes that the currently known fields are sorted
|
||||
* and all in the same category.
|
||||
*/
|
||||
void addOverlapSpans(UFieldCategory spanCategory, int8_t firstIndex, UErrorCode& status);
|
||||
|
||||
/**
|
||||
* Sorts the fields: start index first, length second.
|
||||
*/
|
||||
void sort();
|
||||
|
||||
private:
|
||||
UnicodeString fString;
|
||||
UVector32 fFields;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
// better dependency modularization.
|
||||
|
||||
#include "formattedval_impl.h"
|
||||
#include "putilimp.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
|
@ -82,6 +83,94 @@ void FormattedValueFieldPositionIteratorImpl::appendString(
|
|||
}
|
||||
|
||||
|
||||
void FormattedValueFieldPositionIteratorImpl::addOverlapSpans(
|
||||
UFieldCategory spanCategory,
|
||||
int8_t firstIndex,
|
||||
UErrorCode& status) {
|
||||
// In order to avoid fancy data structures, this is an O(N^2) algorithm,
|
||||
// which should be fine for all real-life applications of this function.
|
||||
int32_t s1a = INT32_MAX;
|
||||
int32_t s1b = 0;
|
||||
int32_t s2a = INT32_MAX;
|
||||
int32_t s2b = 0;
|
||||
int32_t numFields = fFields.size() / 4;
|
||||
for (int32_t i = 0; i<numFields; i++) {
|
||||
int32_t field1 = fFields.elementAti(i * 4 + 1);
|
||||
for (int32_t j = i + 1; j<numFields; j++) {
|
||||
int32_t field2 = fFields.elementAti(j * 4 + 1);
|
||||
if (field1 != field2) {
|
||||
continue;
|
||||
}
|
||||
// Found a duplicate
|
||||
s1a = uprv_min(s1a, fFields.elementAti(i * 4 + 2));
|
||||
s1b = uprv_max(s1b, fFields.elementAti(i * 4 + 3));
|
||||
s2a = uprv_min(s2a, fFields.elementAti(j * 4 + 2));
|
||||
s2b = uprv_max(s2b, fFields.elementAti(j * 4 + 3));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (s1a != INT32_MAX) {
|
||||
// Success: add the two span fields
|
||||
fFields.addElement(spanCategory, status);
|
||||
fFields.addElement(firstIndex, status);
|
||||
fFields.addElement(s1a, status);
|
||||
fFields.addElement(s1b, status);
|
||||
fFields.addElement(spanCategory, status);
|
||||
fFields.addElement(1 - firstIndex, status);
|
||||
fFields.addElement(s2a, status);
|
||||
fFields.addElement(s2b, status);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FormattedValueFieldPositionIteratorImpl::sort() {
|
||||
// Use bubble sort, O(N^2) but easy and no fancy data structures.
|
||||
int32_t numFields = fFields.size() / 4;
|
||||
while (true) {
|
||||
bool isSorted = true;
|
||||
for (int32_t i=0; i<numFields-1; i++) {
|
||||
int32_t categ1 = fFields.elementAti(i*4 + 0);
|
||||
int32_t field1 = fFields.elementAti(i*4 + 1);
|
||||
int32_t start1 = fFields.elementAti(i*4 + 2);
|
||||
int32_t limit1 = fFields.elementAti(i*4 + 3);
|
||||
int32_t categ2 = fFields.elementAti(i*4 + 4);
|
||||
int32_t field2 = fFields.elementAti(i*4 + 5);
|
||||
int32_t start2 = fFields.elementAti(i*4 + 6);
|
||||
int32_t limit2 = fFields.elementAti(i*4 + 7);
|
||||
int64_t comparison = 0;
|
||||
if (start1 != start2) {
|
||||
// Higher start index -> higher rank
|
||||
comparison = start2 - start1;
|
||||
} else if (limit1 != limit2) {
|
||||
// Higher length (end index) -> lower rank
|
||||
comparison = limit1 - limit2;
|
||||
} else if (categ1 != categ2) {
|
||||
// Higher field category -> lower rank
|
||||
comparison = categ1 - categ2;
|
||||
} else if (field1 != field2) {
|
||||
// Higher field -> higher rank
|
||||
comparison = field2 - field1;
|
||||
}
|
||||
if (comparison < 0) {
|
||||
// Perform a swap
|
||||
isSorted = false;
|
||||
fFields.setElementAt(categ2, i*4 + 0);
|
||||
fFields.setElementAt(field2, i*4 + 1);
|
||||
fFields.setElementAt(start2, i*4 + 2);
|
||||
fFields.setElementAt(limit2, i*4 + 3);
|
||||
fFields.setElementAt(categ1, i*4 + 4);
|
||||
fFields.setElementAt(field1, i*4 + 5);
|
||||
fFields.setElementAt(start1, i*4 + 6);
|
||||
fFields.setElementAt(limit1, i*4 + 7);
|
||||
}
|
||||
}
|
||||
if (isSorted) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
|
|
@ -38,7 +38,8 @@ FieldPositionOnlyHandler::~FieldPositionOnlyHandler() {
|
|||
|
||||
void
|
||||
FieldPositionOnlyHandler::addAttribute(int32_t id, int32_t start, int32_t limit) {
|
||||
if (pos.getField() == id) {
|
||||
if (pos.getField() == id && (!acceptFirstOnly || !seenFirst)) {
|
||||
seenFirst = TRUE;
|
||||
pos.setBeginIndex(start + fShift);
|
||||
pos.setEndIndex(limit + fShift);
|
||||
}
|
||||
|
@ -57,6 +58,10 @@ FieldPositionOnlyHandler::isRecording(void) const {
|
|||
return pos.getField() != FieldPosition::DONT_CARE;
|
||||
}
|
||||
|
||||
void FieldPositionOnlyHandler::setAcceptFirstOnly(UBool acceptFirstOnly) {
|
||||
this->acceptFirstOnly = acceptFirstOnly;
|
||||
}
|
||||
|
||||
|
||||
// utility subclass FieldPositionIteratorHandler
|
||||
|
||||
|
|
|
@ -41,6 +41,8 @@ class U_I18N_API FieldPositionHandler: public UMemory {
|
|||
|
||||
class FieldPositionOnlyHandler : public FieldPositionHandler {
|
||||
FieldPosition& pos;
|
||||
UBool acceptFirstOnly = FALSE;
|
||||
UBool seenFirst = FALSE;
|
||||
|
||||
public:
|
||||
FieldPositionOnlyHandler(FieldPosition& pos);
|
||||
|
@ -49,6 +51,13 @@ class FieldPositionOnlyHandler : public FieldPositionHandler {
|
|||
void addAttribute(int32_t id, int32_t start, int32_t limit) U_OVERRIDE;
|
||||
void shiftLast(int32_t delta) U_OVERRIDE;
|
||||
UBool isRecording(void) const U_OVERRIDE;
|
||||
|
||||
/**
|
||||
* Enable this option to lock in the FieldPosition value after seeing the
|
||||
* first occurrence of the field. The default behavior is to take the last
|
||||
* occurrence.
|
||||
*/
|
||||
void setAcceptFirstOnly(UBool acceptFirstOnly);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -18,10 +18,21 @@
|
|||
#include "unicode/timezone.h"
|
||||
#include "unicode/locid.h"
|
||||
#include "unicode/unistr.h"
|
||||
#include "formattedval_impl.h"
|
||||
|
||||
U_NAMESPACE_USE
|
||||
|
||||
|
||||
// Magic number: FDIV in ASCII
|
||||
UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL(
|
||||
FormattedDateInterval,
|
||||
UFormattedDateInterval,
|
||||
UFormattedDateIntervalImpl,
|
||||
UFormattedDateIntervalApiHelper,
|
||||
udtitvfmt,
|
||||
0x46444956)
|
||||
|
||||
|
||||
U_CAPI UDateIntervalFormat* U_EXPORT2
|
||||
udtitvfmt_open(const char* locale,
|
||||
const UChar* skeleton,
|
||||
|
@ -105,4 +116,21 @@ udtitvfmt_format(const UDateIntervalFormat* formatter,
|
|||
}
|
||||
|
||||
|
||||
U_DRAFT void U_EXPORT2
|
||||
udtitvfmt_formatToResult(
|
||||
const UDateIntervalFormat* formatter,
|
||||
UFormattedDateInterval* result,
|
||||
UDate fromDate,
|
||||
UDate toDate,
|
||||
UErrorCode* status) {
|
||||
if (U_FAILURE(*status)) {
|
||||
return;
|
||||
}
|
||||
auto* resultImpl = UFormattedDateIntervalApiHelper::validate(result, *status);
|
||||
DateInterval interval = DateInterval(fromDate,toDate);
|
||||
resultImpl->fImpl = reinterpret_cast<const DateIntervalFormat*>(formatter)
|
||||
->formatToValue(interval, *status);
|
||||
}
|
||||
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
|
|
@ -28,10 +28,85 @@
|
|||
#include "unicode/dtintrv.h"
|
||||
#include "unicode/dtitvinf.h"
|
||||
#include "unicode/dtptngen.h"
|
||||
#include "unicode/formattedvalue.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
|
||||
class FormattedDateIntervalData;
|
||||
class DateIntervalFormat;
|
||||
|
||||
#ifndef U_HIDE_DRAFT_API
|
||||
/**
|
||||
* An immutable class containing the result of a date interval formatting operation.
|
||||
*
|
||||
* Not intended for public subclassing.
|
||||
*
|
||||
* When calling nextPosition():
|
||||
* The fields are returned from left to right. The special field category
|
||||
* UFIELD_CATEGORY_DATE_INTERVAL_SPAN is used to indicate which datetime
|
||||
* primitives came from which arguments: 0 means fromCalendar, and 1 means
|
||||
* toCalendar. The span category will always occur before the
|
||||
* corresponding fields in UFIELD_CATEGORY_DATE
|
||||
* in the nextPosition() iterator.
|
||||
*
|
||||
* @draft ICU 64
|
||||
*/
|
||||
class U_I18N_API FormattedDateInterval : public UMemory, public FormattedValue {
|
||||
public:
|
||||
/**
|
||||
* Default constructor; makes an empty FormattedDateInterval.
|
||||
* @draft ICU 64
|
||||
*/
|
||||
FormattedDateInterval() : fData(nullptr), fErrorCode(U_INVALID_STATE_ERROR) {};
|
||||
|
||||
/**
|
||||
* Move constructor: Leaves the source FormattedDateInterval in an undefined state.
|
||||
* @draft ICU 64
|
||||
*/
|
||||
FormattedDateInterval(FormattedDateInterval&& src) U_NOEXCEPT;
|
||||
|
||||
/**
|
||||
* Destruct an instance of FormattedDateInterval.
|
||||
* @draft ICU 64
|
||||
*/
|
||||
virtual ~FormattedDateInterval() U_OVERRIDE;
|
||||
|
||||
/** Copying not supported; use move constructor instead. */
|
||||
FormattedDateInterval(const FormattedDateInterval&) = delete;
|
||||
|
||||
/** Copying not supported; use move assignment instead. */
|
||||
FormattedDateInterval& operator=(const FormattedDateInterval&) = delete;
|
||||
|
||||
/**
|
||||
* Move assignment: Leaves the source FormattedDateInterval in an undefined state.
|
||||
* @draft ICU 64
|
||||
*/
|
||||
FormattedDateInterval& operator=(FormattedDateInterval&& src) U_NOEXCEPT;
|
||||
|
||||
/** @copydoc FormattedValue::toString() */
|
||||
UnicodeString toString(UErrorCode& status) const U_OVERRIDE;
|
||||
|
||||
/** @copydoc FormattedValue::toTempString() */
|
||||
UnicodeString toTempString(UErrorCode& status) const U_OVERRIDE;
|
||||
|
||||
/** @copydoc FormattedValue::appendTo() */
|
||||
Appendable &appendTo(Appendable& appendable, UErrorCode& status) const U_OVERRIDE;
|
||||
|
||||
/** @copydoc FormattedValue::nextPosition() */
|
||||
UBool nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const U_OVERRIDE;
|
||||
|
||||
private:
|
||||
FormattedDateIntervalData *fData;
|
||||
UErrorCode fErrorCode;
|
||||
explicit FormattedDateInterval(FormattedDateIntervalData *results)
|
||||
: fData(results), fErrorCode(U_ZERO_ERROR) {};
|
||||
explicit FormattedDateInterval(UErrorCode errorCode)
|
||||
: fData(nullptr), fErrorCode(errorCode) {};
|
||||
friend class DateIntervalFormat;
|
||||
};
|
||||
#endif /* U_HIDE_DRAFT_API */
|
||||
|
||||
|
||||
/**
|
||||
* DateIntervalFormat is a class for formatting and parsing date
|
||||
|
@ -218,7 +293,6 @@ U_NAMESPACE_BEGIN
|
|||
* \endcode
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
class U_I18N_API DateIntervalFormat : public Format {
|
||||
public:
|
||||
|
||||
|
@ -425,6 +499,21 @@ public:
|
|||
FieldPosition& fieldPosition,
|
||||
UErrorCode& status) const ;
|
||||
|
||||
#ifndef U_HIDE_DRAFT_API
|
||||
/**
|
||||
* Format a DateInterval to produce a FormattedDateInterval.
|
||||
*
|
||||
* The FormattedDateInterval exposes field information about the formatted string.
|
||||
*
|
||||
* @param dtInterval DateInterval to be formatted.
|
||||
* @param status Set if an error occurs.
|
||||
* @return A FormattedDateInterval containing the format result.
|
||||
* @draft ICU 64
|
||||
*/
|
||||
FormattedDateInterval formatToValue(
|
||||
const DateInterval& dtInterval,
|
||||
UErrorCode& status) const;
|
||||
#endif /* U_HIDE_DRAFT_API */
|
||||
|
||||
/**
|
||||
* Format 2 Calendars to produce a string.
|
||||
|
@ -455,6 +544,29 @@ public:
|
|||
FieldPosition& fieldPosition,
|
||||
UErrorCode& status) const ;
|
||||
|
||||
#ifndef U_HIDE_DRAFT_API
|
||||
/**
|
||||
* Format 2 Calendars to produce a FormattedDateInterval.
|
||||
*
|
||||
* The FormattedDateInterval exposes field information about the formatted string.
|
||||
*
|
||||
* Note: "fromCalendar" and "toCalendar" are not const,
|
||||
* since calendar is not const in SimpleDateFormat::format(Calendar&),
|
||||
*
|
||||
* @param fromCalendar calendar set to the from date in date interval
|
||||
* to be formatted into date interval string
|
||||
* @param toCalendar calendar set to the to date in date interval
|
||||
* to be formatted into date interval string
|
||||
* @param status Set if an error occurs.
|
||||
* @return A FormattedDateInterval containing the format result.
|
||||
* @draft ICU 64
|
||||
*/
|
||||
FormattedDateInterval formatToValue(
|
||||
Calendar& fromCalendar,
|
||||
Calendar& toCalendar,
|
||||
UErrorCode& status) const;
|
||||
#endif /* U_HIDE_DRAFT_API */
|
||||
|
||||
/**
|
||||
* Date interval parsing is not supported. Please do not use.
|
||||
* <P>
|
||||
|
@ -664,28 +776,14 @@ private:
|
|||
* Below are for generating interval patterns local to the formatter
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provide an updated FieldPosition posResult based on two formats,
|
||||
* the FieldPosition values for each of them, and the pattern used
|
||||
* to combine them. The idea is for posResult to indicate the first
|
||||
* instance (if any) of the specified field in the combined result,
|
||||
* with correct offsets.
|
||||
*
|
||||
* @param combiningPattern Pattern used to combine pat0 and pat1
|
||||
* @param pat0 Formatted date/time value to replace {0}
|
||||
* @param pos0 FieldPosition within pat0
|
||||
* @param pat1 Formatted date/time value to replace {1}
|
||||
* @param pos1 FieldPosition within pat1
|
||||
* @param posResult FieldPosition to be set to the correct
|
||||
* position of the first field instance when
|
||||
* pat0 and pat1 are combined using combiningPattern
|
||||
*/
|
||||
static void
|
||||
adjustPosition(UnicodeString& combiningPattern, // has {0} and {1} in it
|
||||
UnicodeString& pat0, FieldPosition& pos0, // pattern and pos corresponding to {0}
|
||||
UnicodeString& pat1, FieldPosition& pos1, // pattern and pos corresponding to {1}
|
||||
FieldPosition& posResult);
|
||||
|
||||
/** Like fallbackFormat, but only formats the range part of the fallback. */
|
||||
void fallbackFormatRange(
|
||||
Calendar& fromCalendar,
|
||||
Calendar& toCalendar,
|
||||
UnicodeString& appendTo,
|
||||
int8_t& firstIndex,
|
||||
FieldPositionHandler& fphandler,
|
||||
UErrorCode& status) const;
|
||||
|
||||
/**
|
||||
* Format 2 Calendars using fall-back interval pattern
|
||||
|
@ -703,8 +801,8 @@ private:
|
|||
* (any difference is in ampm/hours or below)
|
||||
* @param appendTo Output parameter to receive result.
|
||||
* Result is appended to existing contents.
|
||||
* @param pos On input: an alignment field, if desired.
|
||||
* On output: the offsets of the alignment field.
|
||||
* @param firstIndex See formatImpl for more information.
|
||||
* @param fphandler See formatImpl for more information.
|
||||
* @param status output param set to success/failure code on exit
|
||||
* @return Reference to 'appendTo' parameter.
|
||||
* @internal (private)
|
||||
|
@ -713,7 +811,8 @@ private:
|
|||
Calendar& toCalendar,
|
||||
UBool fromToOnSameDay,
|
||||
UnicodeString& appendTo,
|
||||
FieldPosition& pos,
|
||||
int8_t& firstIndex,
|
||||
FieldPositionHandler& fphandler,
|
||||
UErrorCode& status) const;
|
||||
|
||||
|
||||
|
@ -977,11 +1076,11 @@ private:
|
|||
* to be formatted into date interval string
|
||||
* @param appendTo Output parameter to receive result.
|
||||
* Result is appended to existing contents.
|
||||
* @param fieldPosition On input: an alignment field, if desired.
|
||||
* On output: the offsets of the alignment field.
|
||||
* There may be multiple instances of a given field type
|
||||
* in an interval format; in this case the fieldPosition
|
||||
* offsets refer to the first instance.
|
||||
* @param firstIndex 0 if the first output date is fromCalendar;
|
||||
* 1 if it corresponds to toCalendar;
|
||||
* -1 if there is only one date printed.
|
||||
* @param fphandler Handler for field position information.
|
||||
* The fields will be from the UDateFormatField enum.
|
||||
* @param status Output param filled with success/failure status.
|
||||
* Caller needs to make sure it is SUCCESS
|
||||
* at the function entrance
|
||||
|
@ -991,9 +1090,17 @@ private:
|
|||
UnicodeString& formatImpl(Calendar& fromCalendar,
|
||||
Calendar& toCalendar,
|
||||
UnicodeString& appendTo,
|
||||
FieldPosition& fieldPosition,
|
||||
int8_t& firstIndex,
|
||||
FieldPositionHandler& fphandler,
|
||||
UErrorCode& status) const ;
|
||||
|
||||
/** Version of formatImpl for DateInterval. */
|
||||
UnicodeString& formatIntervalImpl(const DateInterval& dtInterval,
|
||||
UnicodeString& appendTo,
|
||||
int8_t& firstIndex,
|
||||
FieldPositionHandler& fphandler,
|
||||
UErrorCode& status) const;
|
||||
|
||||
|
||||
// from calendar field to pattern letter
|
||||
static const char16_t fgCalendarFieldToPatternLetter[];
|
||||
|
|
|
@ -149,7 +149,6 @@ U_NAMESPACE_BEGIN
|
|||
* calendar; non-Gregorian calendars are supported from ICU 4.4.1.
|
||||
* @stable ICU 4.0
|
||||
**/
|
||||
|
||||
class U_I18N_API DateIntervalInfo U_FINAL : public UObject {
|
||||
public:
|
||||
/**
|
||||
|
|
|
@ -49,6 +49,7 @@ class FieldPositionHandler;
|
|||
class TimeZoneFormat;
|
||||
class SharedNumberFormat;
|
||||
class SimpleDateFormatMutableNFs;
|
||||
class DateIntervalFormat;
|
||||
|
||||
namespace number {
|
||||
class LocalizedNumberFormatter;
|
||||
|
@ -1217,6 +1218,7 @@ public:
|
|||
|
||||
private:
|
||||
friend class DateFormat;
|
||||
friend class DateIntervalFormat;
|
||||
|
||||
void initializeDefaultCentury(void);
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "unicode/umisc.h"
|
||||
#include "unicode/localpointer.h"
|
||||
#include "unicode/uformattedvalue.h"
|
||||
|
||||
/**
|
||||
* \file
|
||||
|
@ -81,6 +82,15 @@
|
|||
struct UDateIntervalFormat;
|
||||
typedef struct UDateIntervalFormat UDateIntervalFormat; /**< C typedef for struct UDateIntervalFormat. @stable ICU 4.8 */
|
||||
|
||||
#ifndef U_HIDE_DRAFT_API
|
||||
struct UFormattedDateInterval;
|
||||
/**
|
||||
* Opaque struct to contain the results of a UDateIntervalFormat operation.
|
||||
* @draft ICU 64
|
||||
*/
|
||||
typedef struct UFormattedDateInterval UFormattedDateInterval;
|
||||
#endif /* U_HIDE_DRAFT_API */
|
||||
|
||||
/**
|
||||
* Open a new UDateIntervalFormat object using the predefined rules for a
|
||||
* given locale plus a specified skeleton.
|
||||
|
@ -123,6 +133,55 @@ U_STABLE void U_EXPORT2
|
|||
udtitvfmt_close(UDateIntervalFormat *formatter);
|
||||
|
||||
|
||||
#ifndef U_HIDE_DRAFT_API
|
||||
/**
|
||||
* Creates an object to hold the result of a UDateIntervalFormat
|
||||
* operation. The object can be used repeatedly; it is cleared whenever
|
||||
* passed to a format function.
|
||||
*
|
||||
* @param ec Set if an error occurs.
|
||||
* @return A pointer needing ownership.
|
||||
* @draft ICU 64
|
||||
*/
|
||||
U_CAPI UFormattedDateInterval* U_EXPORT2
|
||||
udtitvfmt_openResult(UErrorCode* ec);
|
||||
|
||||
/**
|
||||
* Returns a representation of a UFormattedDateInterval as a UFormattedValue,
|
||||
* which can be subsequently passed to any API requiring that type.
|
||||
*
|
||||
* The returned object is owned by the UFormattedDateInterval and is valid
|
||||
* only as long as the UFormattedDateInterval is present and unchanged in memory.
|
||||
*
|
||||
* You can think of this method as a cast between types.
|
||||
*
|
||||
* When calling ufmtval_nextPosition():
|
||||
* The fields are returned from left to right. The special field category
|
||||
* UFIELD_CATEGORY_DATE_INTERVAL_SPAN is used to indicate which datetime
|
||||
* primitives came from which arguments: 0 means fromCalendar, and 1 means
|
||||
* toCalendar. The span category will always occur before the
|
||||
* corresponding fields in UFIELD_CATEGORY_DATE
|
||||
* in the ufmtval_nextPosition() iterator.
|
||||
*
|
||||
* @param uresult The object containing the formatted string.
|
||||
* @param ec Set if an error occurs.
|
||||
* @return A UFormattedValue owned by the input object.
|
||||
* @draft ICU 64
|
||||
*/
|
||||
U_CAPI const UFormattedValue* U_EXPORT2
|
||||
udtitvfmt_resultAsValue(const UFormattedDateInterval* uresult, UErrorCode* ec);
|
||||
|
||||
/**
|
||||
* Releases the UFormattedDateInterval created by udtitvfmt_openResult().
|
||||
*
|
||||
* @param uresult The object to release.
|
||||
* @draft ICU 64
|
||||
*/
|
||||
U_CAPI void U_EXPORT2
|
||||
udtitvfmt_closeResult(UFormattedDateInterval* uresult);
|
||||
#endif /* U_HIDE_DRAFT_API */
|
||||
|
||||
|
||||
#if U_SHOW_CPLUSPLUS_API
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
@ -138,6 +197,19 @@ U_NAMESPACE_BEGIN
|
|||
*/
|
||||
U_DEFINE_LOCAL_OPEN_POINTER(LocalUDateIntervalFormatPointer, UDateIntervalFormat, udtitvfmt_close);
|
||||
|
||||
#ifndef U_HIDE_DRAFT_API
|
||||
/**
|
||||
* \class LocalUFormattedDateIntervalPointer
|
||||
* "Smart pointer" class, closes a UFormattedDateInterval via udtitvfmt_close().
|
||||
* For most methods see the LocalPointerBase base class.
|
||||
*
|
||||
* @see LocalPointerBase
|
||||
* @see LocalPointer
|
||||
* @draft ICU 64
|
||||
*/
|
||||
U_DEFINE_LOCAL_OPEN_POINTER(LocalUFormattedDateIntervalPointer, UFormattedDateInterval, udtitvfmt_closeResult);
|
||||
#endif /* U_HIDE_DRAFT_API */
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
@ -181,6 +253,34 @@ udtitvfmt_format(const UDateIntervalFormat* formatter,
|
|||
UFieldPosition* position,
|
||||
UErrorCode* status);
|
||||
|
||||
|
||||
#ifndef U_HIDE_DRAFT_API
|
||||
/**
|
||||
* Formats a date/time range using the conventions established for the
|
||||
* UDateIntervalFormat object.
|
||||
* @param formatter
|
||||
* The UDateIntervalFormat object specifying the format conventions.
|
||||
* @param result
|
||||
* The UFormattedDateInterval to contain the result of the
|
||||
* formatting operation.
|
||||
* @param fromDate
|
||||
* The starting point of the range.
|
||||
* @param toDate
|
||||
* The ending point of the range.
|
||||
* @param status
|
||||
* A pointer to a UErrorCode to receive any errors.
|
||||
* @draft ICU 64
|
||||
*/
|
||||
U_DRAFT void U_EXPORT2
|
||||
udtitvfmt_formatToResult(
|
||||
const UDateIntervalFormat* formatter,
|
||||
UFormattedDateInterval* result,
|
||||
UDate fromDate,
|
||||
UDate toDate,
|
||||
UErrorCode* status);
|
||||
#endif /* U_HIDE_DRAFT_API */
|
||||
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
||||
#endif
|
||||
|
|
|
@ -67,11 +67,25 @@ typedef enum UFieldCategory {
|
|||
*/
|
||||
UFIELD_CATEGORY_RELATIVE_DATETIME,
|
||||
|
||||
/**
|
||||
* Reserved for possible future fields in UDateIntervalFormatField.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
UFIELD_CATEGORY_DATE_INTERVAL,
|
||||
|
||||
#ifndef U_HIDE_INTERNAL_API
|
||||
/** @internal */
|
||||
UFIELD_CATEGORY_COUNT
|
||||
UFIELD_CATEGORY_COUNT,
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Category for spans in a date interval.
|
||||
*
|
||||
* @draft ICU 64
|
||||
*/
|
||||
UFIELD_CATEGORY_DATE_INTERVAL_SPAN = 0x1000 + UFIELD_CATEGORY_DATE_INTERVAL,
|
||||
|
||||
} UFieldCategory;
|
||||
|
||||
|
||||
|
|
|
@ -16,9 +16,11 @@
|
|||
#include "unicode/ustring.h"
|
||||
#include "cintltst.h"
|
||||
#include "cmemory.h"
|
||||
#include "cformtst.h"
|
||||
|
||||
static void TestDateIntervalFormat(void);
|
||||
static void TestFPos_SkelWithSeconds(void);
|
||||
static void TestFormatToResult(void);
|
||||
|
||||
void addDateIntervalFormatTest(TestNode** root);
|
||||
|
||||
|
@ -28,6 +30,7 @@ void addDateIntervalFormatTest(TestNode** root)
|
|||
{
|
||||
TESTCASE(TestDateIntervalFormat);
|
||||
TESTCASE(TestFPos_SkelWithSeconds);
|
||||
TESTCASE(TestFormatToResult);
|
||||
}
|
||||
|
||||
static const char tzUSPacific[] = "US/Pacific";
|
||||
|
@ -316,4 +319,65 @@ static void TestFPos_SkelWithSeconds()
|
|||
}
|
||||
}
|
||||
|
||||
static void TestFormatToResult() {
|
||||
UErrorCode ec = U_ZERO_ERROR;
|
||||
UDateIntervalFormat* fmt = udtitvfmt_open("de", u"dMMMMyHHmm", -1, zoneGMT, -1, &ec);
|
||||
UFormattedDateInterval* fdi = udtitvfmt_openResult(&ec);
|
||||
assertSuccess("Opening", &ec);
|
||||
|
||||
{
|
||||
const char* message = "Field position test 1";
|
||||
const UChar* expectedString = u"27. September 2010, 15:00 – 2. März 2011, 18:30";
|
||||
udtitvfmt_formatToResult(fmt, fdi, Date201009270800, Date201103021030, &ec);
|
||||
assertSuccess("Formatting", &ec);
|
||||
static const UFieldPositionWithCategory expectedFieldPositions[] = {
|
||||
// category, field, begin index, end index
|
||||
{UFIELD_CATEGORY_DATE_INTERVAL_SPAN, 0, 0, 25},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_DATE_FIELD, 0, 2},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_MONTH_FIELD, 4, 13},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_YEAR_FIELD, 14, 18},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_HOUR_OF_DAY0_FIELD, 20, 22},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_MINUTE_FIELD, 23, 25},
|
||||
{UFIELD_CATEGORY_DATE_INTERVAL_SPAN, 1, 28, 47},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_DATE_FIELD, 28, 29},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_MONTH_FIELD, 31, 35},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_YEAR_FIELD, 36, 40},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_HOUR_OF_DAY0_FIELD, 42, 44},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_MINUTE_FIELD, 45, 47}};
|
||||
checkMixedFormattedValue(
|
||||
message,
|
||||
udtitvfmt_resultAsValue(fdi, &ec),
|
||||
expectedString,
|
||||
expectedFieldPositions,
|
||||
UPRV_LENGTHOF(expectedFieldPositions));
|
||||
}
|
||||
{
|
||||
const char* message = "Field position test 1";
|
||||
const UChar* expectedString = u"27. September 2010, 15:00–22:00 Uhr";
|
||||
udtitvfmt_formatToResult(fmt, fdi, Date201009270800, Date201009270800 + 7*_HOUR, &ec);
|
||||
assertSuccess("Formatting", &ec);
|
||||
static const UFieldPositionWithCategory expectedFieldPositions[] = {
|
||||
// category, field, begin index, end index
|
||||
{UFIELD_CATEGORY_DATE, UDAT_DATE_FIELD, 0, 2},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_MONTH_FIELD, 4, 13},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_YEAR_FIELD, 14, 18},
|
||||
{UFIELD_CATEGORY_DATE_INTERVAL_SPAN, 0, 20, 25},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_HOUR_OF_DAY0_FIELD, 20, 22},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_MINUTE_FIELD, 23, 25},
|
||||
{UFIELD_CATEGORY_DATE_INTERVAL_SPAN, 1, 26, 31},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_HOUR_OF_DAY0_FIELD, 26, 28},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_MINUTE_FIELD, 29, 31},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_AM_PM_FIELD, 32, 35}};
|
||||
checkMixedFormattedValue(
|
||||
message,
|
||||
udtitvfmt_resultAsValue(fdi, &ec),
|
||||
expectedString,
|
||||
expectedFieldPositions,
|
||||
UPRV_LENGTHOF(expectedFieldPositions));
|
||||
}
|
||||
|
||||
udtitvfmt_close(fmt);
|
||||
udtitvfmt_closeResult(fdi);
|
||||
}
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
|
|
@ -55,6 +55,7 @@ void DateIntervalFormatTest::runIndexedTest( int32_t index, UBool exec, const ch
|
|||
TESTCASE(7, testTicket11985);
|
||||
TESTCASE(8, testTicket11669);
|
||||
TESTCASE(9, testTicket12065);
|
||||
TESTCASE(10, testFormattedDateInterval);
|
||||
default: name = ""; break;
|
||||
}
|
||||
}
|
||||
|
@ -1620,4 +1621,67 @@ void DateIntervalFormatTest::testTicket12065() {
|
|||
}
|
||||
|
||||
|
||||
void DateIntervalFormatTest::testFormattedDateInterval() {
|
||||
IcuTestErrorCode status(*this, "testFormattedDateInterval");
|
||||
LocalPointer<DateIntervalFormat> fmt(DateIntervalFormat::createInstance(u"dMMMMy", "en-US", status), status);
|
||||
|
||||
{
|
||||
const char16_t* message = u"FormattedDateInterval test 1";
|
||||
const char16_t* expectedString = u"July 20 \u2013 25, 2018";
|
||||
LocalPointer<Calendar> input1(Calendar::createInstance("en-GB", status));
|
||||
LocalPointer<Calendar> input2(Calendar::createInstance("en-GB", status));
|
||||
input1->set(2018, 6, 20);
|
||||
input2->set(2018, 6, 25);
|
||||
FormattedDateInterval result = fmt->formatToValue(*input1, *input2, status);
|
||||
static const UFieldPositionWithCategory expectedFieldPositions[] = {
|
||||
// field, begin index, end index
|
||||
{UFIELD_CATEGORY_DATE, UDAT_MONTH_FIELD, 0, 4},
|
||||
{UFIELD_CATEGORY_DATE_INTERVAL_SPAN, 0, 5, 7},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_DATE_FIELD, 5, 7},
|
||||
{UFIELD_CATEGORY_DATE_INTERVAL_SPAN, 1, 10, 12},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_DATE_FIELD, 10, 12},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_YEAR_FIELD, 14, 18}};
|
||||
checkMixedFormattedValue(
|
||||
message,
|
||||
result,
|
||||
expectedString,
|
||||
expectedFieldPositions,
|
||||
UPRV_LENGTHOF(expectedFieldPositions));
|
||||
}
|
||||
|
||||
// To test the fallback pattern behavior, make a custom DateIntervalInfo.
|
||||
DateIntervalInfo dtitvinf(status);
|
||||
dtitvinf.setFallbackIntervalPattern("<< {1} --- {0} >>", status);
|
||||
fmt.adoptInsteadAndCheckErrorCode(
|
||||
DateIntervalFormat::createInstance(u"dMMMMy", "en-US", dtitvinf, status),
|
||||
status);
|
||||
|
||||
{
|
||||
const char16_t* message = u"FormattedDateInterval with fallback format test 1";
|
||||
const char16_t* expectedString = u"<< July 25, 2018 --- July 20, 2018 >>";
|
||||
LocalPointer<Calendar> input1(Calendar::createInstance("en-GB", status));
|
||||
LocalPointer<Calendar> input2(Calendar::createInstance("en-GB", status));
|
||||
input1->set(2018, 6, 20);
|
||||
input2->set(2018, 6, 25);
|
||||
FormattedDateInterval result = fmt->formatToValue(*input1, *input2, status);
|
||||
static const UFieldPositionWithCategory expectedFieldPositions[] = {
|
||||
// field, begin index, end index
|
||||
{UFIELD_CATEGORY_DATE_INTERVAL_SPAN, 1, 3, 16},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_MONTH_FIELD, 3, 7},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_DATE_FIELD, 8, 10},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_YEAR_FIELD, 12, 16},
|
||||
{UFIELD_CATEGORY_DATE_INTERVAL_SPAN, 0, 21, 34},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_MONTH_FIELD, 21, 25},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_DATE_FIELD, 26, 28},
|
||||
{UFIELD_CATEGORY_DATE, UDAT_YEAR_FIELD, 30, 34}};
|
||||
checkMixedFormattedValue(
|
||||
message,
|
||||
result,
|
||||
expectedString,
|
||||
expectedFieldPositions,
|
||||
UPRV_LENGTHOF(expectedFieldPositions));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
|
|
@ -15,11 +15,12 @@
|
|||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#include "intltest.h"
|
||||
#include "itformat.h"
|
||||
|
||||
/**
|
||||
* Test basic functionality of various API functions
|
||||
**/
|
||||
class DateIntervalFormatTest: public IntlTest {
|
||||
class DateIntervalFormatTest: public IntlTestWithFieldPosition {
|
||||
void runIndexedTest( int32_t index, UBool exec, const char* &name, char* par = NULL );
|
||||
|
||||
public:
|
||||
|
@ -63,6 +64,8 @@ public:
|
|||
|
||||
void testTicket12065();
|
||||
|
||||
void testFormattedDateInterval();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Test formatting against expected result
|
||||
|
|
|
@ -329,10 +329,21 @@ void SimpleFormatterTest::TestBadArguments() {
|
|||
}
|
||||
|
||||
void SimpleFormatterTest::TestTextWithNoArguments() {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
IcuTestErrorCode status(*this, "TestTextWithNoArguments");
|
||||
SimpleFormatter fmt("{0} has no {1} arguments.", status);
|
||||
assertEquals(
|
||||
"", " has no arguments.", fmt.getTextWithNoArguments());
|
||||
assertEquals("String output 1",
|
||||
" has no arguments.", fmt.getTextWithNoArguments());
|
||||
|
||||
// Test offset positions
|
||||
int32_t offsets[3];
|
||||
assertEquals("String output 2",
|
||||
u" has no arguments.", fmt.getTextWithNoArguments(offsets, 3));
|
||||
assertEquals("Offset at 0",
|
||||
0, offsets[0]);
|
||||
assertEquals("Offset at 1",
|
||||
8, offsets[1]);
|
||||
assertEquals("Offset at 2",
|
||||
-1, offsets[2]);
|
||||
}
|
||||
|
||||
void SimpleFormatterTest::TestFormatReplaceNoOptimization() {
|
||||
|
|
Loading…
Add table
Reference in a new issue