0

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! :-)

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Malawirel
  • 115
  • 6
  • 16
  • 1
    That timestamp isn't in UTC. It's in GMT-05:00 (sometimes called Eastern Standard Time). – T.J. Crowder May 24 '19 at 08:55
  • 1
    Why are you using Java 6? It's ***massively*** out of date. It was superceded by Java 7 nearly **eight years ago**. (The current version is 11, although for licensing reasons some folks are on 9 or 10.) This isn't just an idle question, Java 8 added the `java.time` package, which is dramatically more useful than `java.lang.Date` and its associated stuff. – T.J. Crowder May 24 '19 at 08:56
  • Try to take a look at joda-time library or if you can upgrade your jdk you can use LocalDate class – GJCode May 24 '19 at 08:58
  • Joda-Time is a decent suggestion, @GJCode, but Joda-Time is in maintenance mode. IMHO [ThreeTen Backport](https://www.threeten.org/threetenbp/), the backport of java.time, would be a still better suggestion. java.time is the modern Java date and time API, built-in from Java 8, so it is also the future-proof bet. In any case it is worth considering leaving `Date` and `SimpleDateFormat` far behind, They were always poorly designed. – Ole V.V. May 24 '19 at 09:14
  • An old-fashioned `Date` has got neither time zone nor UTC offset, so your question doesn’t make perfect sense. See for example [Calendar returns date in wrong time zone](https://stackoverflow.com/questions/26678030/calendar-returns-date-in-wrong-time-zone) and [DateFormat parse - not return date in UTC](https://stackoverflow.com/questions/47307909/dateformat-parse-not-return-date-in-utc). – Ole V.V. May 24 '19 at 09:17
  • @OleV.V. of course java.time is the best choice, I supposed that he needs to use java 1.6 for some reason – GJCode May 24 '19 at 09:20
  • Yes, @GJCode, we agree. I was just trying to say that java.time *does work on Java 1.6* when you use the backport. – Ole V.V. May 24 '19 at 09:20

3 Answers3

1

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

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
0

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

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 1
    `assert` was introduced in Java 1.4. – Ole V.V. May 24 '19 at 09:48
  • Yes, currently I am stuck with Java 6 but I will update to Java 8 (due to the licensing issues that were discussed above) in the next version of my application. The developer who provided the XML told me that the timestamps in the XML are in UTC, although - as you pointed out - they contain a timezone indicator. Total mess but now I have to live with it. – Malawirel May 24 '19 at 12:31
  • @Malawirel - They aren't, but hopefully the above helps you handle them. :-) – T.J. Crowder May 24 '19 at 12:33
  • *I will update to Java 8* So much more reason to use ThreeTen Backport until then. After upgrading you may change your import statements, retest and you’re on the built-in java.time and can skip the dependency on the backport again. Just my 2 øre… – Ole V.V. May 24 '19 at 15:12
0

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.
GPI
  • 9,088
  • 2
  • 31
  • 38