Search code examples
javasimpledateformatdate-format

SimpleDateFormat ignoring TimeZone when also present in String


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.


Solution

  • 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.