TimeZone with calendars confuses results

Recently, I have been working with time zone conversions and am very surprised at the result I get. Basically, I want to convert the date from one time zone to another. below code, conversions work fine, but what I observed while debugging, the date will not be converted unless I call Calendar#get(Calendar.FIELD) .

  private static void convertTimeZone(String date, String time, TimeZone fromTimezone, TimeZone toTimeZone){ Calendar cal = Calendar.getInstance(fromTimezone); String[] dateSplit = null; String[] timeSplit = null; if(time !=null){ timeSplit = time.split(":"); } if(date!=null){ dateSplit = date.split("/"); } if(dateSplit !=null){ cal.set(Calendar.DATE, Integer.parseInt(dateSplit[0])); cal.set(Calendar.MONTH, Integer.parseInt(dateSplit[1])-1); cal.set(Calendar.YEAR, Integer.parseInt(dateSplit[2])); } if(timeSplit !=null){ cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(timeSplit[0])); cal.set(Calendar.MINUTE, Integer.parseInt(timeSplit[1])); } // System.out.println("Time in " + fromTimezone.getDisplayName() + " : " + cal.get(Calendar.DATE) +"/"+ (cal.get(Calendar.MONTH)+1)+"/"+ cal.get(Calendar.YEAR) +" " + ((cal.get(Calendar.HOUR_OF_DAY)<10) ? ("0"+cal.get(Calendar.HOUR_OF_DAY) ): (cal.get(Calendar.HOUR_OF_DAY))) // +":" + (cal.get(Calendar.MINUTE)<10 ? "0"+cal.get(Calendar.MINUTE) : cal.get(Calendar.MINUTE)) ); cal.setTimeZone(toTimeZone); System.out.println("Time in " + toTimeZone.getDisplayName() + " : " + cal.get(Calendar.DATE) +"/"+ (cal.get(Calendar.MONTH)+1)+"/"+ cal.get(Calendar.YEAR) +" " + ((cal.get(Calendar.HOUR_OF_DAY)<10) ? ("0"+cal.get(Calendar.HOUR_OF_DAY) ): (cal.get(Calendar.HOUR_OF_DAY))) +":" + (cal.get(Calendar.MINUTE)<10 ? "0"+cal.get(Calendar.MINUTE) : cal.get(Calendar.MINUTE)) ); } public static void main(String[] args) throws ParseException { convertTimeZone("23/04/2013", "23:00", TimeZone.getTimeZone("EST5EDT"), TimeZone.getTimeZone("GB")); } 

Expected Result: Time in Greenwich Mean Time : 24/4/2013 04:00

The conclusion I got when I comment on sysout 1: Time in Greenwich Mean Time : 23/4/2013 23:00

If I do not comment on sysout1, I get the expected valid output.

Any help is appreciated

+4
source share
5 answers

The internal representation of a given date is not evaluated until it is really needed, that is, until you try to access it with these getters. However, the best way to parse is SimpleDateFormat.


EDIT (added for a summary of the comments below and to better clarify my answer).

The calendar works this way to increase efficiency: instead of recounting everithing every time you call setter, it waits until you call getter.

The calendar should be used mainly for calculating the date (see add() and roll() ), but you use it for parsing and formatting: these tasks are better done with SimpleDateFormat, so I say that your use of the Calendar is not elegant.

See this example:

 private static void convertTimeZone(String date, String time, TimeZone fromTimezone, TimeZone toTimeZone) throws ParseException { SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy HH:mm"); df.setTimeZone(fromTimezone); Date d = df.parse(date + " " + time); df.setTimeZone(toTimeZone); System.out.println("Time in " + toTimeZone.getDisplayName() + " : " + df.format(d)); } 

I repeated your method only with SimpleDateFormat. My method is smaller, there is no splitting logic (it is hidden in parse() ), and it is also processed in a simpler way. In addition, the date format is expressed in a compact and standard way that can be easily internationalized using the ResourceBundle.

Also note that time zone conversion is just a formatting task: the internal representation of the syntax date does not change.

+3
source

The answer is partially explained in the comment in setTimeZone() :

Consider the sequence of calls: cal.setTimeZone (EST); cal.set (HOUR, 1); cal.setTimeZone (PST). Is β€œ1 hour” EST or 1 hour PST set? Answer: PST. More commonly, the setTimeZone () call affects set () calls BEFORE AND AFTER moving to the next complete () call.

In other words, the sequence

  • Set calendar time
  • Change TimeZone

interpreted as "Use this time in this new time zone", and the sequence

  • Set calendar time
  • Get time (or part of it)
  • Change time zone

will be interpreted as "Use this time in the old time zone and then change it."

So, in your case, to get the behavior you are hoping for, you will need to call get() or any other method that internally calls complete() inside Calendar before you change the time zone.

+1
source

Are you limited to using TimeZone and Calendar? If not, I suggest using the excellent JodaTime library, which greatly simplifies the processing of time zones.

Your example would look like this:

 public static void convertTimeZoneJoda(String date, String time, DateTimeZone fromTimezone, DateTimeZone toTimeZone) { DateTimeFormatter dtf = DateTimeFormat.forPattern("dd/MM/yyyyHH:mm").withZone(fromTimezone); DateTime dt = dtf.parseDateTime(date+time).withZone(toTimeZone); System.out.println("Time in " + toTimeZone.getID() + " : " + dt.toString()); } public static void main(String[] args) { convertTimeZoneJoda("23/04/2013", "23:00", DateTimeZone.forID("EST5EDT"), DateTimeZone.forID("GMT")); } 

JodaTime also allows you to have a comfortable conversation between TimeZone and DateTimeZone.

0
source

To add an answer to @Pino , the reason why get() does not return the updated time is because setTimeZone() just does not update the fields, just sets areAllFieldsSet to false, which is useless since get() does not check it. If you ask me, this is very poorly encoded from the SUN part. Here is the competent code from Calendar :

setTimeZone()

  public void setTimeZone(TimeZone value){ zone = value; sharedZone = false; areAllFieldsSet = areFieldsSet = false; } 

get()

 protected final int internalGet(int field){ return fields[field]; } 
0
source

This code works for me to convert to UTC:

  • create a calendar object with a UTC time zone

     Calendar utcTime = Calendar.getInstance(TimeZone.getTimeZone("GMT")); 
  • set the point in time in the UTC calendar object from the "any time zone" calendar object

     utcTime.setTimeInMillis(myCalendarObjectInSomeOtherTimeZone.getTimeInMillis()); 
  • utcTime will now contain the same point in time as myCalendarObjectInSomeOtherTimeZone converted to UTC.

0
source

All Articles