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
How to Represent a Range in Java
Access "This" from Java Anonymous Class
Jackson JSON Custom Serialization for Certain Fields
Java Array with More Than 4Gb Elements
Why String.Replaceall() in Java Requires 4 Slashes "\\\\" in Regex to Actually Replace "\"
Turning an Executorservice to Daemon in Java
Difference Between Webdriver.Get() and Webdriver.Navigate()
If Parenthesis Has a Higher Precedence Then Why Is Increment Operator Solved First
How Can "This" of the Outer Class Be Accessed from an Inner Class
Apache Httpclient Making Multipart Form Post