LocalDate.parse()
Turn a date string such as 2026-07-04 into a LocalDate, use a custom format when needed, and turn bad input into a clean 400.
public static LocalDate parse(CharSequence text)
public static LocalDate parse(CharSequence text, DateTimeFormatter formatter)- Added in Java Java 8
Parameters
| Parameter | Type | Description |
|---|---|---|
$text | CharSequence | The text to parse. Without a formatter it must be an ISO-8601 date such as 2026-07-04. |
$formatteroptional | DateTimeFormatter= DateTimeFormatter.ISO_LOCAL_DATE | The formatter to use instead of DateTimeFormatter.ISO_LOCAL_DATE. |
Return value
LocalDateThe parsed local date.
Errors
DateTimeParseException (a DateTimeException) if the text cannot be parsed or is not a valid date; NullPointerException if text or formatter is null.
LocalDate.parse(text) reads an ISO-8601 date, yyyy-MM-dd, which is the format you should use in URLs, query strings and JSON anyway. It is the inverse of toString(): whatever a LocalDate prints, parse reads back.
The default format is strict
Without a formatter, parsing uses DateTimeFormatter.ISO_LOCAL_DATE. It wants two-digit months and days, so 2026-7-4 fails. It also uses the strict resolver, so 2026-02-30 is rejected rather than quietly turned into 28 February. For an API that is exactly what you want: the client sent something wrong and should be told.
Custom formats
For other layouts, pass a formatter: LocalDate.parse("04/07/2026", DateTimeFormatter.ofPattern("dd/MM/uuuu")). Two traps come with this.
- Formatters from
ofPatternuse the smart resolver. It clamps an over-large day to the end of the month, so31/04/2026becomes 30 April. Add.withResolverStyle(ResolverStyle.STRICT)when input comes from users. - Strict mode with
yyyyfails.yis year-of-era and needs an era to resolve strictly. Useuuuu, the proleptic year, in strict patterns.
Handling bad input
A bad string throws DateTimeParseException, an unchecked exception. In a web handler, catch it at the edge and answer with a 400 that names the field. getParsedString() returns the original text and getErrorIndex() the position where parsing stopped. Do not let it escape as a 500: a malformed date is the client's mistake, not the server's.
Ambiguous formats such as 04/07/2026 are 4 July in the UK and 7 April in the US. If you control the API, accept only ISO dates and the problem disappears. If you must accept local formats, decide on one per endpoint and document it.
To parse a full timestamp, use the matching type: LocalDateTime.parse, OffsetDateTime.parse or Instant.parse. LocalDate.parse rejects anything after the date.
Example
When to use it
- Reading ?from=2026-03-01&to=2026-03-31 query parameters for a report endpoint and answering 400 on bad input.
- Importing a CSV export from a UK system whose dates look like 04/07/2026, with a strict formatter.
- Reading a date of birth field from a JSON request body that a library has left as a string.
Frequently asked questions
How do I convert a String to a LocalDate in Java?
Use LocalDate.parse("2026-07-04") for ISO dates, or LocalDate.parse(text, DateTimeFormatter.ofPattern("dd/MM/uuuu")) for other layouts.
Why does LocalDate.parse accept 31/04/2026 with my formatter?
Formatters from ofPattern use ResolverStyle.SMART, which clamps the day to 30 April. Call withResolverStyle(ResolverStyle.STRICT) on the formatter, and use uuuu for the year.
What exception does LocalDate.parse throw?
DateTimeParseException, which is unchecked. Catch it at the edge of your API and turn it into a 400 response.
More examples
Smart versus strict resolving
The smart resolver quietly moves 31 April to 30 April, which hides bad input. The strict resolver rejects it, which is what a validating API needs.
import java.time.*;
import java.time.format.*;
void main() {
DateTimeFormatter smart = DateTimeFormatter.ofPattern("dd/MM/uuuu");
DateTimeFormatter strict = smart.withResolverStyle(ResolverStyle.STRICT);
IO.println("smart: " + LocalDate.parse("31/04/2026", smart));
try {
LocalDate.parse("31/04/2026", strict);
} catch (DateTimeParseException e) {
IO.println("strict: " + e.getMessage());
}
}
// Output:
// smart: 2026-04-30
// strict: Text '31/04/2026' could not be parsed: Invalid date 'APRIL 31'Parsing a date range from query parameters
Parse failures and logical errors both become 400 responses. getParsedString() gives back the client's original text so the error message can quote it.
import java.time.*;
import java.time.format.*;
record Range(LocalDate from, LocalDate to) {}
Range parseRange(Map<String, String> query) {
LocalDate from = LocalDate.parse(query.getOrDefault("from", "2026-01-01"));
LocalDate to = LocalDate.parse(query.getOrDefault("to", "2026-12-31"));
if (to.isBefore(from)) {
throw new IllegalArgumentException("'to' is before 'from'");
}
return new Range(from, to);
}
void main() {
List<Map<String, String>> requests = List.of(
Map.of("from", "2026-03-01"),
Map.of("from", "2026-03-01", "to", "2026-02-01"),
Map.of("to", "March 5"));
for (var query : requests) {
try {
IO.println("200 " + parseRange(query));
} catch (DateTimeParseException e) {
IO.println("400 bad date: " + e.getParsedString());
} catch (IllegalArgumentException e) {
IO.println("400 " + e.getMessage());
}
}
}
// Output:
// 200 Range[from=2026-03-01, to=2026-12-31]
// 400 'to' is before 'from'
// 400 bad date: March 5Choosing the right parse method for the input
LocalDate.parse refuses text with a time part, so parse a timestamp with Instant or OffsetDateTime and take the date from that. BASIC_ISO_DATE reads the compact yyyyMMdd form often found in file names.
import java.time.*;
import java.time.format.*;
void main() {
String stamp = "2026-07-04T10:15:30Z";
try {
LocalDate.parse(stamp);
} catch (DateTimeParseException e) {
IO.println("LocalDate.parse: " + e.getMessage());
}
IO.println("Instant.parse: " + Instant.parse(stamp));
IO.println("as a date: " + OffsetDateTime.parse(stamp).toLocalDate());
IO.println("basic ISO: " + LocalDate.parse("20260704", DateTimeFormatter.BASIC_ISO_DATE));
}
// Output:
// LocalDate.parse: Text '2026-07-04T10:15:30Z' could not be parsed, unparsed text found at index 10
// Instant.parse: 2026-07-04T10:15:30Z
// as a date: 2026-07-04
// basic ISO: 2026-07-04
Discussion