2

I have this code , but it gives 2 more hours

  val leftTimeInMillis = 12728918
  val mTimeFormat = SimpleDateFormat("HH:mm:ss", Locale.ROOT)
  binding.textTimeLeft.text = mTimeFormat.format(leftTimeInMillis)

Normally the text must be indicate 03:32:08 but it is 05:32:08

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Ahmet B.
  • 1,290
  • 10
  • 20
  • 2
    Most likely you live somewhere in the UTC+2 time zone and these 2 hours have been automatically added. Better use Java Duration class and their appropriate formatter instead of using SimpleDateFormat. – Robert Jul 02 '21 at 22:29
  • ok, after I added mTimeFormat.timeZone = TimeZone.getTimeZone("GMT") it works fine, thanks @Robert – Ahmet B. Jul 02 '21 at 22:37
  • I recommend you never use `SimpleDateFormat`. That class is notoriously troublesome and long outdated. Even if you wanted to take the trouble with `SimpleDateFormat`, it’s for formatting a date and time of day, not an amount of time. For your own sake and for the sake of those maintaining your code after you: do not do that. Use `Duration` from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). – Ole V.V. Jul 03 '21 at 04:01
  • 1
    Similar and helpful: [Convert number of seconds into HH:MM (without seconds) in Java](https://stackoverflow.com/questions/50096083/convert-number-of-seconds-into-hhmm-without-seconds-in-java) – Ole V.V. Jul 03 '21 at 04:13
  • 1
    Does this answer your question? [How do I convert milliseconds to days, hour, minutes](https://stackoverflow.com/questions/49881667/how-do-i-convert-milliseconds-to-days-hour-minutes) – Ole V.V. Jul 03 '21 at 04:17

2 Answers2

5

java.time.Duration through desugaring and String.format()

Use java.time, the modern Java date and time API, for your time work.

I haven’t got an Android development environment, but I expect the following to work with desugaring (see the link below). It works with desktop Java version 9 and later.

    int leftTimeInMillis = 12_728_918;
    
    Duration timeLeft = Duration.ofMillis(leftTimeInMillis);
    String formattedTimeLeft = String.format("%02d:%02d:%02d", 
            timeLeft.toHours(), timeLeft.toMinutesPart(), timeLeft.toSecondsPart());
    
    System.out.println(formattedTimeLeft);

Output is the required:

03:32:08

If for some reason instead of desugaring your are using ThreeTenABP, I believe that the toMinutesPart and toSecondsPart methods are not included. Then the conversion takes a little bit more:

    Duration timeLeft = Duration.ofMillis(leftTimeInMillis);
    long hours = timeLeft.toHours();
    timeLeft = timeLeft.minusHours(hours);
    long minutes = timeLeft.toMinutes();
    timeLeft = timeLeft.minusMinutes(minutes);
    long seconds = timeLeft.getSeconds();
    String formattedTimeLeft = String.format("%02d:%02d:%02d", 
            hours, minutes, seconds);

Output is the same as before.

Alternative: Time4A

If you’re happy with an extensive external library for date and time, an option is Time4A, the Android version of Time4J. It offers a solution that is a bit more elegant and more in line with what you tried yourself. I prefer to declare a static formatter:

private static final Duration.Formatter<ClockUnit> M_TIME_FORMAT 
        = Duration.formatter(ClockUnit.class, "hh:mm:ss");

Now formatting goes like:

    int leftTimeInMillis = 12_728_918;
    
    Duration<ClockUnit> timeLeft = Duration.of(leftTimeInMillis, ClockUnit.MILLIS)
            .with(Duration.STD_CLOCK_PERIOD);
    String formattedTimeLeft = M_TIME_FORMAT.format(timeLeft);
    
    System.out.println(formattedTimeLeft);

03:32:08

Again I have developed my code on desktop Java using Time4J.

What went wrong in your code?

What happened in your code? Where did those 2 hours come from? SimpleDateFormat was designed in the 1990’s and was always for formatting and parsing a point in time, never an amount of time. So it takes your number, 12 728 918, to mean a point in time this many milliseconds after the Java epoch of Jan 1 1970 at start of day in UTC. In other words you are trying to format an instant of 1970-01-01T03:32:08.918Z. Since the time zone setting of your device (a guess would be Europe/Athens) was 2 hours ahead of UTC in the winter of 1970, the date and time used for formatting is 1970-01-01T05:32:08.918+02:00[Europe/Athens]. Which is why you got 05:32:08.

Assuming that you did not have a time in New Year’s night in 1970 in mind, what you are doing is wrong, and even if you could make it work, it will confuse everyone reading your code. Also if one day the anount of milliseconds exceeds 24 hours, the whole days will not be printed, probably causing great confusion. Don’t use SimpleDateFormat for this purpose. And consider never using SimpleDateFormat at all since that class is notoriously troublesome and long outdated.

Question: Doesn’t java.time require Android API level 26?

java.time works nicely on both 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 non-Android 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 either use desugaring or the Android edition of ThreeTen Backport. It’s called ThreeTenABP. In the latter case make sure you import the date and time classes from org.threeten.bp with subpackages.

Links

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

I know its a late answer

But try setting the GMT's offset for the SimpleDateFormat to "GMT+0", for me I'm using this method to get the time left for a timeStamp, and it's working as expected:

private fun getTimeString(
    timeStamp: Long,
    isShowHoursEventItsZero: Boolean = false,
    isShowMillis: Boolean = false,
    locale: Locale = Locale.ENGLISH,
): String {
    var mainFormat = "mm:ss"
    if (TimeUnit.MILLISECONDS.toHours(timeStamp) > 0 || isShowHoursEventItsZero) {
        mainFormat = mainFormat.prependIndent("HH:")
    }
    if (isShowMillis) {
        mainFormat = mainFormat.plus(".SS")
    }
    val dateFormat = SimpleDateFormat(mainFormat, locale)
    dateFormat.timeZone = TimeZone.getTimeZone("GMT+0")
    return dateFormat.format(Date(timeStamp))
}
Farouq Afghani
  • 296
  • 1
  • 6
  • Thanks Farouq for your answer. I solved this problem many times. I noticed your answer and will it try it next time. – Ahmet B. Oct 25 '22 at 11:34
  • Discouraged in more than one way. No one should use the notoriously troublesome and long outdated `SimpleDateFormat` class any more. And it was never for an amount of time like a time left (always only for a point in time seen in a time zone). Time left is not a timesatmp, so calling it `timeStamp` in your code is misleading. Your code is also wrong. I tried `getTimeString(1, false, true)` and expected 00:00.001 but got `00:00.01` (a factor 10 wrong). – Ole V.V. May 22 '23 at 15:59