0

In one of my module I have to achieve following.

I am getting date from server in yyyy-MM-dd'T'HH:mm:ss'Z' format. I have to convert this date and current date in PST and then show difference between those two in hours/mins/seconds.

For converting current time into PST I have written following code

        Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("America/Los_Angeles"));

        Date currentLocalTime = cal.getTime();
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", LocaleManager.getLocale());


        TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
        format.setTimeZone(tz);
        String localTime = format.format(currentLocalTime);
        Date currentDate = format.parse(localTime);

Here current time is getting converted into string (localTime in code) but when I am parsing this string to get Date object, I am getting date in my timezone only.

My question is if formatter is formatting date in different timezone then while parsing why same date is not being converted into Date object?

Is there any other way to get difference between two dates (current and once received from server) in milliseconds?

silwar
  • 6,470
  • 3
  • 46
  • 66

2 Answers2

3

Lots of issues here, let's see:

So first you should parse the whole string, including the UTC information. And then you use another formatter to convert it to another timezone:

// parse the date/time with Z
SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
Date date = parser.parse("2018-03-21T10:00:00Z");

// convert to another timezone
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
format.setTimeZone(tz);
String formattedDate = format.format(date); // 2018-03-21T03:00:00-07:00

But I'd recommend you to use the threeten backport - see how to use it in Android here (or just use the java.time classes, if your API level has it).

This API is much better and easier:

// parse input
Instant instant = Instant.parse("2018-03-21T10:00:00Z");

// convert to timezone
ZonedDateTime zdt = instant.atZone(ZoneId.of("America/Los_Angeles"));

DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX");
System.out.println(zdt.format(fmt)); // 2018-03-21T03:00:00-07:00
qps
  • 43
  • 4
  • 1
    Good Answer. But fyi, that pattern you defined at the end is already pre-defined: [`DateTimeFormatter.ISO_DATE_TIME`](https://docs.oracle.com/javase/10/docs/api/java/time/format/DateTimeFormatter.html#ISO_DATE_TIME) or [`DateTimeFormatter.ISO_OFFSET_DATE_TIME`](https://docs.oracle.com/javase/10/docs/api/java/time/format/DateTimeFormatter.html#ISO_OFFSET_DATE_TIME) – Basil Bourque Mar 29 '18 at 19:52
  • Both answers (by basil and your) are correct and helped me a lot and it is really hard to choose. I am choosing another answer as correct one as it gave me more insight. upvoting your answer. – silwar Apr 04 '18 at 11:19
1

tl;dr

Never use Date, Calendar, or SimpleDateFormat classes.

Instead, use java.time classes.

ChronoUnit.MILLISECONDS.between( 
    Instant.parse( "2018-01-23T12:34:56Z" )  , 
    Instant.now() 
)

Details

The Answer by QPS is correct.

Is there any other way to get difference between two dates (current and once received from server) in milliseconds?

Yes, there is another way, using the modern java.time classes.

ISO 8601

Your input is in standard ISO 8601 format. The java.time classes use these standard formats by default when parsing/generating strings. So no need to specify a formatting pattern.

The Z on the end is short for Zulu and means UTC. Do not ignore this as you were doing in your example code.

Instant

The Instant class represents a moment in the timeline in UTC with a resolution of nanoseconds.

String input = "2018-01-23T12:34:56Z" ;
Instant instant = Instant.parse( input ) ;

Your attempt to adjust from UTC to zoned time in America/Los_Angeles is unnecessary and irrelevant. Applying a time zone to a UTC value does not change its position on the timeline, it only changes the wall-clock time. So elapsed time between a pair of UTC is identical to the elapsed time after applying a time zone.

Elapsed time

To calculate elapsed time, use Duration.

Duration d = Duration.between( instant , otherInstant ) ;

I recommend using Duration rather than a mere number count of milliseconds to track elapsed time. But if you insist on a total number of milliseconds, use ChronoUnit enum.

long millisElapsed = ChronoUnit.MILLISECONDS.between( instant , otherInstant ) ;

Never use the confusing poorly-designed legacy classes such as Date, Calendar, and SimpleDateFormat. Use only their replacement, the java.time classes, as they are much cleaner, simpler, and are well-designed.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154