0

I am receiving a time from my server in UTC ISO8601 format like so:

2018-01-25T05:14:03.4973436

I am converting this string to milliseconds and I am generating a time elapsed value using my current time like so:

public static CharSequence getRelativeTimeSpan(String timeStamp) {

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

    Date date = format.parse(timeStamp);

    long past = date.getTime();

    int offset = TimeZone.getDefault().getRawOffset() + TimeZone.getDefault().getDSTSavings();
    long now = System.currentTimeMillis() + offset;

    return DateUtils.getRelativeTimeSpanString(
            past,
            now,
            DateUtils.MINUTE_IN_MILLIS);
}

However, for some reason, the code produces an immediate difference of 12 hours between past and now. I'm not sure why there is such a difference but for instance this UTC string ( 2018-01-25T05:14:03.4973436 ) generates the following milliseconds value ( 1516857243000 ) which doesn't really correspond to the current time in milliseconds.

The interesting part of all of this is that there's this automatic 12 hour difference. I made sure that I use "HH" instead of "hh" to account for AM/PM differences so I don't know what's causing this discrepancy.

UPDATED USING JAVA8 time - still the same behavior

I have updated the code with the following snippet and I am getting the exact same behavior ( 12 hour immediate difference )

public static CharSequence getRelativeTimeSpan(String timeStamp) {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSS");
    LocalDateTime fromCustomPattern = LocalDateTime.parse(timeStamp, formatter);
    return DateUtils.getRelativeTimeSpanString(
            fromCustomPattern.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(),
            Instant.now().toEpochMilli(),
            DateUtils.MINUTE_IN_MILLIS);
}
AndFantasy
  • 73
  • 1
  • 10
  • 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. Mar 07 '18 at 08:06
  • java.time classes parse ISO 8601 without any explicit formatter, so `OffsetDateTime past = LocalDateTime.parse(timeStamp).atOffset(ZoneOffset.UTC);` and then `long minutes = ChronoUnit.MINUTES.between(past, OffsetDateTime.now(ZoneOffset.UTC));` (just produced 59219). – Ole V.V. Mar 07 '18 at 08:14
  • @OleV.V., this only works for API 26+ . I did try ThreeTenABP but I am still getting the same result. Please, see my updated description. – AndFantasy Mar 07 '18 at 16:52
  • 12 hours sounds like pretty much. Which time zone are you in? It comes from your JVM’s time zone setting. – Ole V.V. Mar 08 '18 at 08:19
  • I take `DateUtils` to be [`android.text.format.DateUtils`](https://developer.android.com/reference/android/text/format/DateUtils.html), is that correct? – Ole V.V. Mar 08 '18 at 09:53

2 Answers2

3

Two issues:

  1. long millisecond times coming from Date and System.currentTimeInMillis() are in UTC. Adding timezone offsets is not necessary when comparing such values. You should not be adjusting for timezone like that.

  2. Date, SimpleDateFormat etc. only support milliseconds resolution but your source strings have 100 nanosecond resolution. There are implementations that would take the 4973436 in 2018-01-25T05:14:03.4973436 as milliseconds, resulting in 436 millis and overflowing the 4973000 to other fields. This happens on some older API levels, newer ones truncate/pad the millis part to exactly 3 digits.

    To fix that, you can truncate your inputs to 3 fractional digits yourself, or use newer java.time APIs (API 26+, backport available for example in ThreeTenABP library).

laalto
  • 150,114
  • 66
  • 286
  • 303
  • I have updated my code to use the java.time libraray using the ThreeTenABP library but I am still getting the exact same results. Please see my updated description. – AndFantasy Mar 07 '18 at 14:42
0

Of course it can be made to work with java.time, the modern Java date and time API also known as JSR-310:

public static CharSequence getRelativeTimeSpan(String timeStamp) {
    LocalDateTime fromIso8601 = LocalDateTime.parse(timeStamp);
    return DateUtils.getRelativeTimeSpanString(
            fromIso8601.atOffset(ZoneOffset.UTC).toInstant().toEpochMilli(),
            Instant.now().toEpochMilli(),
            DateUtils.MINUTE_IN_MILLIS);
}

You don’t need an explicit formatter. The modern date and time classes parse ISO 8601 as their default. Since your date-time string is in UTC, convert it using atOffset(ZoneOffset.UTC) (not your default time zone, this must have been what gave you the wrong result).

To use the above code with ThreeTenABP, make sure you import from org.threeten.bp:

import org.threeten.bp.Instant;
import org.threeten.bp.LocalDateTime;
import org.threeten.bp.ZoneOffset;

Links

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