For most purposes you should not want to convert a point in time from one string format in one time zone into a different string format in a different time zone. In your program keep your date and time in proper date-time objects, not strings (just like you wouldn’t keep an integer or floating-point value in a string). If you just need the point in time (not the original GMT offset, +05:30), the Instant
class is the correct one to use. When your program accepts a string input, parse and convert it to Instant
first thing and keep it as such. Only when you need to give string output, format the time back into a string and pass it out.
java.time and ThreeTenABP
Parse and convert input
ZoneId originalZone = ZoneId.of("Asia/Kolkata");
Instant time = LocalDateTime.parse("2019-07-11T21:28:02.8469576")
.atZone(originalZone)
.toInstant();
System.out.println(time);
The converted time prints as:
2019-07-11T15:58:02.846957600Z
For most purposes, don’t give time zone as a naked GMT offset. A named time zone better explains to the reader why this zone was chosen and is more future-proof in case the offset is changed (which happens more often than you would think). I am exploiting the fact that your string is in ISO 8601 format. In this case we don’t need to supply an explicit formatter. BTW your example string has 7 decimals on the seconds and your oldDateFormat
seems to want 6. It doesn’t matter here since LocalDateTime.parse
accepts anything from 0 through 9 decimals.
Format output
The output you asked for is a different variant of ISO 8601. The output above resembles pretty well because it too is ISO 8601, only there are too many decimals. So let’s apply an explicit formatting this time:
ZoneOffset newOffset = ZoneOffset.UTC;
DateTimeFormatter newFormatter = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral('T')
.appendPattern("HH:mm:ss.SSSX")
.toFormatter();
String formattedUtcDateTime = time.atOffset(newOffset).format(newFormatter);
System.out.println(formattedUtcDateTime);
2019-07-11T15:58:02.846Z
We see that java.time, the modern Java date and time API, forces us to specify the time zone offset, so forgetting to do so (like it seems you did in the code in the question, causing the unexpected output) simply is not possible.
I recommend against SimpleDateFormat and TimeZone
The date-time classes that you tried to use, SimpleDateFormat
and TimeZone
, are poorly designed and long outdated, the former in particular notoriously troublesome. Also there is no way that SimpleDateFormat
can parse 6 or 7 decimals on the seconds correctly; it supports only milliseconds, exactly three decimals. Instead I am using java.time, the modern Java date and time API. I find it so much nicer to work with.
Question: Can I use java.time on Android?
Yes, java.time works nicely on 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 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