0

Java 8 here, I have the following code:

public class PossibleBug {

  public static void main(String[] args) {
    new PossibleBug().run();
  }

  public void run() {

    buildDate("20181205");

  }

  public Date buildDate(final String yyyyMmDd) throws ParseException {
    TimeZone expectedTz = TimeZone.getTimeZone("America/New_York");
    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
    sdf.setTimeZone(expectedTz);

    TimeZone actualTz = sdf.getTimeZone();

    Date answer = sdf.parse(yyyyMmDd);
    return answer;
  }

}

So pretty basic stuff:

  1. create a SimpleDateFormat and set its timezone to EST
  2. Use the SDF to parse a date string
  3. Result should be a date in EST as well

However at runtime, look at the debugger results:

enter image description here

How is this possible?!?! sdf.parse(yyyyMmDd) is returning a date formatted in GMT. Is there something I'm missing on my end or is this a bug in SimpleDateFormat?

I am able to invoke buildDate and run it from inside a different class and it seems to work fine:

enter image description here

yole
  • 92,896
  • 20
  • 260
  • 197
hotmeatballsoup
  • 385
  • 6
  • 58
  • 136
  • 3
    You've explicitly tagged with [java-8], so *don't* use `Date` and `SimpleDateFormat`. Use classes from `java.time`. – Andy Turner Jan 21 '20 at 14:56
  • This works fine for me. Output is `Wed Dec 05 00:00:00 EST 2018`. So something is wrong with your environment. – Sean Bright Jan 21 '20 at 14:57
  • 05.12.2018 00:00 interpreted as being in America/New_York is 05:00 in GMT as it is printed in your example. – Smutje Jan 21 '20 at 15:00
  • 1
    I recommend you don’t use `TimeZone`, `SimpleDateFormat` and `Date`. Those classes are poorly designed and long outdated, `SimpleDateFormat` in particular notoriously troublesome. Instead use `ZoneId`, `LocalDate` and `DateTimeFormatter`, all from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). – Ole V.V. Jan 22 '20 at 04:56

3 Answers3

4

Date stores no timezone. It's essentially just a wrapper around a long, storing millis after epoch.

When you print it (or when your debugger invokes the toString() method to get a string representation to display), your JVM's default timezone is used, irrespective of how it was created.

Date, despite the name, doesn't model a date: it's an instant in time.

Given that your input is "20181205", don't use Date: use classes from java.time like java.time.LocalDate.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • Thanks @Andy Turner (+1) for reasons outside the scope of this question, I *must* use `Date`. But it sounds like you're saying that the `answer` is timezone-less, and that the reason why I'm seeing GMT in the printed output is because IDEA is just selecting my JVM's default TZ? – hotmeatballsoup Jan 21 '20 at 15:01
  • 2
  • OK thanks, however if I simply instantiate a `PossibleBug` instance from inside _another_ class, and then run its `buildDate(...)` method with the same exact input arg string (`"20181205"`), it seems to work perfectly. Any chance you can explain why there would be a difference in the print/debugger output in the 2nd screenshot? – hotmeatballsoup Jan 21 '20 at 15:09
  • 1
    Are you setting the default timezone in that code? – Andy Turner Jan 21 '20 at 15:10
  • No :-/ thats what is so weird about this... – hotmeatballsoup Jan 21 '20 at 15:14
  • Also @Andy Turner I think you may have a wrong notion about Java `Date`s being timezone-less. Check out this answer [here](https://stackoverflow.com/a/2891412/5235665), seems to indicate the pre-Java 8 `Date`s can absolutely have timezones – hotmeatballsoup Jan 21 '20 at 15:29
  • 4
    @hotmeatballsoup that answer does the same as your question’s code, it uses a timezone when parsing the date. At no point does it indicate that the resulting `Date` object has a timezone. So in your case, your debugger formats the date differently. That might be strange, but entirely irrelevant for your application. – Holger Jan 21 '20 at 15:33
  • Thanks @Holger (+1) but if Dates can't hold a timezone, then whats the point of setting the timezone in the SimpleDateFormat? – hotmeatballsoup Jan 21 '20 at 15:41
  • 1
    You are converting a date string to a `Date` object which is actually an instant. Likewise, when you are formatting this instant called `Date` to an actual date/time string, again a timezone will be used. As you've seen yourself, it makes a difference. The same instant becomes `Wed Dec 05 00:00:00` when formatting to EST but `Wed Dec 05 05:00:00` when formatting to GMT. – Holger Jan 21 '20 at 15:49
  • Right, so by setting the timezone on the SimpleDateFormat, I should be telling it to **always** format the instant/Date with the EST timezone. So why is it ignoring EST in one class (and instead using GMT) and honoring the `setTimeZone(...)` call in another? – hotmeatballsoup Jan 21 '20 at 15:56
  • 1
    @hotmeatballsoup have a look at what `TimeZone.getDefault()` is in both places. *You* might not be setting it, but maybe something else is (such is the problem with mutable global state). – Andy Turner Jan 21 '20 at 16:15
1

If you take a look at the Java-Doc for SimpleDateFormat.parse(), you can see that the TimeZone might be overwritten:

The TimeZone value may be overwritten, depending on the given pattern and the time zone value in text. Any TimeZone value that has previously been set by a call to setTimeZone may need to be restored for further operations.

Sean Bright
  • 118,630
  • 17
  • 138
  • 146
Atgoogat
  • 66
  • 4
0

The documentation says: "This parsing operation uses the calendar to produce a Date. All of the calendar's date-time fields are cleared before parsing, and the calendar's default values of the date-time fields are used for any missing date-time information. For example, the year value of the parsed Date is 1970 with GregorianCalendar if no year value is given from the parsing operation. The TimeZone value may be overwritten, depending on the given pattern and the time zone value in text. Any TimeZone value that has previously been set by a call to setTimeZone may need to be restored for further operations."

In short, SimpleDateFormat is a formatter/parser, not a utility for performing time zone conversions. If there's no TZ in the string you are parsing, you get the default value from Calendar.

Consider what would happen if you called setTimeZone, then parsed a string that actually contained a time zone itself? What would you expect to happen?

Also, note that Date doesn't contain a time zone. It's specifically defined as being the number of milliseconds since January 1, 1970, 00:00 UTC. Library functions apply a time zone when needed (like when converting to a String) and if you don't specify one, you'll get the default time zone. You see GMT because your default time zone is GMT or because your IDE always displays Date objects in GMT, and the person who said he got EST must have is default time zone set to EST.

In your case, you're parsing a string that does not contain a time zone at all. In fact, it doesn't even contain a time. Using Date to handle, uh, dates (I realize this is confusing, I mean dates without times), is likely to lead to mistakes, especially when your default time zone isn't UTC/GMT. I recommend using LocalDate and the LocalDate.parse method.

Willis Blackburn
  • 8,068
  • 19
  • 36