* getBestPattern which takes optional skip matcher
*/
private String getBestPattern(String skeleton, DateTimeMatcher skipMatcher, int options) {
- //if (!isComplete) complete();
- // if skeleton contains meta hour field 'j', then
- // replace it with the default hour format char
- skeleton = skeleton.replaceAll("j", String.valueOf(defaultHourFormatChar));
+ int flags = DTPG_FLAGS_NONE;
+ // Replace hour metacharacters 'j' and 'J', set flags as necessary
+ StringBuilder skeletonCopy = new StringBuilder(skeleton);
+ boolean inQuoted = false;
+ for (int patPos = 0; patPos < skeletonCopy.length(); patPos++) {
+ char patChr = skeletonCopy.charAt(patPos);
+ if (patChr == '\'') {
+ inQuoted = !inQuoted;
+ } else if (!inQuoted) {
+ if (patChr == 'j') {
+ skeletonCopy.setCharAt(patPos, defaultHourFormatChar);
+ } else if (patChr == 'J') {
+ // Get pattern for skeleton with H, then (in adjustFieldTypes)
+ // replace H or k with defaultHourFormatChar
+ skeletonCopy.setCharAt(patPos, 'H');
+ flags |= DTPG_FLAGS_SKELETON_USES_CAP_J;
+ }
+ }
+ }
String datePattern, timePattern;
synchronized(this) {
- current.set(skeleton, fp, false);
+ current.set(skeletonCopy.toString(), fp, false);
PatternWithMatcher bestWithMatcher = getBestRaw(current, -1, _distanceInfo, skipMatcher);
if (_distanceInfo.missingFieldMask == 0 && _distanceInfo.extraFieldMask == 0) {
// we have a good item. Adjust the field types
- return adjustFieldTypes(bestWithMatcher, current, false, options);
+ return adjustFieldTypes(bestWithMatcher, current, flags, options);
}
int neededFields = current.getFieldMask();
// otherwise break up by date and time.
- datePattern = getBestAppending(current, neededFields & DATE_MASK, _distanceInfo, skipMatcher, options);
- timePattern = getBestAppending(current, neededFields & TIME_MASK, _distanceInfo, skipMatcher, options);
+ datePattern = getBestAppending(current, neededFields & DATE_MASK, _distanceInfo, skipMatcher, flags, options);
+ timePattern = getBestAppending(current, neededFields & TIME_MASK, _distanceInfo, skipMatcher, flags, options);
}
if (datePattern == null) return timePattern == null ? "" : timePattern;
public String replaceFieldTypes(String pattern, String skeleton, int options) {
synchronized (this) { // synchronized since a getter must be thread-safe
PatternWithMatcher patternNoMatcher = new PatternWithMatcher(pattern, null);
- return adjustFieldTypes(patternNoMatcher, current.set(skeleton, fp, false), false, options);
+ return adjustFieldTypes(patternNoMatcher, current.set(skeleton, fp, false), DTPG_FLAGS_NONE, options);
}
}
* We only get called here if we failed to find an exact skeleton. We have broken it into date + time, and look for the pieces.
* If we fail to find a complete skeleton, we compose in a loop until we have all the fields.
*/
- private String getBestAppending(DateTimeMatcher source, int missingFields, DistanceInfo distInfo, DateTimeMatcher skipMatcher, int options) {
+ private String getBestAppending(DateTimeMatcher source, int missingFields, DistanceInfo distInfo, DateTimeMatcher skipMatcher, int flags, int options) {
String resultPattern = null;
if (missingFields != 0) {
PatternWithMatcher resultPatternWithMatcher = getBestRaw(source, missingFields, distInfo, skipMatcher);
- resultPattern = adjustFieldTypes(resultPatternWithMatcher, source, false, options);
+ resultPattern = adjustFieldTypes(resultPatternWithMatcher, source, flags, options);
while (distInfo.missingFieldMask != 0) { // precondition: EVERY single field must work!
if ((distInfo.missingFieldMask & SECOND_AND_FRACTIONAL_MASK) == FRACTIONAL_MASK
&& (missingFields & SECOND_AND_FRACTIONAL_MASK) == SECOND_AND_FRACTIONAL_MASK) {
resultPatternWithMatcher.pattern = resultPattern;
- resultPattern = adjustFieldTypes(resultPatternWithMatcher, source, true, options);
+ resultPattern = adjustFieldTypes(resultPatternWithMatcher, source, flags | DTPG_FLAGS_FIX_FRACTIONAL_SECONDS, options);
distInfo.missingFieldMask &= ~FRACTIONAL_MASK; // remove bit
continue;
}
int startingMask = distInfo.missingFieldMask;
PatternWithMatcher tempWithMatcher = getBestRaw(source, distInfo.missingFieldMask, distInfo, skipMatcher);
- String temp = adjustFieldTypes(tempWithMatcher, source, false, options);
+ String temp = adjustFieldTypes(tempWithMatcher, source, flags, options);
int foundMask = startingMask & ~distInfo.missingFieldMask;
int topField = getTopBitNumber(foundMask);
resultPattern = MessageFormat.format(getAppendFormat(topField), new Object[]{resultPattern, temp, getAppendName(topField)});
* @param fixFractionalSeconds TODO
*
*/
- private String adjustFieldTypes(PatternWithMatcher patternWithMatcher, DateTimeMatcher inputRequest, boolean fixFractionalSeconds, int options) {
+ // flags values
+ private static final int DTPG_FLAGS_NONE = 0;
+ private static final int DTPG_FLAGS_FIX_FRACTIONAL_SECONDS = 1;
+ private static final int DTPG_FLAGS_SKELETON_USES_CAP_J = 2;
+
+ private String adjustFieldTypes(PatternWithMatcher patternWithMatcher, DateTimeMatcher inputRequest, int flags, int options) {
fp.set(patternWithMatcher.pattern);
StringBuilder newPattern = new StringBuilder();
for (Object item : fp.getItems()) {
// int type = types[canonicalIndex][1];
int type = variableField.getType();
- if (fixFractionalSeconds && type == SECOND) {
+ if ((flags & DTPG_FLAGS_FIX_FRACTIONAL_SECONDS) != 0 && type == SECOND) {
String newField = inputRequest.original[FRACTIONAL_SECOND];
fieldBuilder.append(decimal);
fieldBuilder.append(newField);
}
char c = (type != HOUR && type != MONTH && type != WEEKDAY && (type != YEAR || reqField.charAt(0)=='Y'))?
reqField.charAt(0): fieldBuilder.charAt(0);
+ if (type == HOUR && (flags & DTPG_FLAGS_SKELETON_USES_CAP_J) != 0) {
+ c = defaultHourFormatChar;
+ }
fieldBuilder = new StringBuilder();
for (int i = adjFieldLen; i > 0; --i) fieldBuilder.append(c);
}
// can be generated by using GENERATE_TEST_DATA. Must be reviewed before adding
static final Object[] dateTestData = {
new Date(916300739123L), // 1999-01-13T23:58:59.123,0-0800
+
new ULocale("en_US"),
new String[] {"yM", "1/1999"},
new String[] {"yMMM", "Jan 1999"},
new String[] {"MMMEd", "Wed, Jan 13"},
new String[] {"Ed", "13 Wed"},
new String[] {"jmmssSSS", "11:58:59.123 PM"},
+ new String[] {"JJmm", "11:58"},
+
new ULocale("en_US@calendar=japanese"), // (new locale for testing ticket 6872<-5702)
new String[] {"yM", "1/11 H"},
new String[] {"yMMM", "Jan 11 Heisei"},
new String[] {"MMMEd", "Wed, Jan 13"},
new String[] {"Ed", "13 Wed"},
new String[] {"jmmssSSS", "11:58:59.123 PM"},
+ new String[] {"JJmm", "11:58"},
+
new ULocale("de_DE"),
new String[] {"yM", "1.1999"},
new String[] {"yMMM", "Jan. 1999"},
new String[] {"MMMEd", "Mi., 13. Jan."},
new String[] {"Ed", "Mi., 13."},
new String[] {"jmmssSSS", "23:58:59,123"},
+ new String[] {"JJmm", "23:58"},
+
new ULocale("fi"),
new String[] {"yM", "1.1999"}, // (fixed expected result per ticket 6872<-6626)
new String[] {"yMMM", "tammi 1999"}, // (fixed expected result per ticket 6872<-7007)
new String[] {"MMMEd", "ke 13. tammikuuta"},
new String[] {"Ed", "ke 13."},
new String[] {"jmmssSSS", "23.58.59,123"},
+ new String[] {"JJmm", "23.58"},
+
new ULocale("es"),
new String[] {"yM", "1/1999"},
new String[] {"yMMM", "ene. de 1999"},
new String[] {"MMMEd", "mi\u00E9. 13 de ene."},
new String[] {"Ed", "mi\u00E9. 13"},
new String[] {"jmmssSSS", "23:58:59,123"},
+ new String[] {"JJmm", "23:58"},
+
new ULocale("ja"), // (new locale for testing ticket 6872<-6626)
new String[] {"yM", "1999/1"},
new String[] {"yMMM", "1999\u5E741\u6708"},
new String[] {"MMMEd", "1\u670813\u65E5(\u6C34)"},
new String[] {"Ed", "13\u65E5(\u6C34)"},
new String[] {"jmmssSSS", "23:58:59.123"},
+ new String[] {"JJmm", "23:58"},
+
new ULocale("ja@calendar=japanese"), // (new locale for testing ticket 6872<-5702)
new String[] {"yM", "\u5E73\u621011/1"},
new String[] {"yMMM", "\u5E73\u621011\u5E741\u6708"},
new String[] {"MMMEd", "1\u670813\u65E5(\u6C34)"},
new String[] {"Ed", "13\u65E5(\u6C34)"},
new String[] {"jmmssSSS", "23:58:59.123"},
+ new String[] {"JJmm", "23:58"},
+
new ULocale("zh_Hans_CN"),
new String[] {"yM", "1999/1"},
new String[] {"yMMM", "1999\u5E741\u6708"}, // (fixed expected result per ticket 6872<-6626)
new String[] {"MMMEd", "1\u670813\u65E5\u5468\u4E09"},
new String[] {"Ed", "13\u65E5\u5468\u4E09"},
new String[] {"jmmssSSS", "\u4E0B\u534811:58:59.123"},
+ new String[] {"JJmm", "11:58"},
+
new ULocale("zh_TW@calendar=roc"), // (new locale for testing ticket 6872<-5702)
new String[] {"yM", "\u6C11\u570B88/1"},
new String[] {"yMMM", "\u6C11\u570B88\u5E741\u6708"},
new String[] {"MMMEd", "1\u670813\u65E5\u9031\u4E09"},
new String[] {"Ed", "13\u65E5\uFF08\u9031\u4E09\uFF09"},
new String[] {"jmmssSSS", "\u4E0B\u534811:58:59.123"},
+ new String[] {"JJmm", "11:58"},
+
new ULocale("ru"),
new String[] {"yM", "01.1999"},
new String[] {"yMMM", "\u042F\u043D\u0432. 1999"},
new String[] {"MMMEd", "\u0421\u0440, 13 \u044F\u043D\u0432."},
new String[] {"Ed", "\u0421\u0440, 13"},
new String[] {"jmmssSSS", "23:58:59,123"},
+ new String[] {"JJmm", "23:58"},
new ULocale("zh@calendar=chinese"),
new String[] {"yM", "\u620A\u5BC5\u5E7411\u6708"},
new String[] {"MMMEd", "\u5341\u4E00\u670826\u65E5\u5468\u4E09"},
new String[] {"Ed", "26\u65E5\u5468\u4E09"},
new String[] {"jmmssSSS", "\u4E0B\u534811:58:59.123"},
+ new String[] {"JJmm", "11:58"},
};
public void DayMonthTest() {