6

How do you convert from SystemClock.elapsedRealTime() to a human readable CharSeq that can be displayed in textview?

Basil Bourque
  • 218,480
  • 72
  • 657
  • 915
user3472351
  • 89
  • 1
  • 2
  • 6

2 Answers2

8

tl;dr

Duration.ofMillis(                 // Represent a span of time on scale of hours-minutes-seconds, unattached to the timeline.
    SystemClock.elapsedRealTime()  // 441_000L as an example.
)
.toString()                        // Generate a String in standard ISO 8601 format.

PT7M21S

Avoid Time-of-day Format

Displaying elapsed time in the format of time-of-day (HH:MM:SS.SSS) is misleading and ambiguous as it can easily be misinterpreted as time-of-day.

ISO 8601 – Duration Format

The ISO 8601 standard defines sensible string representations of various date-time values. These values include elapsed time.

The standard format is PnYnMnDTnHnMnS where the beginning is marked with a P while a T separates the years-month-days portion from the hours-minutes-seconds portion. For example:

  • PT30M = a half hour
  • PT8H30M = eight and a half hours
  • P3Y6M4DT12H30M5S represents a duration of "three years, six months, four days, twelve hours, thirty minutes, and five seconds".

Often the terms "period" or "duration" are used as synonyms. The terminology in date-time work has not yet been standardized. Note how the standard uses the term "duration", yet the format begins with a P as in "period". C’est la vie.

java.time

The java.time classes offer two classes to represent a span of time unattached to the timeline:

  • Period
    years-months-days
  • Duration
    hours-minutes-seconds

Both of these classes parse/generate the standard ISO 8601 duration format discussed above.

Your output from SystemClock.elapsedRealTime() is a long integer number counting milliseconds elapsed. So we want likely want to use the Duration class.

long millis = SystemClock.elapsedRealTime() ;
Duration d = Duration.ofMillis( millis ) ;

Let’s try 441000 as our millis.

Duration d = Duration.ofMillis( 441_000L ) ;

Generate the standard formatted string. For 441_000L, we get seven minutes and 21 seconds.

String output = d.toString() ;  // Generate standard ISO 8601 string.

PT7M21S

You can parse the standard strings, to get a Duration object.

Duration d = Duration.parse( "PT7M21S" ) ;

I suggest teaching your users to read the standard ISO 8601 format, as it is readable and unambiguous. I strongly recommend never displaying as a time-of-day format (00:07:21) as I have seen that ambiguity cause much confusion among users. But if your user-base is not amenable to the standard format, you can generate other strings.

In some later versions of this class, you can call the to…Part methods to retrieve the seven and the twenty-one.

int minutesPart = d.toMinutesPart() ; // The whole-minutes portion.
int secondsPart = d.toSecondsPart() ; // The whole-seconds portion.

You can also get back to a grand total of milliseconds.

long millis = d.toMillis() ;  // Not the part, but the entire span of time in terms of milliseconds.

About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.


Joda-Time

UPDATE: The Joda-Time project is now in maintenance-mode, with the team advising migration to java.time classes. I am leaving this section intact for history.

The Joda-Time library offers three classes to represent a span of time, Interval, Duration, and Period. That last one, Period, means a span of time described as a count of days, hours, and such. It parallels this particular ISO 8601 format. The Period class also parses and generates strings in the ISO 8601 format. Joda-Time works in Android, is well-worn and quite popular as a replacement for the notoriously troublesome java.util.Date/.Calendar classes.

The Period class takes a long integer as a count of milliseconds in duration. Just what we need, as SystemClock.elapsedRealtime() gives us a count in milliseconds since the machine booted.

long machineUptimeMillis = SystemClock.elapsedRealtime() ;
Period machineUptimePeriod = new Period( machineUptimeMillis );
String output = machineUptimePeriod.toString(); // Ex: P3DT2H15M37.123S

