Holger Rattenscheid
Holger Rattenscheid

Reputation: 197

Convert UTC java.sql.Time to java.time.localtime with correct DST

I have a problem to convert a java.sql.Time (UTC) which is fetched from a database to a java.time.LocalTime (GMT+1 DST). It is always missing the DST hour. So like a Time of 03:00 is only converted to a LocalTime of 04:00 instead of 05:00.

//Saved UTC time in DB: 03:00
LocalTime.ofInstant(Instant.ofEpochMilli(sqlTime.getTime()), ZoneId.of("Europe/Berlin"));
=> 04:00 //expected 05:00

I guess the problem is that java.sql.Time saves the time with a default date of 1970-01-01 and in 1970 there was no DST in Germany. But of course the time should be shown for today and not for 1970.

So how can I get the correct time for this example?

Upvotes: 3

Views: 3589

Answers (2)

Anonymous
Anonymous

Reputation: 86333

Assuming that you are using at least JDBC 4.2, you should be able to retrieve a LocalTime from your result set:

    LocalTime timeInUtc = yourResultSet.getObject(yourTimeColumn, LocalTime.class);

Then there’s no need bother with the outdated and poorly designed java.sql.Time class. The time you get will still be in UTC, of course. Here’s how to convert:

    LocalTime timeInUtc = LocalTime.of(3, 0);

    ZoneId zone = ZoneId.of("Europe/Berlin");
    LocalTime timeInGermany = OffsetDateTime.now(ZoneOffset.UTC)
            .with(timeInUtc)
            .atZoneSameInstant(zone)
            .toLocalTime();

    System.out.println("Zeit heute in Deutschland: " + timeInGermany);

When I ran the code today I got the output you expected:

Zeit heute in Deutschland: 05:00

Edit: If there’s no way you can avoid getting a java.sql.Time, convert it to LocalTime first. Assuming that the Time is in UTC and we don’t want to rely on a fragile JVM time zone setting for conversion, you are correct that we need the getTime method:

    Time sqlTimeInUtc = // Get from database
    LocalTime timeInUtc 
            = LocalTime.MIDNIGHT.plus(sqlTimeInUtc.getTime(), ChronoUnit.MILLIS);

If you could rely on the JVM time zone setting also being UTC, the following would be nicer:

    LocalTime timeInUtc = sqlTimeInUtc.toLocalTime();

In both cases the rest is as above.

In all cases there are some corner cases around the question whether you want “today in UTC” or “today in Europe/Berlin time zone” when you say “the time should be shown for today”. There’s also a corner case if the time is between 2 and 3 AM and today is the last Sunday in March, where the clocks are turned forward from 2 to 3 to initiate summer time (DST) in Germany. Please think these corner cases through and decide what you want.

By the way your diagnosis is completely correct: Time.getTime returns the time of day on Jan 1, 1970, so when you feed this into an Instant, you are converting the time of day on this date, that is, without summer time.

Upvotes: 5

sirain
sirain

Reputation: 1168

As far as I understand it your question is: Given a time in UTC convert it to local time according to the current time offset. This time offset is different depending of whether DST is in effect or not.

A possible approach is to determine the current offset using TimeZone:

    TimeZone tz = TimeZone.getTimeZone("Europe/Berlin");
    int timeZoneOffsetMillis = tz.getOffset(new Date().getTime());

Now timeZoneOffsetMillis contains the number of milliseconds you have to add to your UTC time to get local time.

You can get a LocalTime like this:

    LocalTime localTime = LocalTime.ofNanoOfDay((sqlTime.getTime() + timeZoneOffsetMillis) * 1000000L);

If your time is only accurate to seconds instead of nanoseconds anyway you might want to use LocalTime.ofSecondOfDay.

Upvotes: 1

Related Questions