JDK-8141243 : Unexpected timezone returned after parsing a date
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util
  • Affected Version: 7u76,8
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2015-11-02
  • Updated: 2018-03-21
  • Resolved: 2015-12-03
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.
JDK 7 JDK 8 JDK 9 Other
7u121Fixed 8u102Fixed 9 b96Fixed openjdk7uFixed
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.7.0_76"
Java(TM) SE Runtime Environment (build 1.7.0_76-b13)
Java HotSpot(TM) 64-Bit Server VM (build 24.76-b04, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Linux adnlt043 2.6.32-504.8.1.el6.x86_64 #1 SMP Fri Dec 19 12:09:25 EST 2014 x86_64 x86_64 x86_64 GNU/Linux

A DESCRIPTION OF THE PROBLEM :
When parsing the virtual timezone "UTC" with java.text.SimpleDateFormat, the timezone is set to the first timezone that matches an actual timezone in the UTC group, which is Antarctica/Troll. When comparing this timezone with the result of TimeZone.getTimeZone("UTC"), we fail.

ADDITIONAL REGRESSION INFORMATION: 
The comparison worked in JDK 1.7.0_21 and older versions of JDK.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile and execute:

public class TimeZoneTest {

	public static void main(String[] args) throws Exception {
		String dateString = "UTC";
		String formatString = "z";
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat(formatString);
		Date date = simpleDateFormat.parse(dateString);
		
		TimeZone timeZone = TimeZone.getTimeZone("UTC");
		
		long now = System.currentTimeMillis();
		
		Calendar cal = new GregorianCalendar(simpleDateFormat.getTimeZone());
		cal.setTime(date);
		
		if (timeZone.getOffset(now) == cal.getTimeZone().getOffset(now)) {
			System.out.println("Works as expected");
		} else {
			System.err.println("The timezone parsed with the simple date format has an offset '" + 
					cal.getTimeZone().getOffset(now) + "', but the timezone offset of TimeZone.getTimeZone('UTC') is '" + timeZone.getOffset(now) + "'");
		}
		System.out.println("TimeZone.getTimeZone('UTC') : " + TimeZone.getTimeZone("UTC"));
		System.out.println("cal.getTimeZone() : " + cal.getTimeZone());
	}
}


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Works as expected
TimeZone.getTimeZone('UTC') : sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
cal.getTimeZone() : sun.util.calendar.ZoneInfo[id="Etc/UCT",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
ACTUAL -
The timezone parsed with the simple date format has an offset '7200000', but the timezone offset of TimeZone.getTimeZone('UTC') is '0'
TimeZone.getTimeZone('UTC') : sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
cal.getTimeZone() : sun.util.calendar.ZoneInfo[id="Antarctica/Troll",offset=0,dstSavings=7200000,useDaylight=true,transitions=67,lastRule=java.util.SimpleTimeZone[id=Antarctica/Troll,offset=0,dstSavings=7200000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]]


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
public class TimeZoneTest {

	public static void main(String[] args) throws Exception {
		String dateString = "UTC";
		String formatString = "z";
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat(formatString);
		Date date = simpleDateFormat.parse(dateString);
		
		TimeZone timeZone = TimeZone.getTimeZone("UTC");
		
		long now = System.currentTimeMillis();
		
		Calendar cal = new GregorianCalendar(simpleDateFormat.getTimeZone());
		cal.setTime(date);
		
		if (timeZone.getOffset(now) == cal.getTimeZone().getOffset(now)) {
			System.out.println("Works as expected");
		} else {
			System.err.println("The timezone parsed with the simple date format has an offset '" + 
					cal.getTimeZone().getOffset(now) + "', but the timezone offset of TimeZone.getTimeZone('UTC') is '" + timeZone.getOffset(now) + "'");
		}
		System.out.println("TimeZone.getTimeZone('UTC') : " + TimeZone.getTimeZone("UTC"));
		System.out.println("cal.getTimeZone() : " + cal.getTimeZone());
	}
}

---------- END SOURCE ----------


Comments
SimpleDateFormat.parse scans time zone names in resource bundles from the top and picks up whatever matched. The symptom is not reproducible with CLDR locale data because the display names for Antarctica/Troll doesn't follow the Olson tzdata. It's defined in tzdata as follows. # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S #Rule Troll 2005 max - Mar 1 1:00u 1:00 CET Rule Troll 2005 max - Mar lastSun 1:00u 2:00 CEST #Rule Troll 2005 max - Oct lastSun 1:00u 1:00 CET #Rule Troll 2004 max - Nov 7 1:00u 0:00 UTC # Remove the following line when uncommenting the above '#Rule' lines. Rule Troll 2004 max - Oct lastSun 1:00u 0:00 UTC # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone Antarctica/Troll 0 - zzz 2005 Feb 12 0:00 Troll %s But CLDR defines its names as "Greenwich Mean Time" and "GMT". Abbreviation "UTC" is used only for UTC.
19-11-2015

The symptom is reproducible in JDK 9 with -Djava.locale.providers=COMPAT (a.k.a. JRE). Removed 9-na.
17-11-2015

This issue happens only while parsing any abbreviated virtual timezone which is also present in the main timezone groups.(Like "UTC"), where it is part of "Antarctica/Troll" main group which includes:- Coordinated Universal Time, UTC, Central European Summer Time, CEST, Troll Time, ATT. And "UTC" is also in the main timezone group - UTC which includes:- Coordinated Universal Time, UTC, Coordinated Universal Time, UTC, Coordinated Universal Time, UTC. Hence to fix this issue what i have done is:- SimpleDateFormat's subParseZoneString(String text, int start, CalendarBuilder calb) method is altered to check and consider for the virtual zonenames which are also part of the main timezones(like UTC,IST,PST,MST,CST,etc...). Please note that, if you pass the other abbreviated virtual timezones which are not part of the main timezone groups(like AKST, ACST, AEST, WAT, etc...) you will find the previous behavior of setting the timezone to the main timezone in that group.
16-11-2015

Attached is the test case. JDK 7 -Pass JDK 7u40 -Pass JDK 7u60 -Pass Below is the 'as expected' output on these versions: ----------------------------------------------------------------------------------- Works as expected TimeZone.getTimeZone('UTC') : sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null] cal.getTimeZone() : sun.util.calendar.ZoneInfo[id="Etc/UCT",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null] ------------------------------------------------------------------------------------- JDK 7u80 - Fail JDK 8 - Fail JDK 8u72 - Fail Below is the output on these version: -------------------------------------------------- Works as expected TimeZone.getTimeZone('UTC') : sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null] cal.getTimeZone() : sun.util.calendar.ZoneInfo[id="Antarctica/Troll",offset=0,dstSavings=7200000,useDaylight=true,transitions=67,lastRule=java.util.SimpleTimeZone[id=Antarctica/Troll,offset=0,dstSavings=7200000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]] ------------------------------------------------------------------------------- JDK 9ea b85 -Pass Below is the output: Works as expected TimeZone.getTimeZone('UTC') : sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null] cal.getTimeZone() : sun.util.calendar.ZoneInfo[id="Universal",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null] ---------------------------------------------------------- Moving across to dev-team for evaluation.
03-11-2015