1

I have the weirdest problem - In our app, at one place we are showing a time formatted with mm:ss, using SimpleDateFormat. The problem is we have a client in US that complains that they get 5min formatted as 35:00. I narrowed it down that SimpleDateFormat.format is called with the same value that I'm testing with, but they are getting minutes padded with 3 instead of 0.

The thing is I'm not able to replicate the problem and they are telling me they tested and are getting it on 4 different devices:

  • Oppo X9076 (Android 5.0)
  • Xiaomi Mi A1(Android 8.1.0)
  • Samsung SM-T715Y(Android 7.0)
  • HUAWEI BLA-L29 (Android 8.0)

I'm also, testing on the Xiaomi Mi A1 (and few other), but the time is ok when I try.

This is how the time is formated:

SimpleDateFormat timeFormatter = new SimpleDateFormat("mm:ss", Locale.getDefault());
timeFormatter.format(new Date(5*60*1000));

Does anyone have seen something like this?

Mit_
  • 149
  • 1
  • 11
  • 1
    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. And I would consider it likely to eliminate your error. – Ole V.V. Nov 09 '18 at 08:23
  • 1
    Are your using `SimpleDateFormat` for formatting a duration? That’s — you may call it misuse, incorrect, inappropriate or against intentions depending on taste. Find inspiration [here: How to format a duration in java? (e.g format H:MM:SS)](https://stackoverflow.com/questions/266825/how-to-format-a-duration-in-java-e-g-format-hmmss) instead. – Ole V.V. Nov 09 '18 at 08:30
  • Is 11 minutes rendered as 11 or 41? 0 minutes as 30 or 33? Have you got other clients in the same US time zone, and if you have, have they got the same problem or not? – Ole V.V. Nov 09 '18 at 08:34

1 Answers1

4

It’s a time zone problem. Or more precisely, a problem of misusing SimpleDateFormat for something that it wasn’t meant for. Here’s a way you can reproduce:

    TimeZone.setDefault(TimeZone.getTimeZone("America/St_Johns"));
    SimpleDateFormat timeFormatter = new SimpleDateFormat("mm:ss", Locale.getDefault());
    System.out.println(timeFormatter.format(new Date(5*60*1000)));

Output from this snippet is:

35:00

Explanation: new Date(5*60*1000) produces a date-time of 5 minutes after the epoch, that is 00:05:00 UTC on January 1, 1970. At this time as at any other time, different zones of the world have different times. However, most time zones have an offset from UTC of a whole number of hours. So the time will be 5 minutes past the hour, and your formatter will print it as 05:00. This is why you didn’t notice the problem before. For example, in Berlin it will be 01:05:00, and in New York it will be 19:05:00 the evening before. However, there are also time zones that have offsets from UTC that are not a whole number of hours. For example Asia/Kolkata is at +05:30 and Asia/Kathmandu is at +05:45. In such a time zone, the time will not be 5 minutes past the hour, and you will get an unexpected result like the one you saw.

Suggested solutions include:

  • Use TimeUnit for converting seconds into minutes and seconds and String.format for formatting them into two digits each.
  • If you want a nice solution and you’re OK with an external dependency on an old library in maintenance mode, look into the PeriodFormatter of Joda-Time.

Here’s an example of using TimeUnit for calculating the parts to format as suggested in the first bullet:

    long totalSeconds = TimeUnit.MINUTES.toSeconds(5); // 300

    long minutes = TimeUnit.SECONDS.toMinutes(totalSeconds);
    long secondsPart = totalSeconds - TimeUnit.MINUTES.toSeconds(minutes);
    String formattedDuration = String.format("%02d:%02d", minutes, secondsPart);
    System.out.println("Formatted duration: " + formattedDuration);

Output:

Formatted duration: 05:00

The ugly hack that I wouldn’t suggest would be to set the time zone of your formatter to UTC.

There’s a lot more inspiration in the question I already linked to in a comment. At time of writing it has 20 answers. I am repeating the link at the bottom.

And by the way, even for formatting and parsing dates and times, you may consider not using SimpleDateFormat. It’s notoriously troublesome and long outmoded. Instead add 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.

Links

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