0

I have one time string format like yyyy-MM-dd'T'HH:mm:ss.SSSSSS with the time zone "GMT+05:30". I need to convert this format in yyyy-MM-dd'T'HH:mm:ss.SSS'Z' format with different time zone - "GMT0:00". I write below funtion to convert time string with different timezone. It works fine but can't change time.

public static String getUTCDate(String dateString) {
    String oldDateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS";
    String newDateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";

    String result = "";
    SimpleDateFormat simpleDateFormatOld;
    SimpleDateFormat simpleDateFormatNew;

    try {
        simpleDateFormatOld = new SimpleDateFormat(oldDateFormat,Locale.US);
        simpleDateFormatNew = new SimpleDateFormat(newDateFormat,Locale.US);
        simpleDateFormatNew.setTimeZone(TimeZone.getTimeZone("GMT+05:30"));
        result = simpleDateFormatNew.format(simpleDateFormatOld.parse(dateString));
    }
    catch(Exception e) {
        ExceptionHandler.handleException(e);
    }
    return result;
}

Example: I passed 2019-07-11T21:28:02.8469576 date time string. But in return I got 2019-07-11T21:28:02.846Z date time string without changing the time.

How can I update time of string?

Sharon Joshi
  • 498
  • 7
  • 19
Sudip Sadhukhan
  • 1,784
  • 12
  • 21
  • 1
    Try to use "hh" (small) in place of "HH" – Sanwal Singh Jul 08 '19 at 10:40
  • Not working @Shane. – Sudip Sadhukhan Jul 08 '19 at 10:51
  • which is your input time zone?. you should give both the input and output time zone to the SimpleDateFormat – Sharon Joshi Jul 08 '19 at 12:54
  • 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. Jul 08 '19 at 13:52
  • 1
    FYI, the troublesome date-time classes such as `java.util.Date`, `java.util.Calendar`, and `java.text.SimpleDateFormat` are now legacy, supplanted by the [*java.time*](https://docs.oracle.com/javase/10/docs/api/java/time/package-summary.html) classes. Most of the *java.time* functionality is back-ported to Java 6 & Java 7 in the [***ThreeTen-Backport***](http://www.threeten.org/threetenbp/) project. Further adapted for earlier Android (<26) in [***ThreeTenABP***](https://github.com/JakeWharton/ThreeTenABP). See [*How to use ThreeTenABP…*](http://stackoverflow.com/q/38922754/642706). – Basil Bourque Jul 08 '19 at 20:55
  • 1
    ***Never* use `'Z'` with quote-marks** in a formatting pattern. The quote-marks mean "ignore this letter Z". But the `Z` provides vital information; don't ignore it! The Z, pronounced "Zulu", means UTC, or more specifically an offset-from-UTC of zero hours-minutes-seconds. Knowing the offset (or a time zone) gives meaning to the date and the time, enabling us to determine a moment. Without the context of a zone or offset, we would not know if, for example, "noon on the 23rd of January this year" means noon in Tokyo, noon in Paris, or noon in Montréal — all very different moments, hours apart. – Basil Bourque Jul 08 '19 at 20:57

4 Answers4

1

Try this:

  public static String getUTCDate(String dateString) {
    String oldDateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS";
    String newDateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";

    String result = "";
    SimpleDateFormat simpleDateFormatOld;
    SimpleDateFormat simpleDateFormatNew;

    try {
        simpleDateFormatOld = new SimpleDateFormat();
        simpleDateFormatOld.setTimeZone(TimeZone.getTimeZone("GMT+05:30"));
        simpleDateFormatOld.applyPattern(oldDateFormat);
        simpleDateFormatNew = new SimpleDateFormat(newDateFormat, Locale.US);
        result = simpleDateFormatNew.format(simpleDateFormatOld.parse(dateString));
    }
    catch(Exception e) {
        e.printStackTrace();
    }
    return result;
}

It worked for me. It just sets the timezone of the simpleDataFormatOld object before applying the date pattern to it.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Varun Sharma
  • 33
  • 1
  • 4
  • Please explain how it might solve the problem (also asking because I don’t think it will). – Ole V.V. Jul 08 '19 at 14:01
  • @OleV.V. Did you try it? It worked for me. It just sets the timezone of the simpleDataFormatOld object before applying the date pattern to it. – Varun Sharma Jul 09 '19 at 22:05
  • Code-only answers are seldom helpful. It’s from the explanations that we all learn. So thank you supplying a little bit of that. I have pasted it into your answer for you. – Ole V.V. Jul 10 '19 at 05:58
  • I hadn’t tried until now. I pass `2019-07-11T21:28:02.8469576` to your method and expect `2019-07-11T15:58:02.846Z`. I get `2019-07-12T03:19:11.576Z`. So no. – Ole V.V. Jul 10 '19 at 15:51
1

For most purposes you should not want to convert a point in time from one string format in one time zone into a different string format in a different time zone. In your program keep your date and time in proper date-time objects, not strings (just like you wouldn’t keep an integer or floating-point value in a string). If you just need the point in time (not the original GMT offset, +05:30), the Instant class is the correct one to use. When your program accepts a string input, parse and convert it to Instant first thing and keep it as such. Only when you need to give string output, format the time back into a string and pass it out.

java.time and ThreeTenABP

Parse and convert input

    ZoneId originalZone = ZoneId.of("Asia/Kolkata");

    Instant time = LocalDateTime.parse("2019-07-11T21:28:02.8469576")
            .atZone(originalZone)
            .toInstant();

    System.out.println(time);

The converted time prints as:

2019-07-11T15:58:02.846957600Z

For most purposes, don’t give time zone as a naked GMT offset. A named time zone better explains to the reader why this zone was chosen and is more future-proof in case the offset is changed (which happens more often than you would think). I am exploiting the fact that your string is in ISO 8601 format. In this case we don’t need to supply an explicit formatter. BTW your example string has 7 decimals on the seconds and your oldDateFormat seems to want 6. It doesn’t matter here since LocalDateTime.parse accepts anything from 0 through 9 decimals.

Format output

The output you asked for is a different variant of ISO 8601. The output above resembles pretty well because it too is ISO 8601, only there are too many decimals. So let’s apply an explicit formatting this time:

    ZoneOffset newOffset = ZoneOffset.UTC;
    DateTimeFormatter newFormatter = new DateTimeFormatterBuilder()
            .append(DateTimeFormatter.ISO_LOCAL_DATE)
            .appendLiteral('T')
            .appendPattern("HH:mm:ss.SSSX")
            .toFormatter();

    String formattedUtcDateTime = time.atOffset(newOffset).format(newFormatter);
    System.out.println(formattedUtcDateTime);

2019-07-11T15:58:02.846Z

We see that java.time, the modern Java date and time API, forces us to specify the time zone offset, so forgetting to do so (like it seems you did in the code in the question, causing the unexpected output) simply is not possible.

I recommend against SimpleDateFormat and TimeZone

The date-time classes that you tried to use, SimpleDateFormat and TimeZone, are poorly designed and long outdated, the former in particular notoriously troublesome. Also there is no way that SimpleDateFormat can parse 6 or 7 decimals on the seconds correctly; it supports only milliseconds, exactly three decimals. Instead I am using java.time, the modern Java date and time API. I find it so much nicer to work with.

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) the modern API comes built-in.
  • In Java 6 and 7 get the ThreeTen Backport, the backport of the modern 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
