Java 8 Localdatetime Is Parsing Invalid Date

Java 8 LocalDateTime is parsing invalid date

You just need a strict ResolverStyle.

Parsing a text string occurs in two phases. Phase 1 is a basic text parse according to the fields added to the builder. Phase 2 resolves the parsed field-value pairs into date and/or time objects. This style is used to control how phase 2, resolving, happens.

Sample code - where withResolverStyle(ResolverStyle.STRICT) is the important change, along with the use of uuuu rather than yyyy (where uuuu is "year" and "yyyy" is "year of era", and therefore ambiguous):

import java.time.*;
import java.time.format.*;
import java.util.*;

public class Test {

public static void main(String[] args) {
String dateFormat = "HH:mm:ss MM/dd/uuuu";
String dateString = "11:30:59 02/31/2015";
DateTimeFormatter dateTimeFormatter = DateTimeFormatter
.ofPattern(dateFormat, Locale.US)
.withResolverStyle(ResolverStyle.STRICT);
try {
LocalDateTime date = LocalDateTime.parse(dateString, dateTimeFormatter);
System.out.println(date);
} catch (DateTimeParseException e) {
// Throw invalid date message
System.out.println("Exception was thrown");
}
}
}

Java 8 LocalDate won't parse valid date string

An example from the documentation:

LocalDate date = LocalDate.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd");
String text = date.format(formatter);
LocalDate parsedDate = LocalDate.parse(text, formatter);

You should use "yyyyMMdd" instead of "YYYYMMdd". The difference between Y and y was mentioned here.

LocalDate is silently correcting bad dates?

This is due to the ResolverStyle used by the formatter to parse the value. By default (at least on my machine) it's "smart":

For example, resolving year-month and day-of-month in the ISO calendar system using smart mode will ensure that the day-of-month is from 1 to 31, converting any value beyond the last valid day-of-month to be the last valid day-of-month.

... but you can make it "strict" instead, in which case the parsing fails. Complete example (using u instead of y to avoid the ambiguity of not specifying an era):

import java.time.*;
import java.time.format.*;

public class Test {

public static void main (String[] args) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("M/d/uuuu");
// With the "smart" resolver style, it parses
System.out.println(formatter.getResolverStyle());
LocalDate date = LocalDate.parse("9/31/2018", formatter);
System.out.println(date);

// But with a strict style...
formatter = formatter.withResolverStyle(ResolverStyle.STRICT);
LocalDate.parse("9/31/2018", formatter);
}
}

Unable to parse date using LocalDateTime?

Let’s read the message:

java.time.format.DateTimeParseException: Text '10.10.2020 12:00:00' could not be parsed at index 2

Indices are 0-based, so index 2 in 10.10.2020 12:00:00 is where the first dot (period, point) is. So Java is unable to parse that dot. To find out why, let’s look at the corresponding place in the format pattern string

String dateTimeFormat = "MM/dd/yyyy HH:mm:ss a";

So the month, 10, has been successfully parsed and next the formatter expects — a slash. The discrepancy between slash and dot explains the exception.

A further tip: When it gets non-trivial to get the format pattern string for parsing right, try formatting a known date first:

    System.out.println("To be parsed: " + date);
LocalDateTime knownDateTime = LocalDateTime.of(2020, Month.OCTOBER, 10, 12, 0);
System.out.println("Formatted: " + knownDateTime.format(format));

Output in this case:

To be parsed: 10.10.2020 12:00:00
Formatted: 10/10/2020 12:00:00 PM

This way of printing it makes it easier to spot the differences between what we’ve got and what the formatter will expect for parsing.

DateTimeFormatter invalid day gets adapted after LocalDateTime.parse

Your confusion is justified. I find that in fact a DateTimeFormatter for pattern "yyyy-MM-dd HH:mm", using the STRICT resolver style, rejects even date strings that it formatted itself.

Upon further investigation, this seems to be related to DateTimeFormatter's new (relative to the well-established java.text.SimpleDateFormat) distinction between "year" and "year of era". With DateTimeFormatter, the 'y' format symbol represents the latter; the former corresponds to format letter 'u'. It turns out that if I use the format string "uuuu-MM-dd HH:mm" that your program produces the output you seem to be looking for:

private void convertToLocalDateTime(String s) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-M-d HH:mm")
.withResolverStyle(ResolverStyle.STRICT);
try {
LocalDateTime ldt = LocalDateTime.parse(s, formatter);
System.out.println("s: " + s + " -> ldt: " + ldt.toString());
} catch(DateTimeParseException e) {
System.out.println("s: " + s + " -> The field is not a valid date.");
}
}

Single-digit vs. double-digit fields does not make a difference for parsing.

This question addresses the difference between "year" and "year of era". Whereas I understand the distinction, I think it was a poor choice to implement DateTimeFormatter with such unnecessary incompatibility with SimpleDateFormat for some of their most common uses. I think I would have swapped the meaning of 'u' and 'y', so as to avoid exactly the kind of problem you've run into. Evidently, however, the designers of this API disagreed, and that's all water under the bridge at this point.

How can I parse/format dates with LocalDateTime? (Java 8)

Parsing date and time

To create a LocalDateTime object from a string you can use the static LocalDateTime.parse() method. It takes a string and a DateTimeFormatter as parameter. The DateTimeFormatter is used to specify the date/time pattern.

String str = "1986-04-08 12:30";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime dateTime = LocalDateTime.parse(str, formatter);

Formatting date and time

To create a formatted string out a LocalDateTime object you can use the format() method.

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime dateTime = LocalDateTime.of(1986, Month.APRIL, 8, 12, 30);
String formattedDateTime = dateTime.format(formatter); // "1986-04-08 12:30"

Note that there are some commonly used date/time formats predefined as constants in DateTimeFormatter. For example: Using DateTimeFormatter.ISO_DATE_TIME to format the LocalDateTime instance from above would result in the string "1986-04-08T12:30:00".

The parse() and format() methods are available for all date/time related objects (e.g. LocalDate or ZonedDateTime)



Related Topics



Leave a reply



Submit