1

How can I parse a time duration with leap second to seconds?

Example, we are receiving from another service the following duration 00:00:60 which means 1 minute but Java 8 DateTimeFormatter.ofPattern("HH:mm:ss[.SSS]") throws

java.time.format.DateTimeParseException: Text '00:00:60' could not be parsed: Invalid value for SecondOfMinute (valid values 0 - 59): 60

Which makes sense because there is no notion of 60 seconds in the ISO standard used by Java.

I would like to avoid using String::replace (as suggested in other SO answers to similar case) and the use of external library if possible.

This is the current code that is throwing the exception:

private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss[.SSS]");

public static Integer parseToSeconds(String durationText) {

    return LocalTime.parse(durationText, TIME_FORMATTER).toSecondOfDay();
}
dazito
  • 7,740
  • 15
  • 75
  • 117
  • 2
    A duration cannot have a leap second. Only a time of day can, and only at 23:59:60. – Ole V.V. Sep 25 '18 at 09:16
  • 1
    It seems that the service you're receiving your duration from, is sending a wrong result. It should send 00:01:00 instead. If you're supposed to manage this, you got to write your own parser – Robert Kock Sep 25 '18 at 09:19
  • Possible duplicate of [Android : how can I add 5 Times in a variable and get total seconds of them](https://stackoverflow.com/questions/52311282/android-how-can-i-add-5-times-in-a-variable-and-get-total-seconds-of-them), At least the answers there will work for your input string too. On the other hand you are not getting a validation: they will accept `92:93:94` too. – Ole V.V. Sep 25 '18 at 09:21
  • @RobertKock yes, the service is wrong but it is an external service to which I've no control over. I was hoping there was some deep method in java that could help with these cases – dazito Sep 25 '18 at 09:40
  • 1
    @OleV.V. yes I know that, that is what I meant in my question 4th paragraph. The external service is wrong but I cannot change it. It is an external company and they are not changing it, unfortunately. – dazito Sep 25 '18 at 09:44

2 Answers2

1

This is how I solved the issue:

private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss[.SSS]");

public static long parsesToSeconds(String durationText) {
    Duration timeSum = Duration.ZERO;
    // Reformat to ISO 8601
    durationText = durationText.replaceFirst("(\\d{2}):(\\d{2}):(\\d{2}(?:\\.\\d+)?)", "PT$1H$2M$3S");

    timeSum = timeSum.plus(Duration.parse(durationText));

    return timeSum.getSeconds();
}

Credits to Ole V.V. with his answer here: https://stackoverflow.com/a/52315938/2240409

dazito
  • 7,740
  • 15
  • 75
  • 117
  • Glad I could help if only indirectly. You don’t need the `ZERO` and the `plus` operation. Just assign `Duration.parse(durationText)` to a `Duration` variable (with a name that doesn’t include the misleading “sum”) and then returns its `getSeconds()`. – Ole V.V. Sep 25 '18 at 13:25
1

This is what we have lenient resolver style for.

    String durationText = "00:00:60";
    DateTimeFormatter lenientTimeFormatter = DateTimeFormatter.ISO_LOCAL_TIME
            .withResolverStyle(ResolverStyle.LENIENT);
    int seconds = LocalTime.parse(durationText, lenientTimeFormatter).toSecondOfDay();
    System.out.println(seconds);

The output from this snippet is:

60

If your 60 seconds are really a duration, not a time of day, I don’t really like it. Then I’d rather parse into a Duration as you do in your own answer. I know that String.replace with a hard-to-read regular expression isn’t a very good solution either, but I still think I’d prefer it over pretending that a duration is a time of day if this is downright wrong. I just thought I’d present the option anyway and let you make your own pick based on your reality and preferences.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • Yes, in my case duration really is a duration and not a time of day. Thanks for your input. – dazito Sep 25 '18 at 15:22