0

EDIT: Try this method:

    public static String getUTCDate(String dateString) {
    Log.d(TAG,"input : "+dateString);
    String oldDateFormat = "yyyy-MM-dd'T'HH:mm:ss";
    String newDateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";

    String result = "";
    SimpleDateFormat simpleDateFormatOld;
    SimpleDateFormat simpleDateFormatNew;

    try {
        simpleDateFormatOld = new SimpleDateFormat(oldDateFormat, Locale.US);
        simpleDateFormatNew = new SimpleDateFormat(newDateFormat,Locale.US);
        simpleDateFormatOld.setTimeZone(TimeZone.getTimeZone("GMT+05:30"));
        simpleDateFormatNew.setTimeZone(TimeZone.getTimeZone("GMT0:00"));
        result = simpleDateFormatNew.format(simpleDateFormatOld.parse(dateString));
    }
    catch(Exception e) {
        ExceptionHandler.handleException(e);
    }
    return result;
}

There are 2 changes: 1. Set time zone for New format also.

simpleDateFormatNew.setTimeZone(TimeZone.getTimeZone("GMT0:00"));
  1. Remove milliseconds pattern from oldDateFormat.

    String oldDateFormat = "yyyy-MM-dd'T'HH:mm:ss";

Rajat Mehra
  • 1,462
  • 14
  • 19
  • 1
    FYI, the terribly troublesome date-time classes such as [`java.util.Date`](https://docs.oracle.com/javase/10/docs/api/java/util/Date.html), [`java.util.Calendar`](https://docs.oracle.com/javase/10/docs/api/java/util/Calendar.html), and `java.text.SimpleDateFormat` are now [legacy](https://en.wikipedia.org/wiki/Legacy_system), supplanted by the [*java.time*](https://docs.oracle.com/javase/10/docs/api/java/time/package-summary.html) classes built into Java 8 and later. See [*Tutorial* by Oracle](https://docs.oracle.com/javase/tutorial/datetime/TOC.html). – Basil Bourque Jul 08 '19 at 20:55
0

I have solved this issue by adding time zone for oldDateFormat also.

For example:

simpleDateFormatOld.setTimeZone(TimeZone.getTimeZone("GMT+5:30"));
simpleDateFormatNew.setTimeZone(TimeZone.getTimeZone("GMT+0:00"));
Sudip Sadhukhan
  • 1,784
  • 12
  • 21
  • Thanks for showing your own solution to the problem. It should work. If you insist on using the poorly designed and outdated `TimeZone` class, the ID to use is `Etc/GMT` (`GMT+0:00` is not recommended when the former ID exists just as `GMT+5:30` is not recommend when you can use `Asia/Kolkata` or `Asia/Colombo`). – Ole V.V. Jul 09 '19 at 03:53