Converting local time to UTC time or vice versa considering daylight saving time

32,923

Solution 1

Are you sure you know how to convert dates to UTC and back? Correctly?
I am afraid, I doubt that.

  1. Yes.
  2. You don't need to convert, you just need to assign correct TimeZone.
  3. What you need an article for? OK, I am working on this, but for now let me put an answer here.

The first thing first. Your program should store Date (or Calendar) in UTC TimeZone internally. Well, in fact in GMT, because there are no leap seconds in Java, but that is another story.
The only place when you should be in need of "converting", is when you are going to display the time to user. That regards to sending email messages as well. In both cases you need to format date to get its textual representation. To that you would use DateFormat and assign correct TimeZone:

    // that's for desktop application
    // for web application one needs to detect Locale
    Locale locale = Locale.getDefault();
    // again, this one works for desktop application
    // for web application it is more complicated
    TimeZone currentTimeZone = TimeZone.getDefault();
    // in fact I could skip this line and get just DateTime instance,
    // but I wanted to show how to do that correctly for
    // any time zone and locale
    DateFormat formatter = DateFormat.getDateTimeInstance(
            DateFormat.DEFAULT,
            DateFormat.DEFAULT,
            locale);
    formatter.setTimeZone(currentTimeZone);

    // Dates "conversion"
    Date currentDate = new Date();
    long sixMonths = 180L * 24 * 3600 * 1000;
    Date inSixMonths = new Date(currentDate.getTime() + sixMonths);

    System.out.println(formatter.format(currentDate));
    System.out.println(formatter.format(inSixMonths));
    // for me it prints
    // 2011-05-14 16:11:29
    // 2011-11-10 15:11:29

    // now for "UTC"
    formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
    System.out.println(formatter.format(currentDate));
    System.out.println(formatter.format(inSixMonths));
    // 2011-05-14 14:13:50
    // 2011-11-10 14:13:50

As you can see, Java cares about handling DST. You can of course handle it manually, just read the TimeZone related JavaDoc.

Solution 2

Here is the best solution that I've found. I'm copying it here, but the solution came from http://biese.wordpress.com/2014/02/28/the-easy-way-to-convert-local-time-to-utc-time/.

package com.test.timezone;

import java.util.TimeZone;

public final class Utility {
    public static final TimeZone utcTZ = TimeZone.getTimeZone("UTC");

    public static long toLocalTime(long time, TimeZone to) {
        return convertTime(time, utcTZ, to);
    }

    public static long toUTC(long time, TimeZone from) {
        return convertTime(time, from, utcTZ);
    }

    public static long convertTime(long time, TimeZone from, TimeZone to) {
        return time + getTimeZoneOffset(time, from, to);
    }

    private static long getTimeZoneOffset(long time, TimeZone from, TimeZone to) {
        int fromOffset = from.getOffset(time);
        int toOffset = to.getOffset(time);
        int diff = 0;

        if (fromOffset >= 0){
            if (toOffset > 0){
                toOffset = -1*toOffset;
            } else {
                toOffset = Math.abs(toOffset);
            }
            diff = (fromOffset+toOffset)*-1;
        } else {
            if (toOffset <= 0){
                toOffset = -1*Math.abs(toOffset);
            }
            diff = (Math.abs(fromOffset)+toOffset);
        }
        return diff;
    }
}

package com.test.timezone;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;

public class TestTimezone {

    public static void main(String[] args) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy MMM dd HH:mm:ss zzzz");
        Calendar date1 = new GregorianCalendar(2014,0,15,10,0,0);
        System.out.println(sdf.format(date1.getTime())+"\n");
        long utcTimeStamp = Utility.toUTC(date1.getTimeInMillis(), date1.getTimeZone());
        Calendar utcCal = Calendar.getInstance();
        utcCal.setTimeInMillis(utcTimeStamp);
        System.out.println("toUTC: "+sdf.format(utcCal.getTime())+"\n");

        System.out.println("---------------------------------------");
        Calendar date2 = new GregorianCalendar(2014,2,15,10,0,0);
        System.out.println(sdf.format(date2.getTime())+"\n");
        utcTimeStamp = Utility.toUTC(date2.getTimeInMillis(), date2.getTimeZone());
        utcCal.setTimeInMillis(utcTimeStamp);
        System.out.println("toUTC: "+sdf.format(utcCal.getTime())+"\n");

        System.out.println("---------------------------------------");
        Calendar date3 = new GregorianCalendar(2014,11,25,9,0,0);
        System.out.println(sdf.format(date3.getTime())+"\n");
        long uTime = Utility.toUTC(date3.getTimeInMillis(), date3.getTimeZone());
        System.out.println("utcTimeStamp: "+uTime+"\n");
        long lTime = Utility.toLocalTime(uTime, TimeZone.getTimeZone("EST"));
        Calendar locCal = Calendar.getInstance();
        locCal.setTimeInMillis(lTime);
        System.out.println("toLocal: "+sdf.format(locCal.getTime())+"\n");

        System.out.println("---------------------------------------");
        Calendar date4 = new GregorianCalendar(2014,6,4,9,0,0);
        System.out.println(sdf.format(date4.getTime())+"\n");
        uTime = Utility.toUTC(date4.getTimeInMillis(), date4.getTimeZone());
        System.out.println("utcTimeStamp: "+uTime+"\n");
        lTime = Utility.toLocalTime(uTime, TimeZone.getTimeZone("EST"));
        locCal = Calendar.getInstance();
        locCal.setTimeInMillis(lTime);
        System.out.println("toLocal: "+sdf.format(locCal.getTime())+"\n");
    }
}

Solution 3

The code in TALE's answer can be simplified:

public final class Utility {
    public static long toLocalTime(long time, TimeZone to) {
        return time + to.getOffset(time);
    }

    public static long toUTC(long time, TimeZone from) {
        return time - from.getOffset(time);
    }
}
Share:
32,923
Newbie
Author by

Newbie

Updated on July 09, 2022

Comments

  • Newbie
    Newbie almost 2 years

    I know how to convert local time to UTC time and vice versa. But I am very much confused about daylight savings time(DST) handling while doing this.

    So can anyone answer the below questions:
    1. Does java internally handle DST when converting between timezones?
    2. What things I need to do while converting between timezones?
    3. Any good article which explains about this more clearly?

    Thanks in advance.

  • Paweł Dyda
    Paweł Dyda about 13 years
    "In six months" should be "in around six months", but it does not really matter. The hour part is what matters.
  • Bas Goossen
    Bas Goossen over 7 years
    This is bad practice since you're converting the time base (Unix Epoch) leading to storing time in a localized matter. Date actually handle's these "conversions" in fact representations for you. If you use the method you describe and use the time as a date as in = new Date(time) the Date object assumes the time is GMT, and the object is wrongly instanced. The only valid use of these functions is to convert time from and to external systems that do not operate in GMT timestamps and hence do not comply to the UNIX Timestamp convention.