Malawirel
Malawirel

Reputation: 115

How to convert UTC date to local date in Java 1.6

I get a string with the UTC timestamp from an XML file like this:

2019-05-24T00:59:55.1-05:00

I convert the string to a date with the method below.

    private Date getUTC2JavaDateB(String dateStr) {
    String dateS = dateStr;
    String timePattern = "";
    Date d = null;
    SimpleDateFormat formatter = null;
    if (dateS.length() >= 19) {
        dateS = dateS.substring(0, 19);
    }
    timePattern = "yyyy-MM-dd'T'hh:mm:ss";
    formatter = new SimpleDateFormat(timePattern);
    d = formatter.parse(dateS, new ParsePosition(0));

    return d;
}

Now here is my problem. I want to convert it from UTC to local time.

I found some code snippets in some other threads but they do not yet work for me.

    private Date toLocalTimeStamp(Date date) {
    try {

        DateFormat utcFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        utcFormat.setTimeZone(TimeZone.getTimeZone("UTC"));

        DateFormat currentTFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        currentTFormat.setTimeZone(TimeZone.getTimeZone(getCurrentTimeZone()));

        return currentTFormat.format(date);
    } catch (Exception ex) {
        return date;
    }
}

I get the exception "String cannot be converted to Date" - which is clear to me. But how do I convert the String to a Date now? And is my method of converting UTC to local date correct?

Any help is greatly appreciated! :-)

Upvotes: 0

Views: 2603

Answers (3)

T.J. Crowder
T.J. Crowder

Reputation: 1075785

Your string already has a timezone indicator on it. If you're stuck with Java 6 for some reason (see my comment), you can easily update the string slightly and then parse it in its entirety, including the timezone, with SimpleDateFormat:

// Remove the colon in the timezone indicator
assert str.charAt(str.length() - 3) == ':';
str = str.substring(0, str.length() - 3) + str.substring(str.length() - 2);
// Parse the result
Date dt = (new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")).parse(str);

Live Example

Upvotes: 0

GPI
GPI

Reputation: 9348

If you're stuck at Java 6 and do not have a more recent time-handling library (Joda Time, 310 backport, ...)...

Using XML date handling methods

The date you are trying to parse is ISO-8601 formatted, which is what the XML DateTime data type uses.

Java 6 shipped with JAXB implementations, that provides many tools for XML processing, including parsing ISO 8601 date types. Of particular interest here is the GregorianXMLCalendar

So this will give you a java.util.Date :

java.util.Date date = DatatypeFactory.newInstance()
  .newXMLGregorianCalendar("2019-05-24T00:59:55.1-05:00")
  .toGregorianCalendar()
  .getTime()

That you will then be able to display and format in your zone of interest...

    // What is the UTC hour of 00:59:55.1 at -5 offset ?
    SimpleDateFormat utc = new SimpleDateFormat("HH:mm");
    utc.setTimeZone(TimeZone.getTimeZone("UTC"));
    System.out.println(utc.format(date)); // --> 05:59

    // What would it be in Paris ?
    SimpleDateFormat paris = new SimpleDateFormat("HH:mm");
    paris.setTimeZone(TimeZone.getTimeZone("Europe/Paris"));
    System.out.println(paris.format(date)); // -> 07:59 Paris is UTC+2 in the summer, so it's OK.

Upvotes: 0

Anonymous
Anonymous

Reputation: 86389

ThreeTen Backport

import org.threeten.bp.OffsetDateTime;
import org.threeten.bp.ZoneId;
import org.threeten.bp.ZonedDateTime;

public class ConvertUtcDateTimeToLocalInJava16 {

    public static void main(String[] args) {
        String dateStr = "2019-05-24T00:59:55.1-05:00";
        OffsetDateTime dateTime = OffsetDateTime.parse(dateStr);
        ZonedDateTime dateTimeInMyTimeZone
                = dateTime.atZoneSameInstant(ZoneId.systemDefault());
        System.out.println(dateTimeInMyTimeZone);
    }

}

I ran this program on Java 1.7.0_67. The output is:

2019-05-24T07:59:55.100+02:00[Europe/Copenhagen]

I don’t have Java 1.6 installed myself, but running the program at least proves that it doesn’t need Java 1.8. I dare claim that it will run on your Java 1.6 too. You just need to add ThreeTen Backport to your project. Use the link at the bottom.

ThreeTen Backport is the backport of java.time, the modern Java date and time API, to Java 6 and 7.

I am exploiting the fact that your date-time string is in ISO 8601 format, the format that the classes from java.time parse (and also print) as their default. As you may know, the -05:00 at the end of your string is a UTC offset meaning 5 hours behind UTC (so your time of 00:59:55.1 corresponds to 05:59:55.1 UTC).

Even on Java 1.6 you don’t need to use Date and SimpleDateFormat. Those classes were always poorly designed, the latter in particular is notoriously troublesome. So consider not using them.

Specify time zone if you can

ZoneId.systemDefault() gives you the JVM’s time zone setting. This ought to be the “local time zone” that you asked for, but is fragile since the setting can be changed by another part of your program or any other program running in the same JVM at any time. If you have a way of knowing the correct time zone, it is better to specify it explicitly. For example:

    ZonedDateTime dateTimeInMyTimeZone
            = dateTime.atZoneSameInstant(ZoneId.of("Africa/Algiers"));

2019-05-24T06:59:55.100+01:00[Africa/Algiers]

What went wrong for you?

First I suspect that your expectations are wrong. Both of your conversion methods return a Date, but a Date cannot have a time zone, so that will be for no use. A Date is a point in time, nothing more, nothing less. Contrary to Date java.time offers an OffsetDateTime, a date and time with a UTC offset, and ZonedDateTime, a date and time with a time zone.

Lowercase hh in your format pattern string is incorrect. It is for hour within AM or PM from 01 through 12. The hour in your string is on a 24 hour clock, from 00 through 23. For this you need uppercase HH as in your second method.

Next you are not parsing the UTC offset from your string, thus probably getting an incorrect time (SimpleDateFormat uses your default time zone in this case). That you also ignore the decimal (.1 in your example) is a detail introducing an error of less than a second. There is no way that SimpleDateFormat can parse one decimal on the seconds correctly anyway (it only supports exactly three decimals).

Links

Upvotes: 1

Related Questions