SYNOPSIS
--------
Regression: SimpleDateFormat incorrectly parses dates formatted with Z and z pattern letters
OPERATING SYSTEM
----------------
All
FULL JDK VERSION
----------------
All JDKs containing fix for CR 7130335
e.g. 6u32 onwards, 7u4 onwards, JDK 8
DESCRIPTION
-----------
Consider the following process:
1. Create a Date object
2. Format that Date object with a custom SimpleDateFormat
3. Parse the String created by step 2 with the same SimpleDateFormat
object
The end result should normally be a Date object with exactly the same time as one created in step 1, because the same format is used for formatting and parsing.
However, since the fix for CR 7130335 this is not always the case. It seems that format Strings containing both the Z and z pattern letters are interpreted incorrectly by the parse() method when daylight savings time is active. The attached testcase demonstrates this.
REPRODUCTION INSTRUCTIONS
-------------------------
1. Compile and run the attached testcase
Expected behaviour (6u31 and earlier)
-------------------------------------
C:\>java DateTimeTest
date.toString(): Wed Aug 08 13:32:28 BST 2012 (getTime() = 1344429148569)
Format string: EEE, d MMM yyyy HH:mm:ss.SSS Z (z)
After formatting: Wed, 8 Aug 2012 13:32:28.569 +0100 (BST)
After parsing: Wed Aug 08 13:32:28 BST 2012 (getTime() = 1344429148569)
Note that the Dates before and after parsing are the same.
Observed behaviour (6u32 and later)
-----------------------------------
C:\>java DateTimeTest
date.toString(): Wed Aug 08 13:32:28 BST 2012 (getTime() = 1344429148569)
Format string: EEE, d MMM yyyy HH:mm:ss.SSS Z (z)
After formatting: Wed, 8 Aug 2012 13:32:28.569 +0100 (BST)
After parsing: Wed Aug 08 12:32:28 BST 2012 (getTime() = 1344425548569)
Note that the Dates before and after parsing are different by one hour.
The testcase is hard coded to test Europe/London (BST), but the problem seems to occur under any DST timezone, for example:
C:\>java DateTimeTest
date.toString(): Wed Aug 08 08:32:28 EDT 2012 (getTime() = 1344429148569)
Format string: EEE, d MMM yyyy HH:mm:ss.SSS Z (z)
After formatting: Wed, 8 Aug 2012 08:32:28.569 -0400 (EDT)
After parsing: Wed Aug 08 07:32:28 EDT 2012 (getTime() = 1344425548569)
C:\>java DateTimeTest
date.toString(): Wed Aug 08 15:32:28 EEST 2012 (getTime() = 1344429148569)
Format string: EEE, d MMM yyyy HH:mm:ss.SSS Z (z)
After formatting: Wed, 8 Aug 2012 15:32:28.569 +0300 (EEST)
After parsing: Wed Aug 08 14:32:28 EEST 2012 (getTime() = 1344425548569)
Also, the problem only happens when the Z and z pattern letters are bot present in the format string. If we remove either of these pattern letters the problem does not occur:
C:\>java DateTimeTest
date.toString(): Wed Aug 08 13:32:28 BST 2012 (getTime() = 1344429148569)
Format string: EEE, d MMM yyyy HH:mm:ss.SSS Z ()
After formatting: Wed, 8 Aug 2012 13:32:28.569 +0100 ()
After parsing: Wed Aug 08 13:32:28 BST 2012 (getTime() = 1344429148569)
C:\>java DateTimeTest
date.toString(): Wed Aug 08 13:32:28 BST 2012 (getTime() = 1344429148569)
Format string: EEE, d MMM yyyy HH:mm:ss.SSS (z)
After formatting: Wed, 8 Aug 2012 13:32:28.569 (BST)
After parsing: Wed Aug 08 13:32:28 BST 2012 (getTime() = 1344429148569)
SUGGESTED FIX
-------------
Our only suggestion at this time is to back out the fix for 7130335.
TESTCASE SOURCE
---------------
import java.text.*;
import java.util.*;
import java.io.*;
public class DateTimeTest {
public static void main(String args[]) throws Exception {
TimeZone.setDefault(TimeZone.getTimeZone("Europe/London"));
long aDstTime = 1344429148569L; // An arbitrary date in mid August 2012
final String formatString = "EEE, d MMM yyyy HH:mm:ss.SSS Z (z)";
final Date dateBeforeParsing = new Date(aDstTime);
System.out.println(" date.toString(): " + dateBeforeParsing + " (getTime() = " + dateBeforeParsing.getTime() + ")");
final SimpleDateFormat outFormat = new SimpleDateFormat(formatString, Locale.US);
System.out.println(" Format string: " + formatString);
final String formattedDateString = outFormat.format(dateBeforeParsing);
System.out.println("After formatting: " + formattedDateString);
final Date dateAfterParsing = outFormat.parse(formattedDateString);
System.out.println(" After parsing: " + dateAfterParsing + " (getTime() = " + dateAfterParsing.getTime() + ")");
}
}