The first thing is that you're using mm:ss.mmm
in your format. According to SimpleDateFormat
javadoc, m
represents the minutes, so you must change it to mm:ss.SSS
because S
represents the milliseconds.
Another detail is that the Z
in the end is the timezone designator for UTC and it can't be ignored (at least it shouldn't). You must use the corresponding pattern for that, which is X
:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
Date date = sdf.parse("2017-08-02T06:05:30.000Z");
PS: the X
pattern was introduced in Java 7. If you're using Java <= 6, the only alternative is to treat Z
as a literal (an ugly workaround, I admit) and set the UTC as the timezone used by the parser:
// treat "Z" as literal
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
// use UTC as timezone
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = sdf.parse("2017-08-02T06:05:30.000Z");
With this, the date will have the value corresponding to 06:05 in UTC. To format the time to your timezone, you must use another SimpleDateFormat
with the corresponding timezone:
// output format: hour:minute AM/PM
SimpleDateFormat outputFormat = new SimpleDateFormat("hh:mm a", Locale.ENGLISH);
// assuming a timezone in India
outputFormat.setTimeZone(TimeZone.getTimeZone("Asia/Kolkata"));
System.out.println(outputFormat.format(date));
The output will be:
11:35 AM
If you don't set a timezone, it'll use the system's default. But the default can be changed without notice, even at runtime, so it's better to explicity set a specific timezone as above.
I also used java.util.Locale
to set the language to English, because some locales can have different symbols for AM/PM. If you don't specify one, it'll use the system default and it's not guaranteed to be one in which the symbols are the ones you need (some locales uses "a.m./p.m." or another different formats, so it's better to use an explicit locale).
Java new Date/Time API
The old classes (Date
, Calendar
and SimpleDateFormat
) have lots of problems and design issues, and they're being replaced by the new APIs.
If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.
If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it here).
The code below works for both.
The only difference is the package names (in Java 8 is java.time
and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp
), but the classes and methods names are the same.
To parse the input you can use the ZonedDateTime
class, which has full support to timezones and it makes the conversion to another zones very easy. Then you use a DateTimeFormatter
to format the output:
// parse the input
ZonedDateTime parsed = ZonedDateTime.parse("2017-08-02T06:05:30.000Z");
// convert to another timezone
ZonedDateTime z = parsed.withZoneSameInstant(ZoneId.of("Asia/Kolkata"));
// format output
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("hh:mm a", Locale.ENGLISH);
System.out.println(fmt.format(z));
The output will be:
11:35 AM
If the input always has Z
in the end, you can also use the Instant
class:
// parse the input
Instant instant = Instant.parse("2017-08-02T06:05:30.000Z");
// convert to a timezone
ZonedDateTime z = instant.atZone(ZoneId.of("Asia/Kolkata"));
Note that I used hh
for the hours: this will format using values from 1 to 12 (it makes sense because I'm also using the AM/PM designators). If you want values from 0 to 23, use HH
instead - check the javadoc for more details.
Also note that the API uses IANA timezones names (always in the format Region/City
, like Asia/Kolkata
or Europe/Berlin
).
Avoid using the 3-letter abbreviations (like CST
or IST
) because they are ambiguous and not standard.
You can get a list of available timezones (and choose the one that fits best your system) by calling ZoneId.getAvailableZoneIds()
.
You can also use the system's default timezone with ZoneId.systemDefault()
, but this can be changed without notice, even at runtime, so it's better to explicity use a specific one.