Java's SimpleDateFormat allows you to specify a TimeZone to be used when parsing a String to a Date.
This works as you'd expect when the String doesn't contain a timezone, but when a timezone is present it appears to do nothing.
The documentation doesn't seem to really explain how the TimeZone is used, either.
Example Code:
public class DateFormatTest {
public static void main(final String[] args) throws ParseException {
testBoth("HH:mm", "13:40");
testBoth("HH:mm z", "13:40 UTC");
}
private static void testBoth(final String dateFormatString, final String dateString) throws ParseException {
// First, work with the "raw" date format
DateFormat dateFormat = new SimpleDateFormat(dateFormatString);
parse(dateFormat, dateString);
// Now, set the timezone to something else and try again
dateFormat = new SimpleDateFormat(dateFormatString);
dateFormat.setTimeZone(TimeZone.getTimeZone("PST"));
parse(dateFormat, dateString);
}
private static void parse(final DateFormat dateFormat, final String dateString) throws ParseException {
System.out.println(MessageFormat.format("Parsed \"{0}\" with timezone {1} to {2}", dateString,
dateFormat.getTimeZone().getDisplayName(), dateFormat.parse(dateString)));
}
}
Example Output:
Parsed "13:40" with timezone Greenwich Mean Time to 01/01/70 13:40
Parsed "13:40" with timezone Pacific Standard Time to 01/01/70 22:40
Parsed "13:40 UTC" with timezone Greenwich Mean Time to 01/01/70 14:40
Parsed "13:40 UTC" with timezone Pacific Standard Time to 01/01/70 14:40
Note how for the first example, the Date changes - but for the second, it does not.
In 2019 nobody should care why the SimpleDateFormat
and TimeZone
classes behave the way they do since we should have given up on using those classes many years ago. Basil Bourque has given you the answer that you should want. This answer will try to satisfy your curiosity, but please don’t use it for getting SimpleDateFormat
to behave. You’ll be much better off not using that class.
I am assuming that your JVM time zone is Europe/London (we shall see that this matters). When I set my time zone this way and my locale to UK, I can reproduce your results exactly.
Parsed "13:40" with timezone Greenwich Mean Time to 01/01/1970, 13:40
Parsing 13:40 in your default time zone gives 13:40 in your default time zone, no surprises. Since Great Britain was at UTC offset +01:00 in the winter of 1970, this time is the same as 12:40 UTC (when not given a date, SimpleDateFormat
uses the default of Jan 1, 1970). When the output says Greenwich Mean Time
, it’s a bug.
Parsed "13:40" with timezone Pacific Standard Time to 01/01/1970, 22:40
In 1970 the West coast of the USA was 8 hours behind UTC, so 9 hours behind Britain. So when you tell SimpleDateFormat
to assume that 13:40 is in America/Los_Angeles time zone, it parses into a time of 13:40 PST, the same as 21:40 UTC or 22:40 British Time (America/Los_Angeles is how TimeZone
interprets PST, but it’s deprecated, don’t rely on it.) MessageFormat
uses your default time zone to print the time, therefore 22:40 is printed.
Parsed "13:40 UTC" with timezone Greenwich Mean Time to 01/01/1970, 14:40
Since (as mentioned) London was at offset +01:00 at this time, and since MessageFormat
uses your default time zone, 14:40 is the correct and expected output. dateFormat.parse(dateString)
parses into a Date
, another poorly designed and long outdated class. A Date
is a point in time and cannot hold a UTC offset (contrary to the OffsetTime
class that Basil Bourque is correctly using). And your observation is correct: SimpleDateFormat
uses the UTC
from the string for interpreting 13:40 into a point in time (not its time zone setting). MessageFormat
has no way of knowing that the time had earlier been parsed from 13:40 UTC.
Parsed "13:40 UTC" with timezone Pacific Standard Time to 01/01/1970, 14:40
Since SimpleDateFormat
uses the UTC
from the string for interpreting 13:40 into a point in time, we get the same time as above.