问题
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?
回答1:
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.
回答2:
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
.
来源:https://stackoverflow.com/questions/52966281/convert-utc-java-sql-time-to-java-time-localtime-with-correct-dst