gvlfm78 gvlfm78 - 1 month ago 15
Java Question

Parsing time in minutes from String like "6m 5d3h "

This is actually for a bukkit plugin, but the question I have to ask is about the Java programming in it.

I wish to turn a String looking like this:

String string = "5d3h6m";


Or even like this:

String string = " 44h 22d";


In any order, and with any number of spaces into an int symbolising the time in minutes.

(D=days H=Hours M=Minutes)

The problems I have had:


  1. Finding the characters in any order

  2. Getting any number of digits that appear before the letter

  3. Making the code efficient and non repetitive?



The following code is me trying to find a solution to problems 1 & 2:

if(timestring.contains(":")){
String[] timestringarray = timestring.split(":");
int timeinminutes = 0;
int timestringarraylength = timestringarray.length;
while(timestringarraylength>0){
if(timestringarray[timestringarraylength].matches("d{1}|D{1}")){
timestring.replace("d{1}|D{1}","");
long days = TimeUnit.MINUTES.convert(Integer.valueOf(timestring), TimeUnit.DAYS);
int daysint = (int) (long) days;
timeinminutes+=daysint;
}
if(timestringarray[timestringarraylength].matches("h{1}|H{1}")){
timestring.replace("h{1}|H{1}","");
long hours = TimeUnit.MINUTES.convert(Integer.valueOf(timestring), TimeUnit.HOURS);
int hoursint = (int) (long) hours;
timeinminutes+=hoursint;
}
if(timestringarray[timestringarraylength].matches("m{1}|M{1}")){
timestring.replace("m{1}|M{1}","");
long mins = TimeUnit.MINUTES.convert(Integer.valueOf(timestring), TimeUnit.MINUTES);
int minsint = (int) (long) mins;
timeinminutes+=minsint;
}
timestringarraylength--;
}
signConfig.set("Sign.time", Integer.valueOf(timeinminutes));
}


Does anybody know how to go about these problems?

To specify, the String is a time entered by the user and won't change during the processing.

Thanks in advance.

Answer

This should get you started

Given that it doesn't work isn't very helpful, this is about all you can hope for. Some direction on how to convert arbitrary encoded duration intervals into something you can process further.

This requires JDK 1.7+ because of the switch on String. Otherwise you will have to convert that to an if/elseif/else block.

Regular Expression (\d+)([hmsd]) explained.

import javax.annotation.Nonnull;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Q24917480
{
    public static void main(final String[] args)
    {
        final Pattern p = Pattern.compile("(\\d+)([hmsd])");
        final Matcher m = p.matcher("5d3h6m");
        long totalMillis = 0;
        while (m.find())
        {
            final int duration = Integer.parseInt(m.group(1));
            final TimeUnit interval = toTimeUnit(m.group(2));
            final long l = interval.toMillis(duration);
            totalMillis = totalMillis + l;
            System.out.format("TimeUnit.%s.toMillis(%s) = %d\n", interval.name(), duration, l);
        }
        System.out.println("TimeUnit.MILLISECONDS.toSeconds(totalMillis) = " + TimeUnit.MILLISECONDS.toSeconds(totalMillis));
        System.out.println("TimeUnit.MILLISECONDS.toMinutes(totalMillis) = " + TimeUnit.MILLISECONDS.toMinutes(totalMillis));
        System.out.println("TimeUnit.MILLISECONDS.toDays(totalMillis) = " + TimeUnit.MILLISECONDS.toDays(totalMillis));
    }

    public static TimeUnit toTimeUnit(@Nonnull final String c)
    {
        switch (c)
        {
            case "s": return TimeUnit.SECONDS;
            case "m": return TimeUnit.MINUTES;
            case "h": return TimeUnit.HOURS;
            case "d": return TimeUnit.DAYS;
            default: throw new IllegalArgumentException(String.format("%s is not a valid code [smhd]", c));
        }
    }
}

The expected output is:

TimeUnit.DAYS.toMillis(5) = 432000000
TimeUnit.HOURS.toMillis(3) = 10800000
TimeUnit.MINUTES.toMillis(6) = 360000
TimeUnit.MILLISECONDS.toSeconds(totalMillis) = 443160
TimeUnit.MILLISECONDS.toMinutes(totalMillis) = 7386
TimeUnit.MILLISECONDS.toDays(totalMillis) = 5
Comments