Jake Stanger
Jake Stanger

Reputation: 449

Convert length of time to appropriate unit

I have a time, in seconds, which has the possibility to be very large. I wish to convert said time into the "appropriate" rounded, readable format.

I already have code which achieves this, however it's not very efficient (and contains a whole bunch of magic numbers):

    String readable = decayTime + " minutes";
    if(decayTime > 60)
    {
        decayTime /= 60;
        readable = decayTime + " hours";

        if(decayTime > 24)
        {
            decayTime /= 24;
            readable = decayTime + " days";

            if(decayTime > 365)
            {
                decayTime /= 365;
                readable = decayTime + " years";

                if(decayTime > 1000000)
                {
                    decayTime /= 1000000;
                    readable = decayTime + "mn years";

                    if(decayTime > 1000)
                    {
                        decayTime /= 1000;
                        readable = decayTime + "bn years"; 
                    }
                }
            }
        }

Apart from switching out the magic numbers, I can't personally think how to make it better.

What I am asking is essentially what would be a better approach to this, or is there something in-built which could help?

Edit: moved to here https://codereview.stackexchange.com/questions/139970/convert-length-of-time-to-appropriate-unit

Upvotes: 0

Views: 549

Answers (3)

Jake Stanger
Jake Stanger

Reputation: 449

The other answers are useful for shorter lengths of time, but without some ugly hacks and likely a block of code less useful for the longer periods I require. @David Wallace over on Code Review gave an answer which used a TreeMap:

import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;

public class TimeFormatter {

    private NavigableMap<Long,String> timeUnits = new TreeMap<>();

    public TimeFormatter() {
        timeUnits.put(Long.MIN_VALUE, " is not a valid argument");
        timeUnits.put(TimeUnit.SECONDS.toSeconds(1), " seconds");
        timeUnits.put(TimeUnit.MINUTES.toSeconds(1), " minutes");
        timeUnits.put(TimeUnit.HOURS.toSeconds(1), " hours");
        timeUnits.put(TimeUnit.DAYS.toSeconds(1), " days");
        timeUnits.put(TimeUnit.DAYS.toSeconds(365), " years");
        timeUnits.put(TimeUnit.DAYS.toSeconds(365 * 1000000L), " million years");
        timeUnits.put(TimeUnit.DAYS.toSeconds(365 * 1000000L * 1000), " billion years");
    }

    public String format(long milliseconds) {
        Map.Entry<Long,String> unitBelow = timeUnits.floorEntry(milliseconds);

        int time = (int) (milliseconds / unitBelow.getKey());
        String formatted = time + unitBelow.getValue();

        if(time == 1 && unitBelow.getKey() < 1000000L) formatted = formatted.substring(0, formatted.length()-1); //Remove plural

        return formatted;
    }
}

I have changed David's answer to use TimeUnit to cut down on the number of magic numbers, as well as dealing with plurals.

EDIT: Some magic numbers do obviously still exist, and actually the code required to deal with larger units are not as bad as I expected.

Upvotes: 0

Basil Bourque
Basil Bourque

Reputation: 340290

ISO 8601

FYI, the ISO 8601 standard defines sensible non-ambiguous formats for date-time values. This includes a compact way to represent a span of time: PnYnMnDTnHnMnS

The P marks the beginning, the T separates the years-months-days from hours-minutes-seconds.

Examples:

  • PT1H30M = An hour and a half.
  • P3Y6M4DT12H30M5S = three years, six months, four days, twelve hours, thirty minutes, and five seconds

java.time

The java.time classes in Java 8 and later use ISO 8601 formats by default when parsing or generating strings to represent date-time values.

The java.time classes include a pair of classes for spans of time. Both can parse/generate this ISO 8601 format.

  • Period for years, months, and days.
  • Duration for days, hours, minutes, seconds, and fraction of second.

Your scale of minutes to billions of years is too extreme for this obviously, but this does suit the more mundane business world outside of science.

Upvotes: 0

Rodolfo
Rodolfo

Reputation: 1181

You can use Duration but it doesn't convert to years.

Duration.ofSeconds(decayTime).toNanos();
Duration.ofSeconds(decayTime).toMillis();
Duration.ofSeconds(decayTime).toMinutes();
Duration.ofSeconds(decayTime).toHours();
Duration.ofSeconds(decayTime).toDays();

See https://docs.oracle.com/javase/tutorial/datetime/iso/period.html and https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html

Another option is:

TimeUnit.SECONDS.toNanos(decayTime);
TimeUnit.SECONDS.toMicros(decayTime);
TimeUnit.SECONDS.toMillis(decayTime);
TimeUnit.SECONDS.toMinutes(decayTime);
TimeUnit.SECONDS.toHours(decayTime);
TimeUnit.SECONDS.toDays(decayTime);

For the year you could use some hack like:

Long.parseLong(new SimpleDateFormat("YYYY").format(new Date(Duration.ofSeconds(decayTime).toMillis())));

But I strongly don't recomend it.

Upvotes: 1

Related Questions