Introduction
Java is very complacent. It often tries to save a developer from extra key presses by providing default values. Sometimes, it leads to undesired behavior.
Problem
Imagine in your application you have the "1970-01-01 00:00:00
" time string
received in a JSON response from a service. The time is a time of a certain event, let's say this is a time of registration for a certain user. The thing is that given only the above string
, you can say nothing about when the event actually happened. This is easy to check with the next code snippet:
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class Main {
public static void main(final String[] args) throws ParseException {
final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(format.parse("1970-01-01 00:00:00").getTime());
}
}
Expected output is 0
as the user in our example registered exactly at the epoch time and getTime()
returns the number of milliseconds since the epoch. However on my PC (which is currently in Riga), the output is -7200000
(i.e., 2 hours before epoch). And if you live in Los Angeles, the output will be 28800000
(i.e., 8 hours after epoch). What does it mean? Does the time of an event really depend on where we read it? Fortunately, no.
Cause
The problem is that when you have the "1970-01-01 00:00:00
" string
, you have only partial information about the time of the event, namely you don't know in which time zone the time of the event was recorded. And Java doesn't know either. So it "kindly" treats the event time in the default time zone. The default time zone for a Java application is the time zone of the platform the JVM is running on. For my PC it is "Europe/Helsinki
" so I see one result. For the PC in Los Angeles, time zone will be likely "America/Los_Angeles
", so there another output will be observed.
Solution
Whenever you parse a time string
, you should always take into consideration the time zone for which this time string
is specified. Sometimes, the string
already contains the time zone information. But if no, it is quite a bad idea to let Java make the assumption. Better to find in the documentation of a service from where you received the string
in which time zone the service provides dates and then specify this time zone explicitly every time parsing is performed. In the ideal world, every service which provides dates should default to UTC, but unfortunately some developers don't implement this best practice.
For the above case with the user, the next code will print the same result for every location, as expected:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
public class Main {
public static void main(final String[] args) throws ParseException {
final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
format.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(format.parse("1970-01-01 00:00:00").getTime());
}
}
Strictly speaking, UTC is not a time zone, but a time standard. It may be convenient to think about UTC as about a huge clock somewhere in the middle of the Universe, so for anyone in any place of the Universe (including our Earth), they show the same time which, obviously, stays the same also at summer/winter. Why UTC is considered as a timezone in Java (and not only there) — it's a different question and a reason for another article. For now to be more accurate, we can assume that whenever we speak about UTC as a time zone, we assume that "UTC" in this context means "any time zone which always has the same time as that huge UTC clock in the middle of the Universe".
Summary
To summarize, whenever you deal with string
s which contain date/time information:
- Try to avoid parsing/formatting in your application. In case of JSON, you can request date as a number of milliseconds. In this case, parsing is not needed at all. Although the disadvantage of this approach is that it's not human-readable.
- If the parsing is needed, make sure the
string
contains the time zone information and you consider it during parsing, e.g., the string
you receive is like "1970-01-01 00:00:00 UTC
" and the pattern you use for parsing is "yyyy-MM-dd HH:mm:ss Z
". - If the parsing is needed and no time zone information is contained in the
string
, check the documentation (or implementation) of the service which provides the time string
and find in which time zone one is provided. Make sure every time you deal with parsing/formatting, you specify the time zone explicitly (like in the example above). - Alternatively, instead of specifying explicitly, you can run JVM with the parameter which overrides the time zone with the required one, e.g.
-Duser.timezone=UTC
, or even set default timezone
globally for your application:
TimeZone.setDefault(TimeZone.getTimeZone("UTC"))
Of course, you can change your platform time zone to one which the service uses, but that's far not the best solution: if this is a local PC, then why not to use the proper time zone of the city you're living in; if it is a server, it's recommended to use UTC timezone always. Moreover, there is a possibility that one day your application will be running on another platform where there's another time zone. - If nothing is specified in the documentation of a service, assume the time
string
s are provided in UTC time zone. Here, it would be useful to contact the developers of the service and make sure they follow the best practice to provide times in UTC.