You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@myfaces.apache.org by Yee-wah Lee <ye...@oracle.com> on 2009/07/10 01:15:04 UTC

[Trinidad] DateTimeConverter and SimpleDateFormat changes in JDK 6 lead to issue with daylight savings

Hi everyone,

The Trinidad DateTimeConverter has a problem when using JDK 6, because 
of changes in SimpleDateFormat. I have written a test case to replicate 
the converter's behavior.

Test case:
      DateFormat df = DateFormat.getDateTimeInstance (DateFormat.SHORT, 
DateFormat.LONG);
      Date d = new Date();
      // getFormattingTimeZone copied from 
trinidad-impl\DateTimeConverter.java, it clones the converter's timezone 
and gives it a
     //  customID (GMT +/- offset)./ This affects the display name, e.g. 
instead of 'PST' it will display 'GMT-08:00'
      TimeZone tz = getFormattingTimeZone (df.getTimeZone());
      df.setTimeZone (tz);
      System.out.println ("The date is " + d + ", DateFormat.format() 
returns " + df.format (d));

On JDK 1.5 this prints:
The date is Wed Jul 08 10:55:25 PDT 2009, DateFormat.format() returns 
7/8/09 10:55:25 AM GMT-07:00

On JDK 6:
The date is Wed Jul 08 10:56:39 PDT 2009, DateFormat.format() returns 
7/8/09 10:56:39 AM GMT-08:00

Notice that the offset in the second case is 8h. This is incorrect, the 
date (Jul 8 09)  is in Pacific Daylight time (GMT-7), and if posted 
back, it would change the underlying data by +1h.

I can understand why getFormattingTimeZone was originally used: timezone 
is thus always displayed in GMT +/- x, which helps when calculating 
offsets on the client side.  I would like to discuss the possible fixes 
for the JDK 6 case (details to follow in second email), any input is 
welcome.

I've filed a JIRA tracking this:
https://issues.apache.org/jira/browse/TRINIDAD-1512

Thanks,
Yee-Wah

Re: [Trinidad] DateTimeConverter and SimpleDateFormat changes in JDK 6 lead to issue with daylight savings: code snippets

Posted by Yee-wah Lee <ye...@oracle.com>.
Here is the code relevant to the discussion:

1)  DateTimeConverter.java#getFormattingTimeZone(TimeZone tZone)
This effectively creates a custom timezone.
    TimeZone zone = (TimeZone) tZone.clone();
    // set the id as "GMT Sign Hours : Minutes"
    StringBuilder zoneId = new StringBuilder(9);
    int rawOffset = zone.getRawOffset();
     ..  code to calculate and append GMT +/- hours:mins
    zone.setID(zoneId.toString());
    return zone;


2) JDK 1.5: SimpleDateFormat#subFormat()
The 1.5 format code would look up a zone info file using the date's 
offset and daylight savings.
        case 17: // 'z' - ZONE_OFFSET
            int zoneIndex =   
formatData.getZoneIndex(calendar.getTimeZone().getID());
            if (zoneIndex == -1) {
                value = calendar.get(Calendar.ZONE_OFFSET) +  
calendar.get(Calendar.DST_OFFSET);
               
buffer.append(ZoneInfoFile.toCustomID(value));                    
<---------Uses this code path
           }....

3) JDK 6: SimpleDateFormat#subFormat()
The 1.6 code now checks the DateFormatSymbols.locale and 
isZoneStringsSet(). By default, the locale would be null and 
zoneStringsSet = false unless user overrides either.
It then calls TimeZone.getDisplayName() instead of checking the 
zoneInfoFile.

        case 17: // 'z' - ZONE_OFFSET
           if (formatData.locale == null || formatData.isZoneStringsSet) {
               ..
               // same as 1.5, looks up zone info file
            String[][] zoneStrings = formatData.getZoneStringsWrapper();
            buffer.append(zoneStrings[zoneIndex][index]);
         } else {
            TimeZone tz = calendar.getTimeZone();
            boolean daylight = (calendar.get(Calendar.DST_OFFSET) != 0);
            int tzstyle = (count < 4 ? TimeZone.SHORT : TimeZone.LONG);
            buffer.append(tz.getDisplayName(daylight, tzstyle, 
formatData.locale));         <------------ Uses this code path
        }...

4) JDK 1.5/6: TimeZone.getDisplayName()
This method is the same in both JDKs, but only called in JDK 6 case. If 
the ID is customized (GMT +/-x), it simply returns that.

    String id = getID();
    String[] names = getDisplayNames(id, locale);
    if (names == null) {
        if (id.startsWith("GMT")) {
          char sign = id.charAt(3);
          if (sign == '+' || sign == '-') {
              return id;  

Therefore, the display name for the Converter's timezone in JDK 6 is 
fixed as its ID (GMT+/-rawOffset).

Notes:
1) Although this used to work in JDK 1.5, I'm not convinced this is a 
JDK issue. Since we are creating a custom timezone in 
getFormattingTimeZone(), and the JDK documentation states: "No daylight 
saving time transition schedule can be specified with a custom time zone 
ID."
http://java.sun.com/javase/6/docs/api/java/util/TimeZone.html#setID%28java.lang.String

Arguably, the DateTimeConverter should get the formattingTimeZone() with 
the value to be formatted included, so it instead does:
    StringBuilder zoneId = new StringBuilder(9);
    int offset = zone.getOffset((Date) value);

2) On the other hand, I don't understand the check in #subformat() for 
formatData.locale or formatData.isZoneStringsSet. 
It seems like a change for the default behavior in SimpleDateFormat 
since most users will not plug-in their own DateFormatSymbols, and the 
defaults for those fields is false.

Yee-Wah


Yee-wah Lee wrote:
> Hi everyone,
>
> The Trinidad DateTimeConverter has a problem when using JDK 6, because 
> of changes in SimpleDateFormat. I have written a test case to 
> replicate the converter's behavior.
>
> Test case:
>      DateFormat df = DateFormat.getDateTimeInstance (DateFormat.SHORT, 
> DateFormat.LONG);
>      Date d = new Date();
>      // getFormattingTimeZone copied from 
> trinidad-impl\DateTimeConverter.java, it clones the converter's 
> timezone and gives it a
>     //  customID (GMT +/- offset)./ This affects the display name, 
> e.g. instead of 'PST' it will display 'GMT-08:00'
>      TimeZone tz = getFormattingTimeZone (df.getTimeZone());
>      df.setTimeZone (tz);
>      System.out.println ("The date is " + d + ", DateFormat.format() 
> returns " + df.format (d));
>
> On JDK 1.5 this prints:
> The date is Wed Jul 08 10:55:25 PDT 2009, DateFormat.format() returns 
> 7/8/09 10:55:25 AM GMT-07:00
>
> On JDK 6:
> The date is Wed Jul 08 10:56:39 PDT 2009, DateFormat.format() returns 
> 7/8/09 10:56:39 AM GMT-08:00
>
> Notice that the offset in the second case is 8h. This is incorrect, 
> the date (Jul 8 09)  is in Pacific Daylight time (GMT-7), and if 
> posted back, it would change the underlying data by +1h.
>
> I can understand why getFormattingTimeZone was originally used: 
> timezone is thus always displayed in GMT +/- x, which helps when 
> calculating offsets on the client side.  I would like to discuss the 
> possible fixes for the JDK 6 case (details to follow in second email), 
> any input is welcome.
>
> I've filed a JIRA tracking this:
> https://issues.apache.org/jira/browse/TRINIDAD-1512
>
> Thanks,
> Yee-Wah