Search code examples
javajacksondeserializationdurationjackson-modules

What is the easiest way to deserialize human readable time duration in Java?


I need to have configurable timeout for a project, which reads different configs from YAML file.

I noticed that java.time.Duration has method parse which is used by Jackson to deserialize duration strings. The problem is that it uses ISO-8601 formatting and expects the duration to have PnDTnHnMn.nS format. While it is a good idea to follow standards, asking people provide timeout as PT10M is not the best option and 10m is preferred.

I did write a custom deserializer for duration fields, but seems strange that Jackson can't handle this by default.

What is the easiest way to deserialize human friendly 10m, 5s and 1h to java.time.Duration using Jackson ObjectMapper?


Solution

  • "Easiest" way to deserialize human friendly 10m, 5s and 1h is likely a combination of regex and Java code.

    public static Duration parseHuman(String text) {
        Matcher m = Pattern.compile("\\s*(?:(\\d+)\\s*(?:hours?|hrs?|h))?" +
                                    "\\s*(?:(\\d+)\\s*(?:minutes?|mins?|m))?" +
                                    "\\s*(?:(\\d+)\\s*(?:seconds?|secs?|s))?" +
                                    "\\s*", Pattern.CASE_INSENSITIVE)
                           .matcher(text);
        if (! m.matches())
            throw new IllegalArgumentException("Not valid duration: " + text);
        int hours = (m.start(1) == -1 ? 0 : Integer.parseInt(m.group(1)));
        int mins  = (m.start(2) == -1 ? 0 : Integer.parseInt(m.group(2)));
        int secs  = (m.start(3) == -1 ? 0 : Integer.parseInt(m.group(3)));
        return Duration.ofSeconds((hours * 60L + mins) * 60L + secs);
    }
    

    Test

    System.out.println(parseHuman("1h"));
    System.out.println(parseHuman("1 hour 200 minutes"));
    System.out.println(parseHuman("3600 secs"));
    System.out.println(parseHuman("2h3m4s"));
    

    Output

    PT1H
    PT4H20M
    PT1H
    PT2H3M4S