--- old/src/share/classes/java/text/SimpleDateFormat.java Mon Aug 13 12:03:05 2012
+++ new/src/share/classes/java/text/SimpleDateFormat.java Mon Aug 13 12:03:03 2012
@@ -41,23 +41,20 @@
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
+import static java.text.DateFormatSymbols.*;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.Map;
-import java.util.MissingResourceException;
-import java.util.ResourceBundle;
import java.util.SimpleTimeZone;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import sun.util.locale.provider.LocaleProviderAdapter;
import sun.util.calendar.CalendarUtils;
import sun.util.calendar.ZoneInfoFile;
-import sun.util.resources.LocaleData;
-import static java.text.DateFormatSymbols.*;
-
/**
* SimpleDateFormat
is a concrete class for formatting and
* parsing dates in a locale-sensitive manner. It allows for formatting
@@ -117,95 +114,100 @@
*
* X
* | Time zone
* | ISO 8601 time zone
@@ -270,7 +272,17 @@
* Month:
* If the number of pattern letters is 3 or more, the month is
* interpreted as text; otherwise,
- * it is interpreted as a number.
+ * it is interpreted as a number.
+ *
+ * - Letter M produces context-sensitive month names, such as the
+ * embedded form of names. If a {@code DateFormatSymbols} has been set
+ * explicitly with constructor {@link #SimpleDateFormat(String,
+ * DateFormatSymbols)} or method {@link
+ * #setDateFormatSymbols(DateFormatSymbols)}, the month names given by
+ * the {@code DateFormatSymbols} are used.
+ * - Letter L produces the standalone form of month names.
+ *
+ *
* General time zone:
* Time zones are interpreted as text if they have
* names. For time zones representing a GMT offset value, the
@@ -459,6 +471,11 @@
transient private boolean hasFollowingMinusSign = false;
/**
+ * True if standalone form needs to be used.
+ */
+ transient private boolean forceStandaloneForm = false;
+
+ /**
* The compiled pattern.
*/
transient private char[] compiledPattern;
@@ -502,16 +519,10 @@
private static final String GMT = "GMT";
/**
- * Cache to hold the DateTimePatterns of a Locale.
- */
- private static final ConcurrentMap cachedLocaleData
- = new ConcurrentHashMap(3);
-
- /**
* Cache NumberFormat instances with Locale key.
*/
private static final ConcurrentMap cachedNumberFormatData
- = new ConcurrentHashMap(3);
+ = new ConcurrentHashMap<>(3);
/**
* The Locale used to instantiate this
@@ -541,7 +552,9 @@
* class.
*/
public SimpleDateFormat() {
- this(SHORT, SHORT, Locale.getDefault(Locale.Category.FORMAT));
+ this("", Locale.getDefault(Locale.Category.FORMAT));
+ applyPatternImpl(LocaleProviderAdapter.getResourceBundleBased().getLocaleResources(locale)
+ .getDateTimePattern(SHORT, SHORT, calendar));
}
/**
@@ -608,51 +621,6 @@
useDateFormatSymbols = true;
}
- /* Package-private, called by DateFormat factory methods */
- SimpleDateFormat(int timeStyle, int dateStyle, Locale loc) {
- if (loc == null) {
- throw new NullPointerException();
- }
-
- this.locale = loc;
- // initialize calendar and related fields
- initializeCalendar(loc);
-
- /* try the cache first */
- String[] dateTimePatterns = cachedLocaleData.get(loc);
- if (dateTimePatterns == null) { /* cache miss */
- ResourceBundle r = LocaleData.getDateFormatData(loc);
- if (!isGregorianCalendar()) {
- try {
- dateTimePatterns = r.getStringArray(getCalendarName() + ".DateTimePatterns");
- } catch (MissingResourceException e) {
- }
- }
- if (dateTimePatterns == null) {
- dateTimePatterns = r.getStringArray("DateTimePatterns");
- }
- /* update cache */
- cachedLocaleData.putIfAbsent(loc, dateTimePatterns);
- }
- formatData = DateFormatSymbols.getInstanceRef(loc);
- if ((timeStyle >= 0) && (dateStyle >= 0)) {
- Object[] dateTimeArgs = {dateTimePatterns[timeStyle],
- dateTimePatterns[dateStyle + 4]};
- pattern = MessageFormat.format(dateTimePatterns[8], dateTimeArgs);
- }
- else if (timeStyle >= 0) {
- pattern = dateTimePatterns[timeStyle];
- }
- else if (dateStyle >= 0) {
- pattern = dateTimePatterns[dateStyle + 4];
- }
- else {
- throw new IllegalArgumentException("No date or time style specified");
- }
-
- initialize(loc);
- }
-
/* Initialize compiledPattern and numberFormat fields */
private void initialize(Locale loc) {
// Verify and compile the given pattern.
@@ -750,10 +718,10 @@
private char[] compile(String pattern) {
int length = pattern.length();
boolean inQuote = false;
- StringBuilder compiledPattern = new StringBuilder(length * 2);
+ StringBuilder compiledCode = new StringBuilder(length * 2);
StringBuilder tmpBuffer = null;
- int count = 0;
- int lastTag = -1;
+ int count = 0, tagcount = 0;
+ int lastTag = -1, prevTag = -1;
for (int i = 0; i < length; i++) {
char c = pattern.charAt(i);
@@ -766,7 +734,9 @@
if (c == '\'') {
i++;
if (count != 0) {
- encode(lastTag, count, compiledPattern);
+ encode(lastTag, count, compiledCode);
+ tagcount++;
+ prevTag = lastTag;
lastTag = -1;
count = 0;
}
@@ -773,7 +743,7 @@
if (inQuote) {
tmpBuffer.append(c);
} else {
- compiledPattern.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | c));
+ compiledCode.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | c));
}
continue;
}
@@ -780,7 +750,9 @@
}
if (!inQuote) {
if (count != 0) {
- encode(lastTag, count, compiledPattern);
+ encode(lastTag, count, compiledCode);
+ tagcount++;
+ prevTag = lastTag;
lastTag = -1;
count = 0;
}
@@ -795,14 +767,14 @@
if (len == 1) {
char ch = tmpBuffer.charAt(0);
if (ch < 128) {
- compiledPattern.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | ch));
+ compiledCode.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | ch));
} else {
- compiledPattern.append((char)(TAG_QUOTE_CHARS << 8 | 1));
- compiledPattern.append(ch);
+ compiledCode.append((char)(TAG_QUOTE_CHARS << 8 | 1));
+ compiledCode.append(ch);
}
} else {
- encode(TAG_QUOTE_CHARS, len, compiledPattern);
- compiledPattern.append(tmpBuffer);
+ encode(TAG_QUOTE_CHARS, len, compiledCode);
+ compiledCode.append(tmpBuffer);
}
inQuote = false;
}
@@ -814,13 +786,15 @@
}
if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) {
if (count != 0) {
- encode(lastTag, count, compiledPattern);
+ encode(lastTag, count, compiledCode);
+ tagcount++;
+ prevTag = lastTag;
lastTag = -1;
count = 0;
}
if (c < 128) {
// In most cases, c would be a delimiter, such as ':'.
- compiledPattern.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | c));
+ compiledCode.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | c));
} else {
// Take any contiguous non-ASCII alphabet characters and
// put them in a single TAG_QUOTE_CHARS.
@@ -831,9 +805,9 @@
break;
}
}
- compiledPattern.append((char)(TAG_QUOTE_CHARS << 8 | (j - i)));
+ compiledCode.append((char)(TAG_QUOTE_CHARS << 8 | (j - i)));
for (; i < j; i++) {
- compiledPattern.append(pattern.charAt(i));
+ compiledCode.append(pattern.charAt(i));
}
i--;
}
@@ -850,7 +824,9 @@
count++;
continue;
}
- encode(lastTag, count, compiledPattern);
+ encode(lastTag, count, compiledCode);
+ tagcount++;
+ prevTag = lastTag;
lastTag = tag;
count = 1;
}
@@ -860,13 +836,17 @@
}
if (count != 0) {
- encode(lastTag, count, compiledPattern);
+ encode(lastTag, count, compiledCode);
+ tagcount++;
+ prevTag = lastTag;
}
+ forceStandaloneForm = (tagcount == 1 && prevTag == PATTERN_MONTH);
+
// Copy the compiled pattern to a char array
- int len = compiledPattern.length();
+ int len = compiledCode.length();
char[] r = new char[len];
- compiledPattern.getChars(0, len, r, 0);
+ compiledCode.getChars(0, len, r, 0);
return r;
}
@@ -873,7 +853,7 @@
/**
* Encodes the given tag and length and puts encoded char(s) into buffer.
*/
- private static final void encode(int tag, int length, StringBuilder buffer) {
+ private static void encode(int tag, int length, StringBuilder buffer) {
if (tag == PATTERN_ISO_ZONE && length >= 4) {
throw new IllegalArgumentException("invalid ISO 8601 format: length=" + length);
}
@@ -941,6 +921,7 @@
* @return the formatted date-time string.
* @exception NullPointerException if the given {@code date} is {@code null}.
*/
+ @Override
public StringBuffer format(Date date, StringBuffer toAppendTo,
FieldPosition pos)
{
@@ -999,6 +980,7 @@
* @return AttributedCharacterIterator describing the formatted value.
* @since 1.4
*/
+ @Override
public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
StringBuffer sb = new StringBuffer();
CharacterIteratorFieldDelegate delegate = new
@@ -1022,47 +1004,84 @@
}
// Map index into pattern character string to Calendar field number
- private static final int[] PATTERN_INDEX_TO_CALENDAR_FIELD =
- {
- Calendar.ERA, Calendar.YEAR, Calendar.MONTH, Calendar.DATE,
- Calendar.HOUR_OF_DAY, Calendar.HOUR_OF_DAY, Calendar.MINUTE,
- Calendar.SECOND, Calendar.MILLISECOND, Calendar.DAY_OF_WEEK,
- Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK_IN_MONTH,
- Calendar.WEEK_OF_YEAR, Calendar.WEEK_OF_MONTH,
- Calendar.AM_PM, Calendar.HOUR, Calendar.HOUR, Calendar.ZONE_OFFSET,
+ private static final int[] PATTERN_INDEX_TO_CALENDAR_FIELD = {
+ Calendar.ERA,
+ Calendar.YEAR,
+ Calendar.MONTH,
+ Calendar.DATE,
+ Calendar.HOUR_OF_DAY,
+ Calendar.HOUR_OF_DAY,
+ Calendar.MINUTE,
+ Calendar.SECOND,
+ Calendar.MILLISECOND,
+ Calendar.DAY_OF_WEEK,
+ Calendar.DAY_OF_YEAR,
+ Calendar.DAY_OF_WEEK_IN_MONTH,
+ Calendar.WEEK_OF_YEAR,
+ Calendar.WEEK_OF_MONTH,
+ Calendar.AM_PM,
+ Calendar.HOUR,
+ Calendar.HOUR,
Calendar.ZONE_OFFSET,
- // Pseudo Calendar fields
- CalendarBuilder.WEEK_YEAR,
- CalendarBuilder.ISO_DAY_OF_WEEK,
- Calendar.ZONE_OFFSET
+ Calendar.ZONE_OFFSET,
+ CalendarBuilder.WEEK_YEAR, // Pseudo Calendar field
+ CalendarBuilder.ISO_DAY_OF_WEEK, // Pseudo Calendar field
+ Calendar.ZONE_OFFSET,
+ Calendar.MONTH
};
// Map index into pattern character string to DateFormat field number
private static final int[] PATTERN_INDEX_TO_DATE_FORMAT_FIELD = {
- DateFormat.ERA_FIELD, DateFormat.YEAR_FIELD, DateFormat.MONTH_FIELD,
- DateFormat.DATE_FIELD, DateFormat.HOUR_OF_DAY1_FIELD,
- DateFormat.HOUR_OF_DAY0_FIELD, DateFormat.MINUTE_FIELD,
- DateFormat.SECOND_FIELD, DateFormat.MILLISECOND_FIELD,
- DateFormat.DAY_OF_WEEK_FIELD, DateFormat.DAY_OF_YEAR_FIELD,
- DateFormat.DAY_OF_WEEK_IN_MONTH_FIELD, DateFormat.WEEK_OF_YEAR_FIELD,
- DateFormat.WEEK_OF_MONTH_FIELD, DateFormat.AM_PM_FIELD,
- DateFormat.HOUR1_FIELD, DateFormat.HOUR0_FIELD,
- DateFormat.TIMEZONE_FIELD, DateFormat.TIMEZONE_FIELD,
- DateFormat.YEAR_FIELD, DateFormat.DAY_OF_WEEK_FIELD,
- DateFormat.TIMEZONE_FIELD
+ DateFormat.ERA_FIELD,
+ DateFormat.YEAR_FIELD,
+ DateFormat.MONTH_FIELD,
+ DateFormat.DATE_FIELD,
+ DateFormat.HOUR_OF_DAY1_FIELD,
+ DateFormat.HOUR_OF_DAY0_FIELD,
+ DateFormat.MINUTE_FIELD,
+ DateFormat.SECOND_FIELD,
+ DateFormat.MILLISECOND_FIELD,
+ DateFormat.DAY_OF_WEEK_FIELD,
+ DateFormat.DAY_OF_YEAR_FIELD,
+ DateFormat.DAY_OF_WEEK_IN_MONTH_FIELD,
+ DateFormat.WEEK_OF_YEAR_FIELD,
+ DateFormat.WEEK_OF_MONTH_FIELD,
+ DateFormat.AM_PM_FIELD,
+ DateFormat.HOUR1_FIELD,
+ DateFormat.HOUR0_FIELD,
+ DateFormat.TIMEZONE_FIELD,
+ DateFormat.TIMEZONE_FIELD,
+ DateFormat.YEAR_FIELD,
+ DateFormat.DAY_OF_WEEK_FIELD,
+ DateFormat.TIMEZONE_FIELD,
+ DateFormat.MONTH_FIELD
};
// Maps from DecimalFormatSymbols index to Field constant
private static final Field[] PATTERN_INDEX_TO_DATE_FORMAT_FIELD_ID = {
- Field.ERA, Field.YEAR, Field.MONTH, Field.DAY_OF_MONTH,
- Field.HOUR_OF_DAY1, Field.HOUR_OF_DAY0, Field.MINUTE,
- Field.SECOND, Field.MILLISECOND, Field.DAY_OF_WEEK,
- Field.DAY_OF_YEAR, Field.DAY_OF_WEEK_IN_MONTH,
- Field.WEEK_OF_YEAR, Field.WEEK_OF_MONTH,
- Field.AM_PM, Field.HOUR1, Field.HOUR0, Field.TIME_ZONE,
+ Field.ERA,
+ Field.YEAR,
+ Field.MONTH,
+ Field.DAY_OF_MONTH,
+ Field.HOUR_OF_DAY1,
+ Field.HOUR_OF_DAY0,
+ Field.MINUTE,
+ Field.SECOND,
+ Field.MILLISECOND,
+ Field.DAY_OF_WEEK,
+ Field.DAY_OF_YEAR,
+ Field.DAY_OF_WEEK_IN_MONTH,
+ Field.WEEK_OF_YEAR,
+ Field.WEEK_OF_MONTH,
+ Field.AM_PM,
+ Field.HOUR1,
+ Field.HOUR0,
Field.TIME_ZONE,
- Field.YEAR, Field.DAY_OF_WEEK,
- Field.TIME_ZONE
+ Field.TIME_ZONE,
+ Field.YEAR,
+ Field.DAY_OF_WEEK,
+ Field.TIME_ZONE,
+ Field.MONTH
};
/**
@@ -1094,7 +1113,8 @@
}
int style = (count >= 4) ? Calendar.LONG : Calendar.SHORT;
- if (!useDateFormatSymbols && field != CalendarBuilder.ISO_DAY_OF_WEEK) {
+ if (!useDateFormatSymbols && field < Calendar.ZONE_OFFSET
+ && patternCharIndex != PATTERN_MONTH_STANDALONE) {
current = calendar.getDisplayName(field, style, locale);
}
@@ -1106,20 +1126,23 @@
case PATTERN_ERA: // 'G'
if (useDateFormatSymbols) {
String[] eras = formatData.getEras();
- if (value < eras.length)
+ if (value < eras.length) {
current = eras[value];
+ }
}
- if (current == null)
+ if (current == null) {
current = "";
+ }
break;
case PATTERN_WEEK_YEAR: // 'Y'
case PATTERN_YEAR: // 'y'
if (calendar instanceof GregorianCalendar) {
- if (count != 2)
+ if (count != 2) {
zeroPaddingNumber(value, count, maxIntCount, buffer);
- else // count == 2
- zeroPaddingNumber(value, 2, 2, buffer); // clip 1996 to 96
+ } else {
+ zeroPaddingNumber(value, 2, 2, buffer);
+ } // clip 1996 to 96
} else {
if (current == null) {
zeroPaddingNumber(value, style == Calendar.LONG ? 1 : count,
@@ -1128,7 +1151,7 @@
}
break;
- case PATTERN_MONTH: // 'M'
+ case PATTERN_MONTH: // 'M' (context seinsive)
if (useDateFormatSymbols) {
String[] months;
if (count >= 4) {
@@ -1141,6 +1164,11 @@
} else {
if (count < 3) {
current = null;
+ } else if (forceStandaloneForm) {
+ current = calendar.getDisplayName(field, style | 0x8000, locale);
+ if (current == null) {
+ current = calendar.getDisplayName(field, style, locale);
+ }
}
}
if (current == null) {
@@ -1148,13 +1176,35 @@
}
break;
+ case PATTERN_MONTH_STANDALONE: // 'L'
+ assert current == null;
+ if (locale == null) {
+ String[] months;
+ if (count >= 4) {
+ months = formatData.getMonths();
+ current = months[value];
+ } else if (count == 3) {
+ months = formatData.getShortMonths();
+ current = months[value];
+ }
+ } else {
+ if (count >= 3) {
+ current = calendar.getDisplayName(field, style | 0x8000, locale);
+ }
+ }
+ if (current == null) {
+ zeroPaddingNumber(value+1, count, maxIntCount, buffer);
+ }
+ break;
+
case PATTERN_HOUR_OF_DAY1: // 'k' 1-based. eg, 23:59 + 1 hour =>> 24:59
if (current == null) {
- if (value == 0)
- zeroPaddingNumber(calendar.getMaximum(Calendar.HOUR_OF_DAY)+1,
+ if (value == 0) {
+ zeroPaddingNumber(calendar.getMaximum(Calendar.HOUR_OF_DAY) + 1,
count, maxIntCount, buffer);
- else
+ } else {
zeroPaddingNumber(value, count, maxIntCount, buffer);
+ }
}
break;
@@ -1180,11 +1230,12 @@
case PATTERN_HOUR1: // 'h' 1-based. eg, 11PM + 1 hour =>> 12 AM
if (current == null) {
- if (value == 0)
- zeroPaddingNumber(calendar.getLeastMaximum(Calendar.HOUR)+1,
+ if (value == 0) {
+ zeroPaddingNumber(calendar.getLeastMaximum(Calendar.HOUR) + 1,
count, maxIntCount, buffer);
- else
+ } else {
zeroPaddingNumber(value, count, maxIntCount, buffer);
+ }
}
break;
@@ -1289,7 +1340,7 @@
/**
* Formats a number with the specified minimum and maximum number of digits.
*/
- private final void zeroPaddingNumber(int value, int minDigits, int maxDigits, StringBuffer buffer)
+ private void zeroPaddingNumber(int value, int minDigits, int maxDigits, StringBuffer buffer)
{
// Optimization for 1, 2 and 4 digit numbers. This should
// cover most cases of formatting date/time related items.
@@ -1371,6 +1422,7 @@
* error, returns null.
* @exception NullPointerException if text or pos is null.
*/
+ @Override
public Date parse(String text, ParsePosition pos)
{
checkNegativeNumberExpression();
@@ -1504,7 +1556,9 @@
int i = 0;
int count = data.length;
- if (field == Calendar.DAY_OF_WEEK) i = 1;
+ if (field == Calendar.DAY_OF_WEEK) {
+ i = 1;
+ }
// There may be multiple strings in the data[] array which begin with
// the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
@@ -1767,7 +1821,9 @@
return -1;
}
char c = text.charAt(pos.index);
- if (c != ' ' && c != '\t') break;
+ if (c != ' ' && c != '\t') {
+ break;
+ }
++pos.index;
}
@@ -1912,8 +1968,9 @@
}
}
// [We computed 'value' above.]
- if (value == calendar.getMaximum(Calendar.HOUR_OF_DAY)+1)
+ if (value == calendar.getMaximum(Calendar.HOUR_OF_DAY) + 1) {
value = 0;
+ }
calb.set(Calendar.HOUR_OF_DAY, value);
return pos.index;
@@ -1966,8 +2023,9 @@
}
}
// [We computed 'value' above.]
- if (value == calendar.getLeastMaximum(Calendar.HOUR)+1)
+ if (value == calendar.getLeastMaximum(Calendar.HOUR) + 1) {
value = 0;
+ }
calb.set(Calendar.HOUR, value);
return pos.index;
@@ -2111,21 +2169,14 @@
return -1;
}
- private final String getCalendarName() {
- return calendar.getClass().getName();
- }
-
+ /**
+ * Returns true if the DateFormatSymbols has been set explicitly or locale
+ * is null.
+ */
private boolean useDateFormatSymbols() {
- if (useDateFormatSymbols) {
- return true;
- }
- return isGregorianCalendar() || locale == null;
+ return useDateFormatSymbols || locale == null;
}
- private boolean isGregorianCalendar() {
- return "java.util.GregorianCalendar".equals(getCalendarName());
- }
-
/**
* Translates a pattern, mapping each character in the from string to the
* corresponding character in the to string.
@@ -2138,13 +2189,14 @@
for (int i = 0; i < pattern.length(); ++i) {
char c = pattern.charAt(i);
if (inQuote) {
- if (c == '\'')
+ if (c == '\'') {
inQuote = false;
+ }
}
else {
- if (c == '\'')
+ if (c == '\'') {
inQuote = true;
- else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+ } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
int ci = from.indexOf(c);
if (ci >= 0) {
// patternChars is longer than localPatternChars due
@@ -2162,8 +2214,9 @@
}
result.append(c);
}
- if (inQuote)
+ if (inQuote) {
throw new IllegalArgumentException("Unfinished quote in pattern");
+ }
return result.toString();
}
@@ -2196,6 +2249,10 @@
*/
public void applyPattern(String pattern)
{
+ applyPatternImpl(pattern);
+ }
+
+ private void applyPatternImpl(String pattern) {
compiledPattern = compile(pattern);
this.pattern = pattern;
}
@@ -2246,6 +2303,7 @@
*
* @return a clone of this SimpleDateFormat
*/
+ @Override
public Object clone() {
SimpleDateFormat other = (SimpleDateFormat) super.clone();
other.formatData = (DateFormatSymbols) formatData.clone();
@@ -2257,6 +2315,7 @@
*
* @return the hash code value for this SimpleDateFormat object.
*/
+ @Override
public int hashCode()
{
return pattern.hashCode();
@@ -2270,9 +2329,12 @@
* @return true if the given object is equal to this
* SimpleDateFormat
*/
+ @Override
public boolean equals(Object obj)
{
- if (!super.equals(obj)) return false; // super does class check
+ if (!super.equals(obj)) {
+ return false; // super does class check
+ }
SimpleDateFormat that = (SimpleDateFormat) obj;
return (pattern.equals(that.pattern)
&& formatData.equals(that.formatData));
|