<i18n dev> Java should handle user defined TimeZone subclass instances supporting historic rule changes properly

Yoshito Umaoka y.umaoka at gmail.com
Fri Oct 17 20:43:29 UTC 2014

Dear Java i18n team,

I was looking at recent Russian time zone changes and noticed a design 
issue in JDK. I think I knew this issue, but I forgot about this until 
recently. The problem is - Java does not support user defined custom 
TimeZone implementation properly.

java.util.TimeZone is quite old, and the original design assumed 'raw 
offset' and 'daylight saving amount' will never changed. To implement a 
subclass of TimeZone, you have to override an awkward abstract method - 
abstract int getOffset(int era, int year, int month, int day, int 
dayOfWeek, int milliseconds). This method signature itself does not 
limit a custom TimeZone implementation to support historic raw/dst 
offset changes. However, if you do so, you will see some problems in 
other places.

I filed a bug below:

Java should handle user defined TimeZone subclass instances supporting 
historic rule changes properly.

You can create a custom time zone implementation class by extending 
java.util.TimeZone. By the contract, the subclass must implement a few 

abstract int getOffset(int,int,int,int,int,int);
abstract int getRawOffset();
abstract boolean inDaylightTime(Date);
abstract void setRawOffset(int);
abstract boolean useDaylightTime();

Although, the interface above do not assume UTC offset or daylight 
saving amount is not changing time to time, a user can provide an 
implementation supporting historic UTC offset changes by int 
getOffset(int,int,int,int,int,int). However, there are two Java 
implementation problems.

1. The default implementation of TimeZone#getOffset(long date) does not 
use the abstract method - int getOffset(int,int,int,int,int,int). 
Therefore, when a different 'raw' offset was used in the past, or will 
be used in future, getOffset(long) always returns a result calculated 
from getRawOffset() and getDSTSavings(). Although a subclass 
implementation can provide TimeZone#getOffset(long date), the default 
implementation should use getOffset(int,int,int,int,int,int).

2. java.util.GregorianCalendar checks if a TimeZone instance is 
sun.util.calendar.TimZone or not, and if not, the offset field is 
calculated based on getRawOffset() / getDSTSavings(). Therefore, even a 
custom TimeZone implementation support different 'raw' offsets in the 
past or future, current 'raw' offset (getRawOffset()) value is used.

Use case:

A user may want to create a custom TimeZone instance from iCalendar 
VTIMEZONE component. iCalendr VTIMEZONE can support historic time zone 
rule changes.


1) Add API TimeZone#getRawOffset(long) with the default implementation - 
return getRawOffset().
2) Add API TimeZone#getDSTSavings(long) with the default implementation 
- return getDSTSavings().
2) Update the default implementation of TimeZone#getOffset(long) to use 
#getRawOffset(long) #getDSTSavings(long) above along is existing 
3) Gregorian calendar to use #getRawOffset(long) and 
#getDSTSavings(long), if not ZoneInfo.

There is an IETF working draft for timezone service protocol. In future, 
someone may want to create a custom time zone, interacting with a 
timezone service server to get the latest time zone rule data, instead 
of applying tzdata update utility. In this case, such limitation becomes 
a blocker. There are several ways to resolve the issue - I just 
suggested one way in the bug report. Are there anyone willing to look 
into this?


More information about the i18n-dev mailing list