Calendar.java (set): Never recompute fields here.

* java/util/Calendar.java (set): Never recompute fields here. They
	will already be set if someone set time explicitly, and it can cause
	problems to do so. Don't invalidate AM_PM setting if HOUR is set.
	* java/util/GregorianCalendar.java (computeTime): Don't ignore an
	HOUR setting if AM_PM is set. Don't try to ensure the HOUR value is
	sane.
	* java/text/SimpleDateFormat.java (defaultCentury): New field.
	(readObject): Call set2DigitYearStart if appropriate so that
	defaultCentury is calculated.
	(SimpleDateFormat): Don't bother clearing calendar here. Call
	computeCenturyStart().
	(set2DigitYearStart): Calculate and set defaultCentury.
	(format): Don't clone the calendar. Use "calendar" not "theCalendar"
	everywhere.
	(parse): Likewise. If the pattern is "y" or "yy" and it found exactly
	2 numeric digits, use the 80-20 heuristic to parse the value into a
	default century based on defaultCenturyStart.
	(computeCenturyStart): Rewritten. Call set2DigitYearStart().

From-SVN: r44395
This commit is contained in:
Bryce McKinlay 2001-07-26 11:21:45 +00:00 committed by Bryce McKinlay
parent 2cf50fd3a4
commit fa397ddafb
4 changed files with 112 additions and 71 deletions

View file

@ -1,3 +1,24 @@
2001-07-26 Bryce McKinlay <bryce@waitaki.otago.ac.nz>
* java/util/Calendar.java (set): Never recompute fields here. They
will already be set if someone set time explicitly, and it can cause
problems to do so. Don't invalidate AM_PM setting if HOUR is set.
* java/util/GregorianCalendar.java (computeTime): Don't ignore an
HOUR setting if AM_PM is set. Don't try to ensure the HOUR value is
sane.
* java/text/SimpleDateFormat.java (defaultCentury): New field.
(readObject): Call set2DigitYearStart if appropriate so that
defaultCentury is calculated.
(SimpleDateFormat): Don't bother clearing calendar here. Call
computeCenturyStart().
(set2DigitYearStart): Calculate and set defaultCentury.
(format): Don't clone the calendar. Use "calendar" not "theCalendar"
everywhere.
(parse): Likewise. If the pattern is "y" or "yy" and it found exactly
2 numeric digits, use the 80-20 heuristic to parse the value into a
default century based on defaultCenturyStart.
(computeCenturyStart): Rewritten. Call set2DigitYearStart().
2001-07-25 Tom Tromey <tromey@redhat.com> 2001-07-25 Tom Tromey <tromey@redhat.com>
* Makefile.in: Rebuilt. * Makefile.in: Rebuilt.

View file

