Search code examples
javaandroidtimedatetime-formatmilliseconds

Android converting the time in milliseconds


I have checked questions about converting time in milliseconds and I followed all answers, but I am still getting the wrong number of hours, although the minutes and seconds are ok.

In my application the user has to select a time from a TimePickerDialog and set the time in the textview. It will show for example 01:30 in a clock. It will show me 502125000222:30:00

My code TimePickerDialog:

 TimePickerDialog timePickerDialog = new TimePickerDialog(this, new TimePickerDialog.OnTimeSetListener() {
    @Override
    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {

        Calendar mycalendar  = Calendar.getInstance();
        mycalendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
        mycalendar.set(Calendar.MINUTE, minute);
        mycalendar.set(Calendar.SECOND, 0);

        long times = mycalendar.getTimeInMillis();

         //  hmsTimeFormatter is a method return time format
        String timerRemaind =  hmsTimeFormatter(times);

             //tv_remaindertime is TextView 

           tv_remaindertime.setText(timerRemaind); 
          ............

And this hmsTimeFormatter method that converts milliseconds:

private String hmsTimeFormatter(long milliSeconds) {

    String hms = String.format("%02d:%02d:%02d",
        TimeUnit.MILLISECONDS.toHours(milliSeconds),
        TimeUnit.MILLISECONDS.toMinutes(milliSeconds) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(milliSeconds)),
        TimeUnit.MILLISECONDS.toSeconds(milliSeconds) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(milliSeconds)));

    return hms;
}

Solution

  • To understand what's happening:

    • Calendar.getInstance() creates a Calendar with the current date/time in the JVM default timezone
    • You set the hour, minutes and seconds (to 01:30:00 I suppose)
    • You call getTimeInMillis(), that returns the number of milliseconds since unix epoch (1970-01-01T00:00Z)

    Then you use this milliseconds value with TimeUnit, but that's not what you need. TimeUnit will treat the number of milliseconds as an amount of time, but what you need is a time of the day:

    • a time of the day is a specific moment in a day, such as "10 AM" or "05:30 PM".
    • an amount of time is just, well, an amount of elapsed time, such as "10 minutes and 45 seconds", or "2 years, 4 months, 5 days and 17 hours", but it's not attached to a specific point in time ("10 minutes relative to what?"); it's just the amount of time, by itself.

    Although both concepts might use the same words ("hours", "minutes", "seconds", etc), they're not the same thing.

    TimeUnit deals with amounts of time, so it does conversions like "1000 seconds is equivalent to how many minutes?".
    Example: TimeUnit.MILLISECONDS.toHours(value) returns the number of hours equivalent to value milliseconds (not the time of the day). As the value you're using is the result of getTimeInMillis(), you're getting the total number of hours since January 1970.

    By using the result of getTimeInMillis() (which represents a specific point in time) with TimeUnit, you're misusing the value, treating like it was an amount of time.

    To format a date, you can use a SimpleDateFormat:

    Calendar mycalendar = Calendar.getInstance();
    mycalendar.set(Calendar.HOUR_OF_DAY, 1);
    mycalendar.set(Calendar.MINUTE, 30);
    mycalendar.set(Calendar.SECOND, 0);
    
    // format: hour:minute:second
    SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
    String hms = sdf.format(mycalendar.getTime()); // 01:30:00
    

    With this, hms will have the value 01:30:00. Check the javadoc to know what the format HH:mm:ss means, so you can change it in case you need a different format (If you want just 01:30, for example, the format will be HH:mm).


    Java new Date/Time API

    The old classes (Date, Calendar and SimpleDateFormat) have lots of problems and design issues, and they're being replaced by the new APIs.

    In Android you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. To make it work, you'll also need the ThreeTenABP (more on how to use it here).

    First you create a org.threeten.bp.LocalTime (a class that represents a time of the day), then you format it with a org.threeten.bp.format.DateTimeFormatter:

    // create time for 01:30
    LocalTime time = LocalTime.of(1, 30);
    // format: hour:minute:second
    DateTimeFormatter fmt = DateTimeFormatter.ofPattern("HH:mm:ss");
    String hms = fmt.format(time);
    System.out.println(hms); // 01:30:00
    

    Also, check the javadoc for all available formats.