Why is Calendar returning the wrong hour with the

2019-06-16 19:07发布

问题:


The time and date returned are correct except for the hour, which is 1 hour less than what it should be.

I seem to be setting everything that is required to get the correct time and date:

 - I'm using Calendar.getInstance(), instead of new Date()
 - I'm setting the timezone of the Calendar instance with Timezone.getTimeZone
 - I'm using DateFormat and SimpleDateFormat to format the output


My timezone is Eastern Standard Time, aka UTC/GMT-5:00. None of these lines are having any effect:

 - cal.setTimeZone(TimeZone.getTimeZone(cal.getTimeZone().getDisplayName()));
 - cal.setTimeZone(TimeZone.getTimeZone("EST"));
 - cal.setTimeZone(TimeZone.getTimeZone("UTC"));
 - cal.setTimeZone(TimeZone.getTimeZone("GMT"));
 - cal.setTimeZone(TimeZone.getTimeZone("GMT-5:00"));

...but each of these options sets my desired timezone.


Here is my attempt, where I am erroneously adding 1 hour to the Calendar instance:

PrintWriter output = null;

try {
  output = new PrintWriter(
   new BufferedWriter(new FileWriter("output.txt", true)));

  DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss:ms MM/dd/yyyy");
  Calendar cal = Calendar.getInstance();

  // ...doesn't seem to be working:
  cal.setTimeZone(TimeZone.getTimeZone(cal.getTimeZone().getDisplayName()));

  /*
   * adding an extra hour here, to make up for the incorrect hour value???
   * ...without this line, everything is correct except the hour = n - 1:
   */
  //cal.setTimeInMillis(cal.getTimeInMillis() + (1000 * 60 * 60));

  // printing to console here:
  System.out.println(dateFormat.format(cal.getTime()));
  System.out.println(cal.getTimeZone().getDisplayName());

  // printing to the log-file here:
  output.println(dateFormat.format(cal.getTime()));
  output.println(cal.getTimeZone().getDisplayName());

} catch (IOException e) {
  e.printStackTrace();

} finally {
  if (output != null) {
    output.close();
  }
}


Output:

10:05:43:543 10/10/2013
GMT-05:00

WRONG -- It's supposed to be 11:05:43:543! (p.s. -- I unfortunately can't use Joda-Time)

回答1:

1) Set the TimeZone in SimpleDateFormat

2) Either use UTC, or a "real" Time Zone like ("Austria/Vienna");
(Country name, and biggest city in TimeZone look it up to be sure)
EST (=UTC-5) is not a time zone very suitable for computational purpose, because it is the time without daylight saving.

One more example from central europe:
In Winter we use MEZ (CET), in Summer (Daylight savings) we use (MESZ = CEST).
But you want that your computer caluclates that for you, so don't use that:

TimeZones are geo poilitical, therfore the name of the country is needed.
Each country can decide to change its time zone when they want (e.g russia some time ago, and spain is discussing now.)



回答2:

Calendar does not account for daylight savings.

According to this post: How to tackle daylight savings using Timezone in java

The 3-letter abbreviations should be wholeheartedly avoided in favour of TZDB zone IDs. EST is Eastern Standard Time - and Standard time never observes DST; it's not really a full time zone name. It's the name used for part of a time zone. (Unfortunately I haven't come across a good term for this "half time zone" concept.)

Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("EST"));

This is a good place to start: http://download.java.net/jdk8/docs/api/java/time/ZoneId.html



回答3:


For posterity, here is my improved code based on the post "How to tackle daylight savings using Timezone in java":

PrintWriter output = null;

try {
  output = new PrintWriter(
        new BufferedWriter(new FileWriter("output.txt", true)));

  // here are the relevant changes (a lot less code!):
  TimeZone zone = TimeZone.getTimeZone("America/New_York");
  SimpleDateFormat simpleFormat = new SimpleDateFormat("HH:mm:ss:ms MM/dd/yyyy");
  simpleFormat.setTimeZone(zone);

  // printing to console here:
  System.out.println(simpleFormat.format(new Date()));
  System.out.println(simpleFormat.getTimeZone().getDisplayName());

  // printing to the log-file here:
  output.println(simpleFormat.format(new Date()));
  output.println(simpleFormat.getTimeZone().getDisplayName());

} catch (IOException e) {
  e.printStackTrace();

} finally {
  if (output != null) {
    output.close();
  }
}


Note that I'm using SimpleDateFormat instead of DateFormat, to customize the output format (don't need to use both).