@ -62,7 +62,8 @@ public class SimpleDateFormat extends DateFormat
private transient Vector tokens; private transient Vector tokens;
private DateFormatSymbols formatData; // formatData private DateFormatSymbols formatData; // formatData
private Date defaultCenturyStart = computeCenturyStart (); private Date defaultCenturyStart;
private transient int defaultCentury;
private String pattern; private String pattern;
private int serialVersionOnStream = 1; // 0 indicates JDK1.1.3 or earlier private int serialVersionOnStream = 1; // 0 indicates JDK1.1.3 or earlier
private static final long serialVersionUID = 4774881970558875024L; private static final long serialVersionUID = 4774881970558875024L;
@ -78,9 +79,12 @@ public class SimpleDateFormat extends DateFormat
stream.defaultReadObject(); stream.defaultReadObject();
if (serialVersionOnStream < 1) if (serialVersionOnStream < 1)
{ {
defaultCenturyStart = computeCenturyStart (); computeCenturyStart ();
serialVersionOnStream = 1; serialVersionOnStream = 1;
} }
else
// Ensure that defaultCentury gets set.
set2DigitYearStart(defaultCenturyStart);
// Set up items normally taken care of by the constructor. // Set up items normally taken care of by the constructor.
tokens = new Vector(); tokens = new Vector();
@ -159,7 +163,7 @@ public class SimpleDateFormat extends DateFormat
super(); super();
Locale locale = Locale.getDefault(); Locale locale = Locale.getDefault();
calendar = new GregorianCalendar(locale); calendar = new GregorianCalendar(locale);
calendar.clear (); computeCenturyStart();
tokens = new Vector(); tokens = new Vector();
formatData = new DateFormatSymbols(locale); formatData = new DateFormatSymbols(locale);
pattern = (formatData.dateFormats[DEFAULT] + ' ' pattern = (formatData.dateFormats[DEFAULT] + ' '
@ -186,7 +190,7 @@ public class SimpleDateFormat extends DateFormat
{ {
super(); super();
calendar = new GregorianCalendar(locale); calendar = new GregorianCalendar(locale);
calendar.clear (); computeCenturyStart();
tokens = new Vector(); tokens = new Vector();
formatData = new DateFormatSymbols(locale); formatData = new DateFormatSymbols(locale);
compileFormat(pattern); compileFormat(pattern);
@ -203,10 +207,6 @@ public class SimpleDateFormat extends DateFormat
{ {
super(); super();
calendar = new GregorianCalendar(); calendar = new GregorianCalendar();
calendar.clear ();
// FIXME: XXX: Is it really necessary to set the timezone?
// The Calendar constructor is supposed to take care of this.
calendar.setTimeZone(TimeZone.getDefault());
tokens = new Vector(); tokens = new Vector();
this.formatData = formatData; this.formatData = formatData;
compileFormat(pattern); compileFormat(pattern);
@ -309,6 +309,10 @@ public class SimpleDateFormat extends DateFormat
public void set2DigitYearStart(Date date) public void set2DigitYearStart(Date date)
{ {
defaultCenturyStart = date; defaultCenturyStart = date;
calendar.clear();
calendar.setTime(date);
int year = calendar.get(Calendar.YEAR);
defaultCentury = year - (year % 100);
} }
/** /**
@ -386,8 +390,7 @@ public class SimpleDateFormat extends DateFormat
public StringBuffer format(Date date, StringBuffer buffer, FieldPosition pos) public StringBuffer format(Date date, StringBuffer buffer, FieldPosition pos)
{ {
String temp; String temp;
Calendar theCalendar = (Calendar) calendar.clone(); calendar.setTime(date);
theCalendar.setTime(date);
// go through vector, filling in fields where applicable, else toString // go through vector, filling in fields where applicable, else toString
Enumeration e = tokens.elements(); Enumeration e = tokens.elements();
@ -398,10 +401,10 @@ public class SimpleDateFormat extends DateFormat
int beginIndex = buffer.length(); int beginIndex = buffer.length();
switch (p.field) { switch (p.field) {
case ERA_FIELD: case ERA_FIELD:
buffer.append(formatData.eras[theCalendar.get(Calendar.ERA)]); buffer.append(formatData.eras[calendar.get(Calendar.ERA)]);
break; break;
case YEAR_FIELD: case YEAR_FIELD:
temp = String.valueOf(theCalendar.get(Calendar.YEAR)); temp = String.valueOf(calendar.get(Calendar.YEAR));
if (p.size < 4) if (p.size < 4)
buffer.append(temp.substring(temp.length()-2)); buffer.append(temp.substring(temp.length()-2));
else else
@ -409,60 +412,60 @@ public class SimpleDateFormat extends DateFormat
break; break;
case MONTH_FIELD: case MONTH_FIELD:
if (p.size < 3) if (p.size < 3)
withLeadingZeros(theCalendar.get(Calendar.MONTH)+1,p.size,buffer); withLeadingZeros(calendar.get(Calendar.MONTH)+1,p.size,buffer);
else if (p.size < 4) else if (p.size < 4)
buffer.append(formatData.shortMonths[theCalendar.get(Calendar.MONTH)]); buffer.append(formatData.shortMonths[calendar.get(Calendar.MONTH)]);
else else
buffer.append(formatData.months[theCalendar.get(Calendar.MONTH)]); buffer.append(formatData.months[calendar.get(Calendar.MONTH)]);
break; break;
case DATE_FIELD: case DATE_FIELD:
withLeadingZeros(theCalendar.get(Calendar.DATE),p.size,buffer); withLeadingZeros(calendar.get(Calendar.DATE),p.size,buffer);
break; break;
case HOUR_OF_DAY1_FIELD: // 1-24 case HOUR_OF_DAY1_FIELD: // 1-24
withLeadingZeros(((theCalendar.get(Calendar.HOUR_OF_DAY)+23)%24)+1,p.size,buffer); withLeadingZeros(((calendar.get(Calendar.HOUR_OF_DAY)+23)%24)+1,p.size,buffer);
break; break;
case HOUR_OF_DAY0_FIELD: // 0-23 case HOUR_OF_DAY0_FIELD: // 0-23
withLeadingZeros(theCalendar.get(Calendar.HOUR_OF_DAY),p.size,buffer); withLeadingZeros(calendar.get(Calendar.HOUR_OF_DAY),p.size,buffer);
break; break;
case MINUTE_FIELD: case MINUTE_FIELD:
withLeadingZeros(theCalendar.get(Calendar.MINUTE),p.size,buffer); withLeadingZeros(calendar.get(Calendar.MINUTE),p.size,buffer);
break; break;
case SECOND_FIELD: case SECOND_FIELD:
withLeadingZeros(theCalendar.get(Calendar.SECOND),p.size,buffer); withLeadingZeros(calendar.get(Calendar.SECOND),p.size,buffer);
break; break;
case MILLISECOND_FIELD: case MILLISECOND_FIELD:
withLeadingZeros(theCalendar.get(Calendar.MILLISECOND),p.size,buffer); withLeadingZeros(calendar.get(Calendar.MILLISECOND),p.size,buffer);
break; break;
case DAY_OF_WEEK_FIELD: case DAY_OF_WEEK_FIELD:
if (p.size < 4) if (p.size < 4)
buffer.append(formatData.shortWeekdays[theCalendar.get(Calendar.DAY_OF_WEEK)]); buffer.append(formatData.shortWeekdays[calendar.get(Calendar.DAY_OF_WEEK)]);
else else
buffer.append(formatData.weekdays[theCalendar.get(Calendar.DAY_OF_WEEK)]); buffer.append(formatData.weekdays[calendar.get(Calendar.DAY_OF_WEEK)]);
break; break;
case DAY_OF_YEAR_FIELD: case DAY_OF_YEAR_FIELD:
withLeadingZeros(theCalendar.get(Calendar.DAY_OF_YEAR),p.size,buffer); withLeadingZeros(calendar.get(Calendar.DAY_OF_YEAR),p.size,buffer);
break; break;
case DAY_OF_WEEK_IN_MONTH_FIELD: case DAY_OF_WEEK_IN_MONTH_FIELD:
withLeadingZeros(theCalendar.get(Calendar.DAY_OF_WEEK_IN_MONTH),p.size,buffer); withLeadingZeros(calendar.get(Calendar.DAY_OF_WEEK_IN_MONTH),p.size,buffer);
break; break;
case WEEK_OF_YEAR_FIELD: case WEEK_OF_YEAR_FIELD:
withLeadingZeros(theCalendar.get(Calendar.WEEK_OF_YEAR),p.size,buffer); withLeadingZeros(calendar.get(Calendar.WEEK_OF_YEAR),p.size,buffer);
break; break;
case WEEK_OF_MONTH_FIELD: case WEEK_OF_MONTH_FIELD:
withLeadingZeros(theCalendar.get(Calendar.WEEK_OF_MONTH),p.size,buffer); withLeadingZeros(calendar.get(Calendar.WEEK_OF_MONTH),p.size,buffer);
break; break;
case AM_PM_FIELD: case AM_PM_FIELD:
buffer.append(formatData.ampms[theCalendar.get(Calendar.AM_PM)]); buffer.append(formatData.ampms[calendar.get(Calendar.AM_PM)]);
break; break;
case HOUR1_FIELD: // 1-12 case HOUR1_FIELD: // 1-12
withLeadingZeros(((theCalendar.get(Calendar.HOUR)+11)%12)+1,p.size,buffer); withLeadingZeros(((calendar.get(Calendar.HOUR)+11)%12)+1,p.size,buffer);
break; break;
case HOUR0_FIELD: // 0-11 case HOUR0_FIELD: // 0-11
withLeadingZeros(theCalendar.get(Calendar.HOUR),p.size,buffer); withLeadingZeros(calendar.get(Calendar.HOUR),p.size,buffer);
break; break;
case TIMEZONE_FIELD: case TIMEZONE_FIELD:
TimeZone zone = theCalendar.getTimeZone(); TimeZone zone = calendar.getTimeZone();
boolean isDST = theCalendar.get(Calendar.DST_OFFSET) != 0; boolean isDST = calendar.get(Calendar.DST_OFFSET) != 0;
// FIXME: XXX: This should be a localized time zone. // FIXME: XXX: This should be a localized time zone.
String zoneID = zone.getDisplayName(isDST, p.size > 3 ? TimeZone.LONG : TimeZone.SHORT); String zoneID = zone.getDisplayName(isDST, p.size > 3 ? TimeZone.LONG : TimeZone.SHORT);
buffer.append(zoneID); buffer.append(zoneID);
@ -482,7 +485,8 @@ public class SimpleDateFormat extends DateFormat
return buffer; return buffer;
} }
private void withLeadingZeros(int value, int length, StringBuffer buffer) { private void withLeadingZeros(int value, int length, StringBuffer buffer)
{
String valStr = String.valueOf(value); String valStr = String.valueOf(value);
for (length -= valStr.length(); length > 0; length--) for (length -= valStr.length(); length > 0; length--)
buffer.append('0'); buffer.append('0');
@ -514,12 +518,10 @@ public class SimpleDateFormat extends DateFormat
int fmt_index = 0; int fmt_index = 0;
int fmt_max = pattern.length(); int fmt_max = pattern.length();
// We copy the Calendar because if we don't we will modify it and calendar.clear();
// then this.equals() will no longer have the desired result.
Calendar theCalendar = (Calendar) calendar.clone ();
theCalendar.clear();
boolean saw_timezone = false; boolean saw_timezone = false;
int quote_start = -1; int quote_start = -1;
boolean is2DigitYear = false;
for (; fmt_index < fmt_max; ++fmt_index) for (; fmt_index < fmt_max; ++fmt_index)
{ {
char ch = pattern.charAt(fmt_index); char ch = pattern.charAt(fmt_index);
@ -552,7 +554,7 @@ public class SimpleDateFormat extends DateFormat
int first = fmt_index; int first = fmt_index;
while (++fmt_index < fmt_max && pattern.charAt(fmt_index) == ch) while (++fmt_index < fmt_max && pattern.charAt(fmt_index) == ch)
; ;
int count = fmt_index - first; int fmt_count = fmt_index - first;
--fmt_index; --fmt_index;
// We can handle most fields automatically: most either are // We can handle most fields automatically: most either are
@ -564,6 +566,7 @@ public class SimpleDateFormat extends DateFormat
boolean is_numeric = true; boolean is_numeric = true;
String[] match = null; String[] match = null;
int offset = 0; int offset = 0;
boolean maybe2DigitYear = false;
switch (ch) switch (ch)
{ {
case 'd': case 'd':
@ -579,7 +582,7 @@ public class SimpleDateFormat extends DateFormat
is_numeric = false; is_numeric = false;
offset = 1; offset = 1;
calendar_field = Calendar.DAY_OF_WEEK; calendar_field = Calendar.DAY_OF_WEEK;
match = (count <= 3 match = (fmt_count <= 3
? formatData.getShortWeekdays() ? formatData.getShortWeekdays()
: formatData.getWeekdays()); : formatData.getWeekdays());
break; break;
@ -591,20 +594,20 @@ public class SimpleDateFormat extends DateFormat
break; break;
case 'M': case 'M':
calendar_field = Calendar.MONTH; calendar_field = Calendar.MONTH;
if (count <= 2) if (fmt_count <= 2)
offset = -1; offset = -1;
else else
{ {
is_numeric = false; is_numeric = false;
match = (count <= 3 match = (fmt_count <= 3
? formatData.getShortMonths() ? formatData.getShortMonths()
: formatData.getMonths()); : formatData.getMonths());
} }
break; break;
case 'y': case 'y':
calendar_field = Calendar.YEAR; calendar_field = Calendar.YEAR;
if (count <= 2) if (fmt_count <= 2)
offset = 1900; maybe2DigitYear = true;
break; break;
case 'K': case 'K':
calendar_field = Calendar.HOUR; calendar_field = Calendar.HOUR;
@ -655,8 +658,8 @@ public class SimpleDateFormat extends DateFormat
found_zone = true; found_zone = true;
saw_timezone = true; saw_timezone = true;
TimeZone tz = TimeZone.getTimeZone (strings[0]); TimeZone tz = TimeZone.getTimeZone (strings[0]);
theCalendar.setTimeZone (tz); calendar.setTimeZone (tz);
theCalendar.set (Calendar.ZONE_OFFSET, tz.getRawOffset ()); calendar.set (Calendar.ZONE_OFFSET, tz.getRawOffset ());
offset = 0; offset = 0;
if (k > 2 && tz instanceof SimpleTimeZone) if (k > 2 && tz instanceof SimpleTimeZone)
{ {
@ -680,9 +683,12 @@ public class SimpleDateFormat extends DateFormat
// Compute the value we should assign to the field. // Compute the value we should assign to the field.
int value; int value;
int index = -1;
if (is_numeric) if (is_numeric)
{ {
numberFormat.setMinimumIntegerDigits(count); numberFormat.setMinimumIntegerDigits(fmt_count);
if (maybe2DigitYear)
index = pos.getIndex();
Number n = numberFormat.parse(dateStr, pos); Number n = numberFormat.parse(dateStr, pos);
if (pos == null || ! (n instanceof Long)) if (pos == null || ! (n instanceof Long))
return null; return null;
@ -690,7 +696,7 @@ public class SimpleDateFormat extends DateFormat
} }
else if (match != null) else if (match != null)
{ {
int index = pos.getIndex(); index = pos.getIndex();
int i; int i;
for (i = offset; i < match.length; ++i) for (i = offset; i < match.length; ++i)
{ {
@ -707,9 +713,28 @@ public class SimpleDateFormat extends DateFormat
} }
else else
value = offset; value = offset;
if (maybe2DigitYear)
{
// Parse into default century if the numeric year string has
// exactly 2 digits.
int digit_count = pos.getIndex() - index;
if (digit_count == 2)
is2DigitYear = true;
}
// Assign the value and move on. // Assign the value and move on.
theCalendar.set(calendar_field, value); calendar.set(calendar_field, value);
}
if (is2DigitYear)
{
// Apply the 80-20 heuristic to dermine the full year based on
// defaultCenturyStart.
int year = defaultCentury + calendar.get(Calendar.YEAR);
calendar.set(Calendar.YEAR, year);
if (calendar.getTime().compareTo(defaultCenturyStart) < 0)
calendar.set(Calendar.YEAR, year + 100);
} }
try try
@ -718,10 +743,10 @@ public class SimpleDateFormat extends DateFormat
{ {
// Use the real rules to determine whether or not this // Use the real rules to determine whether or not this
// particular time is in daylight savings. // particular time is in daylight savings.
theCalendar.clear (Calendar.DST_OFFSET); calendar.clear (Calendar.DST_OFFSET);
theCalendar.clear (Calendar.ZONE_OFFSET); calendar.clear (Calendar.ZONE_OFFSET);
} }
return theCalendar.getTime(); return calendar.getTime();
} }
catch (IllegalArgumentException x) catch (IllegalArgumentException x)
{ {
@ -732,18 +757,10 @@ public class SimpleDateFormat extends DateFormat
// Compute the start of the current century as defined by // Compute the start of the current century as defined by
// get2DigitYearStart. // get2DigitYearStart.
private Date computeCenturyStart () private void computeCenturyStart()
{ {
// Compute the current year. We assume a year has 365 days. Then int year = calendar.get(Calendar.YEAR);
// compute 80 years ago, and finally reconstruct the number of calendar.set(Calendar.YEAR, year - 80);
// milliseconds. We do this computation in this strange way set2DigitYearStart(calendar.getTime());
// because it lets us easily truncate the milliseconds, seconds,
// etc, which don't matter and which confuse
// SimpleDateFormat.equals().
long now = System.currentTimeMillis ();
now /= 365L * 24L * 60L * 60L * 1000L;
now -= 80;
now *= 365L * 24L * 60L * 60L * 1000L;
return new Date (now);
} }
} }

View file

@ -549,8 +549,6 @@ public abstract class Calendar implements Serializable, Cloneable
*/ */
public final void set(int field, int value) public final void set(int field, int value)
{ {
if (!areFieldsSet)
computeFields();
isTimeSet = false; isTimeSet = false;
fields[field] = value; fields[field] = value;
isSet[field] = true; isSet[field] = true;
@ -573,7 +571,6 @@ public abstract class Calendar implements Serializable, Cloneable
isSet[HOUR] = false; isSet[HOUR] = false;
break; break;
case HOUR: case HOUR:
isSet[AM_PM] = false;
isSet[HOUR_OF_DAY] = false; isSet[HOUR_OF_DAY] = false;
break; break;
} }
@ -587,8 +584,6 @@ public abstract class Calendar implements Serializable, Cloneable
*/ */
public final void set(int year, int month, int date) public final void set(int year, int month, int date)
{ {
if (!areFieldsSet)
computeFields();
isTimeSet = false; isTimeSet = false;
fields[YEAR] = year; fields[YEAR] = year;
fields[MONTH] = month; fields[MONTH] = month;

View file

@ -373,9 +373,17 @@ public class GregorianCalendar extends Calendar
year = 1 - year; year = 1 - year;
int[] daysOfYear = getDayOfYear(year); int[] daysOfYear = getDayOfYear(year);
int hour = isSet[HOUR_OF_DAY] ? fields[HOUR_OF_DAY]
: (isSet[HOUR] && isSet[AM_PM] int hour = 0;
? fields[AM_PM] * 12 + (fields[HOUR] % 12) : 0); if (isSet[HOUR_OF_DAY])
hour = fields[HOUR_OF_DAY];
else if (isSet[HOUR])
{
hour = fields[HOUR];
if (isSet[AM_PM] && fields[AM_PM] == PM)
hour += 12;
}
int minute = isSet[MINUTE] ? fields[MINUTE] : 0; int minute = isSet[MINUTE] ? fields[MINUTE] : 0;
int second = isSet[SECOND] ? fields[SECOND] : 0; int second = isSet[SECOND] ? fields[SECOND] : 0;
int millis = isSet[MILLISECOND] ? fields[MILLISECOND] : 0; int millis = isSet[MILLISECOND] ? fields[MILLISECOND] : 0;