Home > Enterprise >  java.time.format.DateTimeParseException, and I do not know how to solve it
java.time.format.DateTimeParseException, and I do not know how to solve it

Time:10-30

I am trying to get data about some athletes from a csv file, then create athlete objects that are going to be stored in a list. The problem is that I get an error when I try to parse the time they've got as LocalDateTime.This is the error I get:

Exception in thread "main" java.time.format.DateTimeParseException: Text '30:27' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {MinuteOfHour=30, MicroOfSecond=0, MilliOfSecond=0, SecondOfMinute=27, NanoOfSecond=0},ISO of type java.time.format.Parsed

This is the code:

public static void addAthletes() {
    try (BufferedReader br = new BufferedReader(
            new FileReader("C:\\Users\\****\\******\\**********\\src\\main\\java\\ro\\sci\\tema10_ski_biathlon_standings\\standings.csv"))) {
        String line = null;
        while ((line = br.readLine()) != null) {
            athleteList.add(getAthleteFromCsvLine(line));
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

private static Athlete getAthleteFromCsvLine(String line) {

    String[] athleteAttributes = line.split(",");

    if (athleteAttributes.length != 7) {
        throw new IllegalArgumentException();
    }

    int athleteNumber = Integer.parseInt(athleteAttributes[0].trim());
    LocalDateTime skiTimeResults = LocalDateTime.parse(athleteAttributes[3].trim(),
            DateTimeFormatter.ofPattern("mm:ss"));

     return new Athlete(
             athleteNumber,
             athleteAttributes[1],
             athleteAttributes[2],
             skiTimeResults,
             athleteAttributes[4],
             athleteAttributes[5],
             athleteAttributes[6]
     );

}

Please help me overcome this

CodePudding user response:

What you wrote makes no sense

Just read it and think: A minute seconds value has absolutely squat to do with the concept of a 'local date time'. It's like writing:

Apple a = new Caravan();

just as crazy.

Those minutes Seconds represent a Duration.

Unfortunately, DateTimeFormatter is fundamentally designed to parse wallclock dates and times.

There's a more central problem here...

The problem with wallclock time and durations

Wallclock time and duration are utterly unrelated but humanity doesn't act like it:

TV and printed results of races tend to state that someone e.g. ran a mile in "04:23", which looks exactly like the wallclock time of "It is 23 seconds and 4 minutes past midnight". However, there is absolutely no relationship whatsoever between these two concepts. This means it's very confusing when computers need to deal with this stuff: Is "04:23":

  • Someone ran a race in 4 hours and 23 minutes and unspecified seconds.
  • Someone ran a race in 4 minutes and 23 seconds.
  • It is 23 minutes past 4 o'clock in the early morning.
  • It is 23 seconds and 4 minutes past midnight.

All of them are reasonable; it depends on context.

Generally I say when humans have weird, inconsistent behaviour, software should just make do, but in the ISO (standards) world, this confusion is so hated that they flat out refuse to treat something like "04:23" as a fair way to put in text the notion of 'a duration of 4 minutes and 23 seconds'.

Instead, you get the ISO8601 Durations format which would write that as "PT30m27s" (P for: This is a period of time, then the T to indicate we aren't going to name any weeks or days, we're moving straight to the sub-day stuff, and then 30 minutes and 27 seconds'. You can parse that out of the box Duration.parse("PT30m27s") works, but you don't have that kind of string input.

What you have is something that in terms of the date/time APIs is a wallclock time and nothing but wallclock time.

I explain all this so that you understand why this method simply does not exist: There is no:

Duration d = Duration.parse("30:27", DateTimeFormatter.ofPattern("mm:ss"));

Nor any other way to directly turn a string datetimeformat pattern into an instance of Duration.

But wait! There is a trick...

We treat the string as wallclock time and then fetch the duration between midnight and that time.

DateTimeFormatter formatter = DateTimeFormatter.of("[HH:]mm:ss");
LocalTime lt = LocalTime.parse("30:27", formatter);
Duration d = Duration.between(LocalTime.MIN, lt);

NB: EDITED: Added the [HH:] which is mandatory here.

That accomplishes your goals: Readable, flexibility in specifying the format string, using just the tools in java.time.

CodePudding user response:

Well, LocalDateTime expects a date component to be present, and a valid time component. Your 30:27 text contains neither: obviously, 30:27 as a wall clock time does not exist.

It seems you are looking for a duration here. Use Duration. Note that Duration does not have a method to parse the text 30:27 successfully, so we have to convert it to a ISO period/duration string:

String[] components = athleteAttributes[3].trim().split(":");
String durationStr = String.format("PT%sM%sS", components[0], components[1]);
Duration duration = Duration.parse(durationStr);

Alternatively, you could use

String[] components = athleteAttributes[3].trim().split(":");
int minutes = Integer.parseInt(components[0]);
int seconds = Integer.parseInt(components[1]);
Duration duration = Duration.ofMinutes(minutes).plusSeconds(seconds);
  • Related