JDK-4209272 : Regression: SimpleDateFormat w/lenient mode false, throws exception for Feb 29
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.text
  • Affected Version: 1.1.5,1.2.0,1.2.1,1.3.1_22
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS:
    generic,windows_95,windows_nt,windows_xp,windows_2008,windows_vista generic,windows_95,windows_nt,windows_xp,windows_2008,windows_vista
  • CPU: generic,x86
  • Submitted: 1999-02-05
  • Updated: 2008-02-14
  • Resolved: 1999-02-19
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other Other Other Other Other Other
1.1.6_007 007Fixed 1.1.7Fixed 1.1.8Fixed 1.2.1_003Fixed 1.2.2Fixed 1.3.1Resolved
Related Reports
Duplicate :  
Duplicate :  
Duplicate :  
Relates :  
Description

Name: gsC80088			Date: 02/05/99


SimpleDateFormat does not correctly consider leap years in the
following code.  This only occurs in jdk1.2.  It works correctly
on 1.1.6

import java.lang.*;
import java.util.*;
import java.text.*;

public class testdate
{
        public testdate()
	{
	}

	public static void main(String[] arg)
	{
		SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
		format.setLenient(false);

		try
		{
			String normal = "03/13/1998 12:22:43";
			String leap2000 = "02/29/2000 13:40:22";
			String leap1996 = "02/29/1996 08:15:15";
			System.out.println("Normal date:");
			Date dnorm = format.parse(normal);
			System.out.println(dnorm);

			System.out.println("2000 date:");
			Date d2000 = format.parse(leap2000);
			System.out.println(d2000);

			System.out.println("1996 date:");
			Date d1996 = format.parse(leap1996);
			System.out.println(d1996);
		}
		catch(ParseException pe)
		{
			System.out.println(pe);
		}
		catch(Exception e)
		{
			System.out.println(e);
		}
	}
}

output from 1.2:

Normal date:
Fri Mar 13 12:22:43 EST 1998
2000 date:
java.text.ParseException: Unparseable date: "02/29/2000 13:40:22"


output from 1.1.6:

Normal date:
Fri Mar 13 12:22:43 EST 1998
2000 date:
Tue Feb 29 13:40:22 EST 2000
1996 date:
Thu Feb 29 08:15:15 EST 1996
(Review ID: 53564)
======================================================================

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: 1.1.8 1.2.2 FIXED IN: 1.1.6_007 1.1.7b_005 1.1.8 1.2.1-003 1.2.2 INTEGRATED IN: 1.1.6_007 1.1.7b_005 1.1.8 1.2.1-003 1.2.2
14-06-2004

EVALUATION Also fails in JDK1.1.8H. norbert.lindenberg@Eng 1999-02-16 From alanl@eng: The general problem is that a zero ERA value means BC, but the default era is actually AD. Hence a year like 2000, without an era, gets interpreted as 2000 BC, instead of 2000 AD (hence the parse failure). The fix is straightforward; replace calls to internalGet(ERA) with calls to the one-line method internalGetEra(), which does the right thing. [get(ERA) is not a problem since it resolves all fields properly before returning a value.] There are 4 such calls. I changed 3 of them. I did not change the call in add(), since it reflects a *numeric* operation on the field, where users will expect a default of 0, not of AD.
11-06-2004

SUGGESTED FIX From alanl@eng: ============================================ Here is the diff to src/share/classes/java/util/GregorianCalendar.java. The line numbers in the diff may be off, since I don't have the latest version of GregorianCalendar.java here. ============================================ 521c521 < if (this.internalGet(ERA) == AD) { --- > if (this.internalGetEra() == AD) { 1144c1144 < int era = internalGet(ERA); --- > int era = internalGetEra(); 1834c1834 < if (internalGet(ERA) == BC) { --- > if (internalGetEra() == BC) { 1913a1914,1921 > } > > /** > * Return the ERA. We need a special method for this because the > * default ERA is AD, but a zero (unset) ERA is BC. > */ > private final int internalGetEra() { > return isSet(ERA) ? internalGet(ERA) : AD; ============================================ Here is the new test, to be added to test/java/text/Format/DateFormatRegression.java. ============================================ /** * @bug 4210209 * DateFormat cannot parse Feb 29 2000 when setLenient(false) */ public void Test4210209() { String pattern = "MMM d, yyyy"; DateFormat fmt = new SimpleDateFormat(pattern, new DateFormatSymbols(Locale.US)); fmt.getCalendar().setLenient(false); Date d = new Date(2000-1900, Calendar.FEBRUARY, 29); String s = fmt.format(d); logln(d + " x " + pattern + " => " + s); ParsePosition pos = new ParsePosition(0); d = fmt.parse(s, pos); logln(d + " <= " + pattern + " x " + s); logln("Parse pos = " + pos); if (pos.getErrorIndex() != -1) { errln("FAIL"); } // The underlying bug is in GregorianCalendar. If the following lines // succeed, the bug is fixed. If the bug isn't fixed, they will throw // an exception. GregorianCalendar cal = new GregorianCalendar(); cal.clear(); cal.setLenient(false); cal.set(2000, Calendar.FEBRUARY, 29); // This should work! logln(cal.getTime().toString()); }
11-06-2004