1

I'm having trouble converting a UTC date string to local date string in Android. Example in case is, I've input UTC date string as 2018-02-28T02:42:41Z

I'm in PST timezone but when I tried to convert to local timezone it doesn't change. I get February 28, 2018 02:42 AM which is incorrect.

public static String convertUTCtoLocalTime(String utcDateString) throws Exception{

    SimpleDateFormat dateFormatter;

    //create a new Date object using the UTC timezone
    Log.v(TAG, "Input utc date:" + utcDateString);
    dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
    dateFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
    Date utcDate = dateFormatter.parse(utcDateString);

    //Convert the UTC date to Local timezone
    dateFormatter = new SimpleDateFormat("MMMM dd, yyyy hh:mm a");
    dateFormatter.setTimeZone(TimeZone.getDefault());
    String formattedDate = dateFormatter.format(utcDate);
    Log.v(TAG, "convertUTCtoLocalTime: local time:" + formattedDate);

    return lv_dateFormateInLocalTimeZone;
}

I don't fully understand the difference but similar conversion works in "swift" as shown below,

    static func utcDateToLocal(utcdate: String) -> String {
      let dateFormatter = DateFormatter()

      dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" //Input Format 2018-02-28T02:42:41Z
      dateFormatter.timeZone = NSTimeZone(name: "UTC")! as TimeZone
      let UTCDate = dateFormatter.date(from: utcdate)

      dateFormatter.dateFormat = "MMMM dd, yyyy hh:mm a" // Output Format
      dateFormatter.timeZone = TimeZone.current
      let UTCToCurrentFormat = dateFormatter.string(from: UTCDate!)
      return UTCToCurrentFormat
  }

Swift example gives me out "February 27, 2018 06:42 PM" which is the correct one.

Swift is using v3.x and

Android is, Studio 3.1.1 Build #AI-173.4697961, built on April 3, 2018 JRE: 1.8.0_152-release-1024-b01 x86_64 JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o Mac OS X 10.13.4

I'm running both examples on the same machine. Any hints to solve issue on Android would be really useful.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Atarang
  • 422
  • 1
  • 6
  • 22
  • I don't know why (which is why I'm not writing an answer), but `getDefault()` is returning a UTC timezone rather than PST in this case. If you switch out `getDefault()` for `getTimeZone("PST")`, it returns the correct value. – pushasha Apr 17 '18 at 16:27
  • https://stackoverflow.com/a/17678000/5167909 – Faysal Ahmed Apr 17 '18 at 16:37
  • As an aside consider throwing away the long outmoded and notoriously troublesome `SimpleDateFormat` and friends, and adding [ThreeTenABP](https://github.com/JakeWharton/ThreeTenABP) to your Android project in order to use `java.time`, the modern Java date and time API. It is so much nicer to work with. – Ole V.V. Apr 17 '18 at 16:57
  • Your conversion is correct. On my computer it passes `convertUTCtoLocalTime: local time:February 27, 2018 06:42 PM` to `Log.v()`. Your problem is that your method returns the value of a variable that you haven’t touched in the method, `lv_dateFormateInLocalTimeZone`, so it cannot possibly hold the converted value. Another thing, are you sure that `TimeZone.getDefault()` returns PST? (And not Philippine Standard Time?? At least three time zones are called PST.) – Ole V.V. Apr 17 '18 at 17:15
  • lv_dateFormateInLocalTimeZone variable is not used. If I set, `TimeZone tz =TimeZone.getTimeZone("America/Los_Angeles"); dateFormatter.setTimeZone(tz);` This prints correct local date string. How do I get the correct timezone while my application running on a mobile device other than `TimeZone.getDefault();?` – Atarang Apr 18 '18 at 01:05
  • `lv_dateFormateInLocalTimeZone` is used in the code you have posted, its value is returned from your method. If `TimeZone.getDefault()` doesn’t give you what you want, there is no other way to get the setting of the device. It gives you the JVM’s time zone setting. This setting may have been changed from another part of your program or another program running in the JVM. If it hasn’t, it will be the device’s setting unless something has been done to set it differently when the JVM was launched. – Ole V.V. Apr 18 '18 at 08:25

1 Answers1

1
static DateTimeFormatter formatter = DateTimeFormatter
        .ofLocalizedDateTime(FormatStyle.LONG, FormatStyle.SHORT)
        .withLocale(Locale.US);

public static String convertUTCtoLocalTime(String utcDateString) {

    Log.v(TAG, "Input utc date:" + utcDateString);

    String formattedDateTime = Instant.parse(utcDateString)
            .atZone(ZoneId.systemDefault())
            .format(formatter);

    Log.v(TAG, "convertUTCtoLocalTime: local time:" + formattedDateTime);

    return formattedDateTime;
}

Try it:

    System.out.println(convertUTCtoLocalTime("2018-02-28T02:42:41Z"));

Output:

February 27, 2018, 6:42 PM

Use the built-in localized formats rather than building your own format pattern string. It gives you some assurance that the format is right for the locale in question, it works in other locales too, and it saves you from the error-prone task of writing a correct format pattern string. In the US locale the long date format gives you the full month name (not just “Feb”), and the short time format gives you the time without seconds. In combination exactly the string I understood you wanted.

The Instant class parses your UTC string without any explicit formatter, which again saves you from building a format pattern string. The format is ISO 8601. The Instant class and the other date-time classes I use are part of java.time, the modern Java date and time API. These classes parse ISO 8601 as their default. And generally the modern API is much nicer to work with.

What went wrong in your code?

Your conversion is correct. When I ran it on my computer, it was passing the following string to Log.v(): convertUTCtoLocalTime: local time:February 27, 2018 06:42 PM. I believe that this was what you expected. I can only see two potential issues:

  1. If your default time zone — as obtained from TimeZone.getDefault() — isn’t what you think it is, you will get a wrong result. In this case, however, your Swift code would likely give the same wrong result, and my code above certainly will.
  2. Your return statement is:

         return lv_dateFormateInLocalTimeZone;
    

    You haven’t touched a variable named lv_dateFormateInLocalTimeZone in what goes before, so this variable cannot hold your formatted date-time. To return it you would need

         return formattedDate;
    

Question: Can I use java.time on Android?

Yes, java.time works nicely on older and newer Android devices. It just requires at least Java 6.

  • In Java 8 and later and on newer Android devices (from API level 26, I’m told) the modern API comes built-in.
  • In Java 6 and 7 get the ThreeTen Backport, the backport of the new classes (ThreeTen for JSR 310; see the links at the bottom).
  • On (older) Android use the Android edition of ThreeTen Backport. It’s called ThreeTenABP. And make sure you import the date and time classes from org.threeten.bp with subpackages.

Links

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