TL:DR
private static final DateTimeFormatter dateFormatter
= DateTimeFormatter.ofPattern("dd/MM/yyyy");
private static String formatDate(String dateString) {
return Instant.parse(dateString)
.atZone(ZoneId.of("Pacific/Tarawa"))
.format(dateFormatter);
}
You need to specify time zone
Given your string 2018-01-18T13:52:49.107Z
the method above returns 19/01/2018
. 19?? Yes, when it’s 13:52 UTC it’s already the following day on the Tarawa Atoll. And since it is never the same date everywhere on Earth, you need to specify the time zone in which you want the date. So please substitute your desired time zone if it didn’t happen to be Pacific/Tarawa. For example Africa/Maputo or Asia/Sakhalin. Then you will get the date in that zone, formatted as specified. It will not always coincide with the date in the string (in this case Jan 18, 2018) because the string gives the date and time in UTC. To use the user’s time zone you may try specifying ZoneId.systemDefault()
. This will use the JVM’s time zone setting. Beware that it is fragile, the setting may be changed under your feet from other parts of your program or other programs running in the same JVM. If you did intend to have the date in UTC as in the string, use:
return Instant.parse(dateString)
.atOffset(ZoneOffset.UTC)
.format(dateFormatter);
Now the result is guaranteed to be 18/01/2018
.
java.time
I am using java.time
, the modern Java date and time API. I recommend you do the same. The Date
class is long outdated, and SimpleDateFormat
is not only that, it is also notoriously troublesome. The modern API is so much nicer to work with.
Question: Can I use java.time on Android?
Yes, you can use java.time
on Android. It just requires at least Java 6.
- In Java 8 and later and on newer Android devices the modern API comes built-in.
- In Java 6 and 7 get the ThreeTen Backport, the backport of the new 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.
What went wrong in your code?
The main error in your method (worse that using the outdated classes) is between the following two lines!
} catch (ParseException e) {
}
An empty catch
block swallows exceptions so you don’t get to see what goes wrong. Never do that. Try for example:
} catch (ParseException e) {
System.out.println("Message: " + e.getMessage());
System.out.println("Error offset: " + e.getErrorOffset());
if (e.getErrorOffset() != -1) {
System.out.println("Error text: " + dateString.substring(e.getErrorOffset()));
}
}
This prints:
Message: Unparseable date: "2018-01-18T13:52:49.107Z"
Error offset: 5
Error text: 01-18T13:52:49.107Z
So your formatter cannot parse 01
, the month. Check the documentation, it says about month: “ If the number of pattern letters is 3 or more, the month is interpreted as text;…”. So lets try MM
instead of MMM
in the format pattern string. Now we get:
Message: Unparseable date: "2018-01-18T13:52:49.107Z"
Error offset: 10
Error text: T13:52:49.107Z
The T
is offending. Of course, it’s not in the pattern string. To indicate that a literal letter is part of the format, enclose it apostrophes: yyyy-MM-dd'T'HH:mm:ss.S
. Next time your method returns:
18/01/2018
Are we through? I’d say not. You are ignoring the Z
at the end. It means offset 0 from UTC or “Zulu time zone”. By ignoring it, you are parsing the date-time string as a date-time in your JVM’s default time zone, which gives an incorrect time. In newer Java versions the Z
can be parsed using the format pattern letter uppercase X
, either one, two or three of them. Try yyyy-MM-dd'T'HH:mm:ss.SXXX
:
Message: Unparseable date: "2018-01-18T13:52:49.107Z"
Error offset: 22
Error text: 7Z
It seems that one S
matches two digits, 10
, but not the third decimal, 7
. I don’t know why, but let’s put three, SSS
for three decimals: yyyy-MM-dd'T'HH:mm:ss.SSSXXX
. Now it works.
Links