0

I got a weird issue.

SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy", Locale.ENGLISH);

I call it with:

Date date = sdf.parse("06/05/2020");

and it show the "date" is Thu Jun 04 18:00:00 MDT 2020, that is, one day less.

Where is wrong for SimpleDateFormat?

I am using Android Studio 3.5.1 and Androidx.

Thanks in advance. Shawn

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Shawn
  • 530
  • 1
  • 5
  • 15
  • If you have not already done so, take a look at some of the related questions on this site, for example [this](https://stackoverflow.com/questions/60857872/simpledateformat-parsing-wrong-date) or [these](https://stackoverflow.com/search?q=%5Bjava%5D+SimpleDateFormat+wrong+date). – andrewJames Jun 16 '20 at 22:20
  • I recommend you don’t use `SimpleDateFormat` and `Date`. Those classes are poorly designed and long outdated, the former in particular notoriously troublesome. Instead use `LocalDate` and `DateTimeFormatter`, both from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). That will certainly also solve your problem. – Ole V.V. Jun 16 '20 at 23:59

2 Answers2

1

java.time and ThreeTenABP

The bullet-proof way is switching to java.time, the modern Java date and time API.

    DateTimeFormatter dateFormatter
            = DateTimeFormatter.ofPattern("MM/dd/yyyy", Locale.ENGLISH);

    LocalDate date = LocalDate.parse("06/05/2020", dateFormatter);

    System.out.println(date);

Output is:

2020-06-05

What went wrong in your code?

It’s certainly a time zone problem. However, it isn’t reproduced by naïvely running your code. There is something more going on, and we can only guess at exactly what. I’ll present a couple of ways that this might have happened.

The first possibility, the default time zone of your JVM might have changed underway. To demonstrate:

    TimeZone.setDefault(TimeZone.getTimeZone("Etc/UTC"));
    SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy", Locale.ENGLISH);
    Date date = sdf.parse("06/05/2020");

    TimeZone.setDefault(TimeZone.getTimeZone("America/Edmonton"));
    System.out.println(date);

Thu Jun 04 18:00:00 MDT 2020

In all fairness it’s not a full day wrong, it’s 6 hours. It’s bad enough, and certainly the day of week and day of month are wrong. Please note that the JVM’s time zone setting may have been changed from a completely different part of your program or from a different program running in the same JVM. What happened was: The SimpleDateFormat got the default time zone of the JVM, that is, UTC. So parsed the date into the first moment of June 5 in UTC. At this point in time, it’s still June 4 in North America. Next, when we print the Date, Date.toString() grabs the (changed) time zone of the JVM and uses it for rendering the string. Therefore we got the MDT time (North American Mountain Daylight Time), not the UTC time that had been used for parsing.

We don’t need something as drastic as setting the time zone for the entire JVM to demonstrate, though. It’s enough to set the time zone of the formatter.

    SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy", Locale.ENGLISH);
    sdf.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
    Date date = sdf.parse("06/05/2020");
    System.out.println(date);

When running in America/Edmonton time zone:

Thu Jun 04 18:00:00 MDT 2020

What happens is basically the same as above.

Question: Doesn’t java.time require Android API level 26?

java.time works nicely on both older and newer Android devices. It just requires at least Java 6.

  • In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in.
  • In non-Android Java 6 and 7 get the ThreeTen Backport, the backport of the modern classes (ThreeTen for JSR 310; see the links at the bottom).
  • On (older) Android use the Android edition of ThreeTen Backport. It’s called ThreeTenABP. And make sure you import the date and time classes from org.threeten.bp with subpackages.

Links

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • Thanks for the explanation. Yes, I set the time zone to sdf before calling its format(): sdf.setTimeZone(TimeZone.getTimeZone("UTC")); this resolves the issue. – Shawn Jun 17 '20 at 15:08
0

This is a common problem with TimeZone, you need to set the correct timezone to your sdf, something like:

sdf.setTimeZone(TimeZone.getTimeZone("GMT"))

If you set the right timezone, you'll see the right day :)

Hope it helps