views:

120

answers:

6

When setting issue estimates in JIRA, you can enter a string like "1d 2h 30m" and JIRA will translate this (I'm assuming) into a corresponding number of milliseconds.

Is there an available Java library that does this?

I'm using a Spring managed bean that takes a property indicating how often a directory ought to be purged, and I'd like to allow the configuration to take a human-readable string rather than an explicit number of milliseconds.

Alternatively, if there's a better approach I'm not thinking of, I'd love to hear it.

A: 

As far as I know, no. I believe you would have to convert those numbers yourself into Calendar (GregorianCalendar) and then from there you can proceed to get the milliseconds, which you probably already know which is why you posted this for the hope of a better answer.

My vote: If no one else can find one perhaps now is a good time to start one yourself and contribute it to the community. :)

Chris Aldrich
Agreed. My thoughts are heading in this direction.
unsquared
+1  A: 

Take a look at joda-time's PeriodConverter. I have never used this part of the joda-time library myself, but it looks like it does what you need.

In general, for date/time stuff, look at joda-time first :)

Michael D
A: 
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateTest {
    public static void main(String[] args) {
        DateFormat df = new SimpleDateFormat("dd'd' HH'h' mm'm'");
        try {
            Date d = df.parse("1d 2h 30m");
            System.out.println(d.getTime());
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}
aboettger
yes, this works if I strictly adhere to the format, but it fails for "1d 2h", "1d, 2h, 30m" and many other special cases. `SimpleDateFormat` is just not flexible enough for this kind of task IMHO.
seanizer
+3  A: 

The parser is not too complex:

public static long parse(String input) {
   long result = 0;
   String number = "";
   for (int i = 0; i < input.length(); i++) {
     char c = input.charAt(i);
     if (Character.isDigit(c)) { 
       number += c; 
     } else if (Character.isLetter(c) && !number.isEmpty()) {
       result += convert(Integer.parseInt(number), c);
       number = "";
     }
   }
   return result;
}

private static long convert(int value, char unit) {
  switch(unit) {
    case 'd' : return value * 1000*60*60*24;
    case 'h' : return value * 1000*60*60;         
    case 'm' : return value * 1000*60;
    case 's' : return value * 1000;
  }
  return 0;
}

The code is pretty fault tolerant, it just ignores almost anything it can't decode (and it ignores any whitspace, so it accepts "1d 1s", "1s 1d", "1d20m300s" and so on).

Andreas_D
Tried to fix that code for you :)
Peter Lang
@Peter - thanks, I've coded and tested the part on another machine, stupid copying and naming errors ;)
Andreas_D
@Andreas_D - Elegant. Thank you.
unsquared
I like this parser. You should add a little bit of configuration to allow internationalized indicators though.
Philipp Jardas
+2  A: 

Here's another solution, this one's configurable:

public class TimeReader{

    private final Map<String, Long> units = new HashMap<String, Long>();

    private static final String UNIT_PATTERN = "\\w+";
    private static final Pattern ITEM_PATTERN = Pattern.compile("(\\d+)\\s*("
        + UNIT_PATTERN + ")");

    /**
     * Add a new time unit.
     * 
     * @param unit
     *            the unit, e.g. "s"
     * @param value
     *            the unit's modifier value (multiplier from milliseconds, e.g.
     *            1000)
     * @return self reference for chaining
     */
    public TimeReader addUnit(final String unit, final long value){
        if(value < 0 || !unit.matches(UNIT_PATTERN)){
            throw new IllegalArgumentException();
        }
        units.put(unit, Long.valueOf(value));
        return this;
    }

    /**
     * Parse a string using the defined units.
     * 
     * @return the resulting number of milliseconds
     */
    public long parse(final String input){
        long value = 0l;
        final Matcher matcher = ITEM_PATTERN.matcher(input);
        while(matcher.find()){
            final long modifier = Long.parseLong(matcher.group(1));
            final String unit = matcher.group(2);
            if(!units.containsKey(unit)){
                throw new IllegalArgumentException("Unrecognized token: "
                    + unit);
            }
            value += units.get(unit).longValue() * modifier;
        }
        return value;
    }

}

Sample Usage:

public static void main(final String[] args){
    final TimeReader timeReader =
        new TimeReader()
            .addUnit("h", 3600000l)
            .addUnit("m", 60000l)
            .addUnit("s", 1000l);

    System.out.println(timeReader.parse("3h, 2m   25  s"));
}

Output:

10945000

seanizer
A: 

Maybe cron expressions would be more helpful for your current task at hand: specyfing how often something should happen. This format is quite flexible, well known and understood. To drive your bean that is doing the periodic chore you could, use the Quartz scheduler, EJB3 timer annotations or the Spring timer annotations.

Philipp Jardas