I am trying to convert UTC time to local time, including daylight saving. Localtime (Stockholm), in summer, should be 2 hours ahead of UTC, but when I convert it in Java it only adds one hour.
public class TimeConverter {
public static void main(String[] args) throws ParseException {
getLocalTime("2:36:10 AM");
}
public static String getLocalTime(String utcTime) throws ParseException {
DateFormat utc = new SimpleDateFormat("hh:mm:ss a");
utc.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = utc.parse(utcTime);
DateFormat local = new SimpleDateFormat("HH:mm:ss");
local.setTimeZone(TimeZone.getDefault());
System.out.println(utc.getTimeZone());
System.out.println(local.getTimeZone());
System.out.println("UTC TIME\t" + utcTime);
System.out.println("LOCAL TIME\t" + local.format(date));
return local.format(date);
}
}
Output:
sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
sun.util.calendar.ZoneInfo[id="Europe/Stockholm",offset=3600000,dstSavings=3600000,useDaylight=true,transitions=143,lastRule=java.util.SimpleTimeZone[id=Europe/Stockholm,offset=3600000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]]
UTC TIME 2:36:10 AM
LOCAL TIME 03:36:10
tl;dr
Your code inadvertently used the moment of 1970-01-01T02:36:10Z, 2 AM on the first day of 1970 as seen in UTC. Then you adjusted to Stockholm time, still in 1970 when no DST was in effect. Thus an offset of one hour (+01:00) rather than the two hours (+02:00) that you expected.
Use modern java.time classes instead.
We see your 2 hour offset, as expected.
Avoid legacy date-time classes
You are using terrible date-time classes that are now legacy, supplanted years ago by the modern java.time classes defined in JSR 310.
Time-of-day in UTC makes no sense
Asking about a time of day alone in UTC makes no sense. You need a date as well as a time to determine a moment, to represent a point on the timeline.
When communicating data textually to represent a time, use standard ISO 8601 format. For a time of day that means simply 24-hour clock with padding zeros.
LocalDate&OffsetDateTimeApply a date to determine a moment. Perhaps you intended the current date as seen in UTC.
ZonedDateTimeAdjust to a time zone. Same moment, different wall-clock time & calendar.
Generate text in standard ISO 8601 format.
There we see the two-hour offset you expected.
Your code uses 1970
Your code failed because your
java.util.Dateobject represents 2 AM on the first day of 1970 as seen in UTC.Avoid calling
java.util.Date#toString. That method unfortunately applies the JVM‘s current default time zone while generating its text. That poor design decision muddies the water.For convenience, we convert your legacy
java.util.Dateobject to its modern replacement, ajava.time.Instantobject — both represent a moment as seen with an offset from UTC of zero hours-minutes-seconds. Fortunately,Instant#toStringtells the truth, unlikeDate#toString.Let's look at the time zone rules for Sweden for that moment.
So there was no Daylight Saving Time (DST) in effect in that Sweden time zone at that moment in 1970.
We can see in that list of transitions that between 1949-10-02 and 1980-04-06 the offset was +01:00. So no +02:00 as you expected.