Learning Objectives
By the end of this lesson you will be able to:
- Distinguish between server-side and client-side date restriction and explain which is authoritative
- Test birth date fields that must not accept future dates
- Test booking date fields that must not accept past dates
- Identify the off-by-one error that occurs at the boundary between past and future (midnight)
- Explain the timezone edge case where a "future" date for the client is a "past" date on the server
Two Common Restrictions
Date fields in most applications fall into one of two restriction categories:
| Field type | Rule | Example |
|---|---|---|
| Birth date | Must not be in the future | Cannot be born tomorrow |
| Booking / appointment | Must not be in the past | Cannot book yesterday's slot |
These sound simple, but they hide several subtle edge cases that trip up even experienced developers.
Server-Side vs Client-Side Enforcement
HTML5 provides min and max attributes for <input type="date">, and JavaScript can add additional client-side validation. However, client-side restrictions are presentational, not authoritative. A user can disable JavaScript, use developer tools to remove the max attribute, or send a raw HTTP request bypassing the UI entirely.
The only reliable validation is server-side. A QA tester must test both layers:
- Does the UI prevent obviously invalid dates? (usability)
- Does the server reject invalid dates sent directly? (security and correctness)
A critical finding is when the server accepts a future birth date even though the UI blocked it.
The Off-by-One at Midnight
Suppose a booking field requires a date "not in the past". What about today? This is ambiguous — "today" is simultaneously not in the past and not in the future. Most systems intend to accept today, but the implementation matters.
If the server compares the submitted date against LocalDate.now() using strict less-than (<), today is accepted. But if it uses less-than-or-equal (<=), today is rejected. This is a classic off-by-one error at the boundary.
The midnight transition makes this worse. A test that passes at 11:00 AM may fail at 11:59 PM if the server's "today" has moved forward. Always test with fixed dates, not relative ones.
Timezone Edge: "Tomorrow" in UTC+12
Consider a user in New Zealand (UTC+12) submitting a booking for their local "today". In UTC, that same moment may already be yesterday or two days from now depending on the time. From the server's perspective (running in UTC), the user's "today" might be "tomorrow".
This causes false rejections: valid bookings are refused because the server thinks the date is in the future. The fix is for the server to interpret submitted dates in the user's stated timezone before comparing. The test case: a user in UTC+12 submits a date that is "today" in their timezone at 23:00 UTC+12 (11:00 UTC). The server must accept it.
Testing Strategy
For birth date (no future):
| Test case | Input | Expected |
|---|---|---|
| Today | today's date | Accept |
| Yesterday | today − 1 day | Accept |
| Tomorrow | today + 1 day | Reject |
| Far past | 1900-01-01 | Accept (if no min age rule) |
| Far future | 2099-12-31 | Reject |
For booking date (no past), swap Accept/Reject for past and future rows.
Always use fixed dates in test scripts rather than new Date() to ensure reproducibility. Pin the clock in unit tests and use fixed date strings in API-level tests.
Summary
Past/future date restrictions seem trivial but contain three subtle failure points: the difference between client and server enforcement, the off-by-one error at today's boundary, and timezone disagreement about what "today" actually means. A thorough QA strategy tests all three layers: UI, server API, and timezone-aware edge cases.