If we are tracking how long some operation takes, we’ll be using a pair of such longs in subtraction.

long start = SystemClock.elapsedRealtime() ;
// … Perform some operation …
long stop = SystemClock.elapsedRealtime() ;
long elapsedMillis = stop - start ;
Period elapsedPeriod = new Period( elapsedMillis );

Lowercase

For presentation to human users, you may want to strip out the P and/or T and use lowercase letters for better readability.

String output = elapsedPeriod.toString().replace( "P" , "" ).replace( "T", " " ).toLowerCase() ; 

For example, 3y6m4d 12h30m5s.

Pretty-Print

You can pretty-print Period values such as "5 years and 2 months".

Use the built-in Locale-sensitive word-based formatter.

PeriodFormatter formatter = PeriodFormat.wordBased( Locale.CANADA_FRENCH ) ;
String output = formatter.print( elapsedPeriod ) ;

Or create your own custom format with a PeriodFormatterBuilder.

 PeriodFormatter yearsAndMonths = new PeriodFormatterBuilder()
     .printZeroAlways()
     .appendYears()
     .appendSuffix(" year", " years")
     .appendSeparator(" and ")
     .printZeroRarelyLast()
     .appendMonths()
     .appendSuffix(" month", " months")
     .toFormatter() ;
Basil Bourque
  • 218,480
  • 72
  • 657
  • 915
  • java.time.Period is available in Android API level 26 upwards – channae Apr 08 '18 at 05:28
  • @channae I revised this Answer to (a) give you links to the *ThreeTen-Backport* & *ThreeTenABP* libraries to use in earlier versions of Android, and (b) Stress using *java.time* classes (or the back-port) as the official successor to *Joda-Time* (both projects are led by the same man, [Stephen Colebourne](http://blog.joda.org)). – Basil Bourque Apr 09 '18 at 22:21
2

See the following question: How to format an elapsed time interval in hh:mm:ss.SSS format in Java?. Using this approach:

private static String formatInterval(final long millis) {
    final long hr = TimeUnit.MILLISECONDS.toHours(millis);
    final long min = TimeUnit.MILLISECONDS.toMinutes(millis - TimeUnit.HOURS.toMillis(hr));
    final long sec = TimeUnit.MILLISECONDS.toSeconds(millis - TimeUnit.HOURS.toMillis(hr) - TimeUnit.MINUTES.toMillis(min));
    final long ms = TimeUnit.MILLISECONDS.toMillis(millis - TimeUnit.HOURS.toMillis(hr) - TimeUnit.MINUTES.toMillis(min) - TimeUnit.SECONDS.toMillis(sec));
    return String.format("%02d:%02d:%02d.%03d", hr, min, sec, ms);
}

And then assuming you have a reference to a TextView:

long elapsedTime = SystemClock.elapsedRealTime();
textView.setText(elapsedTime);

The result is that your TextView will display the total time that your device has been up in a human-readable format.

Community
  • 1
  • 1
EJK
  • 11,784
  • 3
  • 34
  • 53
  • Unfortunately this won't work if `elapsedRealTime()` returns a value greater than or equal to 24 hours. The problem is `HH` when used with `SimpleDateFormat` can only represent an absolute hour of day from 0-23. Absolute date & time represented by formatting using `SimpleDateFormat` is not the same as elapsed / relative time. – Squonk Jul 22 '15 at 22:19
  • Still some issues...the `formatInterval(...)` method doesn't need to be `static`. It's only declared that way in the link you posted because it was being called there from the `main()` method of standard Java code. Also in the example of setting the `TextView` you're just passing in `elapsedTime` rather than the result of `formatInterval(...)`. Lastly, the `formatInterval(...)` method is formatting the return `String` to 2 digits for hours causing misleading results after 99 hours, e.g., 105 hours would show as 05. The code should take in to account days as well as hours etc. – Squonk Jul 22 '15 at 23:03