Android: How to convert from SystemClock.elapsedRealTime to a human readable CharSeq?

user3472351 picture user3472351 · Jul 22, 2015 · Viewed 10.5k times · Source

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

Answer

Basil Bourque picture Basil Bourque · Jul 23, 2015

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() ;