Java: How do you convert a UTC timestamp to local time?

Date has no timezone and internally stores in UTC. Only when a date is formatted is the timezone correction applies. When using a DateFormat, it defaults to the timezone of the JVM it's running in. Use setTimeZone to change it as necessary.

DateFormat utcFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
utcFormat.setTimeZone(TimeZone.getTimeZone("UTC"));

Date date = utcFormat.parse("2012-08-15T22:56:02.038Z");

DateFormat pstFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
pstFormat.setTimeZone(TimeZone.getTimeZone("PST"));

System.out.println(pstFormat.format(date));

This prints 2012-08-15T15:56:02.038

Note that I left out the 'Z' in the PST format as it indicates UTC. If you just went with Z then the output would be 2012-08-15T15:56:02.038-0700


Use the modern Java date & time API, and this is straightforward:

    String inputValue = "2012-08-15T22:56:02.038Z";
    Instant timestamp = Instant.parse(inputValue);
    ZonedDateTime losAngelesTime = timestamp.atZone(ZoneId.of("America/Los_Angeles"));
    System.out.println(losAngelesTime);

This prints

2012-08-15T15:56:02.038-07:00[America/Los_Angeles]

Points to note:

  • There’s a little bug in your expectations. The Z in your timestamp means UTC, also known as Zulu time. So in your local time value, the Z should not be there. Rather you would want a return value like for example 2012-08-15T15:56:02.038-07:00, since the offset is now -7 hours rather than Z.
  • Avoid the three letter time zone abbreviations. They are not standardized and therefore most often ambiguous. PST, for example, may mean Philppine Standard Time, Pacific Standard Time or Pitcairn Standard Time (though S in an abbreviation is often for summer time (meaning DST)). If you intended Pacific Standard Time, that isn’t even a time zone, since in summer (where your sample timestamp falls) Pacific Daylight Time is used instead. Instead of the abbreviations use time zone IDs in the format region/city as in my code.
  • Timestamps are generally best handled as Instant objects. Convert to ZonedDateTime only when you have a need, like for presentation.

Question: Can I use the modern API with my Java version?

If using at least Java 6, you can.

  • In Java 8 and later the new API comes built-in.
  • In Java 6 and 7 get the ThreeTen Backport, the backport of the new classes (that’s ThreeTen for JSR-310, where the modern API was first defined).
  • On Android, use the Android edition of ThreeTen Backport. It’s called ThreeTenABP, and I think that there’s a wonderful explanation in this question: How to use ThreeTenABP in Android Project.