In Java, SimpleDateFormat
uses the JVM default timezone if you don't set one in it. (check what's yours with TimeZone.getDefault()
).
So 1993-10-06T00:00+02:00
is converted to 1993-10-05T23:00+01:00
probably because the default timezone is one with +01:00
offset in October 1993, while in October 1996 it was in DST (+02:00
). My guess is Europe/Paris
, but it can be other, as lots of timezones uses CET as a short name.
Anyway, just check the history of DST in Paris and note that in October 1993 the offset was +01:00
while in October 6th 1996 it was +02:00
. So it's a good guess, but any timezone with the same rules will have the same behaviour.
Also, +02:00
is an offset, not a timezone. Just being +02:00
doesn't necessarily mean that it's CET during DST, because there's more than one timezone that uses this offset. And short names like CET
are ambiguous and not standard, so you should consider using IANA timezones names (always in the format Region/City, like America/Sao_Paulo
or Europe/Paris
).
Anyway, if you don't want to have variable offsets, you shouldn't rely on the JVM default timezone, because it can have DST effects and the offset will vary according to the date (and the default timezone can be changed without notice, even at runtime). One way to avoid it, is to set a fixed offset in the formatter:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSZZZZZ");
// set the offset +02:00, so all dates will be formatted using this
// (instead of the current offset for the JVM default timezone)
sdf.setTimeZone(TimeZone.getTimeZone("GMT+02:00"));
Date date = sdf.parse("1993-10-06T00:00:00.0000000+02:00");
System.out.println(sdf.format(date));
Just some quick notes:
SimpleDateFormat
doesn't work well with more than 3 digits after the decimal point. In the case above, it works fine because it's all zeroes, but if you have any value different from zero and more than 3 digits, you can have strange, wrong, unexpected results. In this case, you should remove the extra digits, because this class simply can't handle more than 3 (and it also doesn't work well for formatting).
- I'm testing with JDK 7, so the pattern
ZZZZZ
doesn't work for parsing. Instead, I've used yyyy-MM-dd'T'HH:mm:ss.SSSSSSSXXX
, which parses the inputs above, and formats the date to 1993-10-06T00:00:00.0000000+02:00
(but note that the X
is not available in JDK 6)
- If you want the output in another offset, just change it accordingly in the
getTimeZone
method. If you want UTC, use getTimeZone("UTC")
Java new Date/Time API
The old classes (Date
, Calendar
and SimpleDateFormat
) have lots of problems and design issues, and they're being replaced by the new APIs.
In Android, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. To make it work, you'll also need the ThreeTenABP (more on how to use it here).
One improvement in this new API is the support to nanoseconds (up to 9 digits after the decimal point), so it can handle your inputs without the problems of SimpleDateFormat
.
This new API also has lots of new date/time types suited for different situations. In this case, you have a date and time in a specific offset, and wish to maintain it. So, the best class is a org.threeten.bp.OffsetDateTime
:
OffsetDateTime odt = OffsetDateTime.parse("1993-10-06T00:00:00.0000000+02:00");
System.out.println(odt.toString()); // 1993-10-06T00:00+02:00
Note that the toString()
method omits the seconds and nanoseconds if they are zero. If you want the output exactly like the input (with 7 digits after the decimal point), just create a org.threeten.bp.format.DateTimeFormatter
:
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSXXX");
System.out.println(fmt.format(odt)); // 1993-10-06T00:00:00.0000000+02:00
To change this to another offset (or to UTC), use a org.threeten.bp.ZoneOffset
:
// convert to UTC
odt = odt.withOffsetSameInstant(ZoneOffset.UTC);
System.out.println(fmt.format(odt)); // 1993-10-05T22:00:00.0000000Z
// convert to another offset (+01:00)
odt = odt.withOffsetSameInstant(ZoneOffset.ofHours(1));
System.out.println(fmt.format(odt)); // 1993-10-05T23:00:00.0000000+01:00