Great work!

XP to next level

BugEater

Parsing Failures: How Backends Misread Dates

Learning Objectives

By the end of this lesson you will be able to:

  • Explain how server-side date parsing works and what can go wrong
  • Distinguish between a hard parsing failure (exception) and silent coercion
  • Describe what happens when an impossible date like February 31 is parsed
  • Design a testing strategy to verify whether a backend validates dates strictly

How Server-Side Date Parsing Works

When a date string arrives at a backend — whether in a form submission, a JSON payload, or a query parameter — the server must convert it from a string to an internal date object. In Java, this is typically done using DateTimeFormatter paired with LocalDate.parse() or ZonedDateTime.parse(). The parser reads the string character by character according to a pattern, extracts the year, month, and day components, and then validates that the result is a real calendar date.

This process has two possible failure modes:

  1. Hard failure: the parser throws an exception when it cannot match the input string to the expected pattern or when it detects an impossible date.
  2. Silent coercion (lenient parsing): the parser accepts the input, adjusts impossible values to the nearest valid date, and returns a result without any error.

DateTimeParseException: The Right Outcome

In Java, java.time.format.DateTimeParseException is thrown when a date string does not match the expected format. For example, parsing "2024/04/05" with a pattern of "yyyy-MM-dd" will throw this exception because the separators do not match.

From a tester's perspective, DateTimeParseException (or its equivalent in other languages — ValueError in Python, FormatException in C#) is the correct response to invalid date input. The API should catch this exception and return an appropriate error response to the client, typically HTTP 400 Bad Request or HTTP 422 Unprocessable Entity, with a message indicating what went wrong.

When you send a malformed date and receive a 200 OK with no error, that is a bug.

Silent Coercion: The Dangerous Outcome

Some systems use lenient parsing, where the date parser does not reject impossible values but instead adjusts them. The classic example is February 31:

  • February has at most 29 days (in a leap year). Day 31 does not exist.
  • A lenient parser computes: February has 28 days (in 2024, a leap year, 29). Day 31 = day 29 + 2 overflow = March 2, 2024.
  • The parser returns March 2 without any error.

JavaScript's Date object is notorious for this behavior. new Date(2024, 1, 31) (month is 0-indexed, so 1 = February) returns Sat Mar 02 2024 silently. Python's datetime raises a ValueError by default. Java's LocalDate.parse throws DateTimeParseException by default.

The "first valid date" coercion pattern works like this: when an overflow occurs (day 31 in a 30-day month, day 29 in a non-leap-year February), the parser adds the overflow days to the last valid date of the month. So:

  • 2023-02-29 → March 1, 2023 (2023 is not a leap year)
  • 2024-04-31 → May 1, 2024 (April has 30 days)
  • 2024-02-31 → March 2, 2024 (leap year, 29 days in February)

The Testing Strategy for Date Parsing

Test 1 — Wrong format: Send a date in a format different from what the API specifies. If the API documents ISO 8601, send 04/05/2024. The expected result is a 400 or 422 error. If the API accepts it, there is a format validation bug.

Test 2 — Impossible day in month: Send 2024-02-31. The expected result is a 400 or 422 error. If the API returns 200 and stores a date, check what date was stored — it reveals whether lenient parsing is in use and by how many days the coercion shifted the value.

Test 3 — Month 13: Send 2024-13-01. There is no 13th month. The expected result is an error. If accepted, the backend has no month range validation.

Test 4 — Non-existent leap day: Send 2023-02-29. 2023 is not a leap year, so February 29 does not exist. A strict parser should reject this. A lenient parser returns March 1, 2023.

Test 5 — Compare sent vs stored value: For all of the above tests where the API returns 200, perform a follow-up GET request to read the stored value. If the stored date differs from the sent date, you have confirmed silent coercion. Document the delta (how many days the value shifted) and file as a defect.

Why This Matters

Silent coercion is dangerous because it produces wrong data without any signal to the user or the developer. A booking system that accepts 2024-02-31 and stores 2024-03-02 will book the customer on a completely different day with no error message. The customer believes their booking is for February 31 (which they interpret as a validation failure of their own input), while the system has silently created a March 2 booking.

Quiz

What does Java throw when a date string does not match the expected format pattern?

What happens in JavaScript when you parse "February 31" using the native Date constructor?

What does a "silent coercion" bug mean in the context of date parsing?

How can you test whether a backend validates date format strictly?