Weird timezone issues with Timestamp.valueOf(String s)
Martin Buchholz
martinrb at google.com
Fri Feb 9 17:15:32 UTC 2018
[redirect to core-libs-dev]
On Fri, Feb 9, 2018 at 7:08 AM, <dwfranken at gmail.com> wrote:
> Not that I encourage using date/time classes from the packages of either
> java.util or java.sql, but sometimes it happens under the hood.
>
>
>
> We found a weird issue with timestamps.
>
> In our (PostgreSQL) database we have a column of SQL type TIMESTAMP - no
> timezone.
>
>
>
> When JPA fills our entity field with a java.sql.Timestamp value, it is
> given
> an inherent timezone of the system it's running on; in our case it was CET
> (UTC +1).
>
> Now this timezone isn't immediately obvious, because if you print it to
> system out, it seems to have the correct time. However when you convert it
> with .toInstant() the timezone rears its ugly head.
>
>
>
> I digged deeper and found Timestamp.valueOf(String s), it does a lot of
> magic, but in the end it calls its own deprecated constructor new
> Timestamp(int year, int month, int date, int hour, int minute, int second,
> int nano).
>
> That constructor calls the deprecated constructor of java.util.Date(int
> year, int month, int date, int hour, int minute, int second) and that is
> the one doing something with the current timezone on the system.
>
> The method Timestamp.valueOf(String s) itself however, is not deprecated
> and
> I find this odd.
>
> More odd is that Timestamp.valueOf(LocalDateTime dateTime) has a
> @SuppresWarnings("deprecation") annotation.
>
>
>
> Thus I found that Timestamp.valueOf("2017-10-04 00:00:00") on a system
> running in CET timezone yields a different result than one running in UTC
> timezone.
>
> Here is some example code where I put the output of the println's in
> comments.
>
>
>
>
> TimeZone.setDefault(TimeZone.getTimeZone(ZoneId.of("Europe/Amsterdam")));
>
> Timestamp fromStringCet = Timestamp.valueOf("2017-10-04 00:00:00");
>
> System.out.println(fromStringCet); // 2017-10-04 00:00:00.0
>
> System.out.println(fromStringCet.toInstant()); // 2017-10-03T22:00:00Z
>
>
>
> TimeZone.setDefault(TimeZone.getTimeZone(ZoneOffset.UTC));
>
> Timestamp fromStringUtc = Timestamp.valueOf("2017-10-04 00:00:00");
>
> System.out.println(fromStringUtc); // 2017-10-04 00:00:00.0
>
> System.out.println(fromStringUtc.toInstant()); // 2017-10-04T00:00:00Z
>
>
>
> System.out.println(fromStringCet.equals(fromStringUtc)); // false
>
>
>
> LocalDateTime localDateTime = LocalDateTime.of(2017, 10, 4, 0, 0, 0);
>
>
>
>
> TimeZone.setDefault(TimeZone.getTimeZone(ZoneId.of("Europe/Amsterdam")));
>
> Timestamp fromLocalDateTimeCet = Timestamp.valueOf(localDateTime);
>
> System.out.println(fromLocalDateTimeCet.toInstant()); //
> 2017-10-03T22:00:00Z
>
>
>
> TimeZone.setDefault(TimeZone.getTimeZone(ZoneOffset.UTC));
>
> Timestamp fromLocalDateTimeUtc = Timestamp.valueOf(localDateTime);
>
> System.out.println(fromLocalDateTimeUtc.toInstant()); // 2017-10-04
> 00:00:00.0
>
>
>
> System.out.println(fromLocalDateTimeCet.equals(fromLocalDateTimeUtc));
> // false
>
>
>
> So what to do?
>
>
>
> Some options are:
>
> * Make Timestamp.valueOf(String s) deprecated?
> * Always use UTC when doing the implicit conversion
>
>
>
> Thoughts?
>
>
>
> Dave Franken
>
>
>
>
More information about the core-libs-dev
mailing list