How to Select Dates from Date Pickers with Selenium Java
Learn reliable Selenium Java strategies for native date inputs and custom calendars, with verification, waits, troubleshooting, and runnable code.

Direct answer: first determine whether the page uses a native <input type="date"> or a custom calendar widget. For a native control, enter a valid ISO date such as 2026-10-15 and assert the element’s normalized value. For a custom picker, open the widget, navigate its month and year controls, click the correct day, and verify the resulting field value or application state. There is no universal calendar locator because custom date pickers are built from each application’s own HTML, CSS, and JavaScript.
This guide shows both approaches in Selenium 4 with Java, including explicit waits, date bounds, duplicate day numbers, keyboard actions, troubleshooting, and verification.
1. Inspect the date control before writing a locator
Open browser developer tools and inspect the field and its surrounding markup. Record:

- Whether the control is
input[type=date]or a custom input plus calendar trigger. - A stable ID, name, label relationship, or test-specific attribute.
- For custom widgets, the trigger, month/year heading, previous and next buttons, day buttons, and the attribute that marks days from adjacent months.
- Whether the field has
min,max,required,readonly, or disabled state.
Selenium’s element commands include click, clear, send keys, and submit. WebDriver checks interactability, so a hidden or non-keyboard-interactable element can produce an element-not-interactable error. Use the control’s real user-facing path and stable locators. See the Selenium element interaction documentation.
2. Native HTML date inputs
A native date input gets its picker UI from the browser and operating system. The visible date format can follow the user’s locale, but the stored value is normalized as yyyy-mm-dd. Assert that canonical value instead of comparing localized text. Native inputs can also reject dates outside their min and max bounds. See MDN’s date input reference.
Runnable Java example
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.time.Duration;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
class NativeDatePickerTest {
private WebDriver driver;
private WebDriverWait wait;
@BeforeEach
void setUp() {
driver = new ChromeDriver();
wait = new WebDriverWait(driver, Duration.ofSeconds(10));
driver.get("https://example.test/booking");
}
@Test
void selectsNativeDate() {
WebElement date = wait.until(ExpectedConditions.elementToBeClickable(
By.id("startDate")));
String min = date.getAttribute("min");
String max = date.getAttribute("max");
if (min != null && "2026-10-15".compareTo(min) < 0) {
throw new AssertionError("Target is before min=" + min);
}
if (max != null && "2026-10-15".compareTo(max) > 0) {
throw new AssertionError("Target is after max=" + max);
}
date.clear();
date.sendKeys("2026-10-15");
assertEquals("2026-10-15", date.getAttribute("value"));
}
@AfterEach
void tearDown() {
if (driver != null) driver.quit();
}
}
Replace the URL, locator, and target date with values from the application under test. If direct entry is rejected, inspect which element is actually editable and use the browser-supported interaction for that control. Assigning a DOM property with JavaScript is not automatically equivalent to a user action: framework listeners and validation may depend on input and change events.
Native date checklist
- Locate the actual date input, not only its decorative calendar icon.
- Confirm it is enabled, displayed, and editable.
- Read
minandmaxbefore selecting a test date. - Send
yyyy-MM-dd. - Assert
getAttribute("value"). - Submit or blur the field if the application validates on that event, then assert the resulting state.
3. Custom calendar widgets
Custom calendars require the widget’s own controls. A robust sequence is:
- Click the input or calendar trigger.
- Wait until the calendar is visible.
- Read the displayed month and year.
- Click next or previous month controls until the target month is displayed.
- Click a day control that belongs to the displayed month.
- Verify the associated input value and any application state.
Do not assume that a day number is unique. Calendars often render trailing days from the previous month and leading days from the next month. Prefer an attribute such as aria-label, a data-date attribute, or a class that identifies outside-month days.
Runnable Java pattern with explicit waits
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.time.Duration;
import java.time.YearMonth;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
class CustomCalendarTest {
@Test
void selectsCustomCalendarDate() {
WebDriver driver = new ChromeDriver();
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
try {
driver.get("https://example.test/booking");
wait.until(ExpectedConditions.elementToBeClickable(
By.cssSelector("[data-testid='date-trigger']"))).click();
By calendar = By.cssSelector("[data-testid='calendar']");
wait.until(ExpectedConditions.visibilityOfElementLocated(calendar));
YearMonth target = YearMonth.of(2026, 10);
for (int i = 0; i < 24; i++) {
String headingText = wait.until(ExpectedConditions.visibilityOfElementLocated(
By.cssSelector("[data-testid='calendar-heading']"))).getText();
YearMonth shown = YearMonth.parse(headingText,
java.time.format.DateTimeFormatter.ofPattern("MMMM yyyy",
java.util.Locale.ENGLISH));
if (shown.equals(target)) break;
if (shown.isBefore(target)) {
wait.until(ExpectedConditions.elementToBeClickable(
By.cssSelector("[data-testid='next-month']"))).click();
} else {
wait.until(ExpectedConditions.elementToBeClickable(
By.cssSelector("[data-testid='previous-month']"))).click();
}
wait.until(ExpectedConditions.textToBePresentInElementLocated(
By.cssSelector("[data-testid='calendar-heading']"),
target.getMonth().getDisplayName(java.time.format.TextStyle.FULL,
java.util.Locale.ENGLISH) + " " + target.getYear()));
}
By day = By.cssSelector("button[data-date='2026-10-15']");
wait.until(ExpectedConditions.elementToBeClickable(day)).click();
WebElement input = wait.until(ExpectedConditions.visibilityOfElementLocated(
By.id("startDate")));
assertEquals("2026-10-15", input.getAttribute("value"));
} finally {
driver.quit();
}
}
}
The data-testid and data-date attributes are illustrative. Substitute the attributes exposed by your widget. If the calendar only exposes day text, scope the locator to the current month container and exclude elements marked outside the month.
When the widget supports keyboard input
Some calendars support arrow keys, Page Up/Page Down, Enter, or Escape. Use Selenium’s Actions API when that is the documented user path, and still verify the resulting value. The Selenium keyboard actions documentation shows Java key sequences and designated-element input.
import org.openqa.selenium.Keys;
import org.openqa.selenium.interactions.Actions;
WebElement trigger = wait.until(ExpectedConditions.elementToBeClickable(
By.cssSelector("[data-testid='date-trigger']")));
trigger.click();
new Actions(driver)
.sendKeys(Keys.ARROW_RIGHT)
.sendKeys(Keys.ENTER)
.perform();
Do not use this sequence unless inspection confirms the widget’s keyboard behavior; key meanings vary by implementation.
4. Waiting, synchronization, and verification
Replace fixed sleeps with waits for observable state: the trigger is clickable, the calendar is visible, the heading changes, the target day is enabled, and the field value equals the expected date. A click returning does not prove that the application accepted the date.
- Wait for a calendar container to become visible after opening.
- After navigation, wait for the heading or month data attribute to change.
- Wait for the target day to be enabled and clickable.
- After selection, wait for the input’s value or a validation message.
- Assert submitted results when the date affects search, pricing, availability, or another page state.
5. Common errors and fixes
| Error or symptom | Likely cause | Fix |
|---|---|---|
ElementNotInteractableException |
The located element is hidden, disabled, or not keyboard-interactable. | Locate the visible editable input or trigger; wait for clickability; inspect overlays and disabled state. |
InvalidElementStateException |
The field cannot be edited in its current state. | Check readonly, disabled state, constraints, and whether a custom trigger must be used. |
| Date appears correct but assertion fails | The display is localized while the stored value is ISO-normalized. | Assert getAttribute("value") as yyyy-MM-dd. |
| Wrong day is selected | Duplicate day numbers from adjacent months are present. | Use a full date attribute or scope to the current-month panel; exclude outside-month elements. |
| Month navigation loops or times out | Heading parsing or next/previous locator is wrong, or the control is disabled. | Log the heading, inspect its format, cap navigation iterations, and handle disabled boundaries. |
| Click intercepted | Animation, popup, or overlay covers the target. | Wait for the overlay to disappear and for the target to be clickable; close the widget’s own popup correctly. |
| Selection is cleared on submit | Date violates min/max or application validation. |
Read constraints, select an allowed date, and assert validation text or submitted state. |
| JavaScript assignment has no effect | Framework state and input/change events were bypassed. | Use supported WebDriver interactions or dispatch the framework’s documented events only when necessary. |
6. Performance, reliability, and maintainability
- Use one browser session per test or fixture strategy rather than repeatedly starting browsers inside a date loop.
- Keep waits short and targeted; a long global implicit wait can hide locator problems and slow failures.
- Prefer semantic attributes, labels, and test IDs over absolute XPath tied to DOM depth.
- Use deterministic dates that satisfy the environment’s allowed range. For tests near today, inject a clock or calculate dates consistently.
- Cover month boundaries, leap days, year changes, minimum and maximum dates, disabled dates, and locale-specific display.
- Capture a screenshot and page source on failure so a calendar’s actual rendered state can be diagnosed.
- For parallel runs, avoid sharing a driver or mutable date state between tests.
7. Capture evidence with ScreenshotNeo
When a date-picker test fails in CI, a browser screenshot makes the rendered state easier to inspect. ScreenshotNeo is a website screenshot API and MCP server. It can remove cookie and consent banners, newsletter popups, and chat widgets before capture, and only clean shots are billed. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed; the response reports the verdict in X-Page-Verdict and billing in X-Billed.

Or skip the browser setup
If the goal is a clean image or PDF of a page rather than WebDriver interaction, call the API directly. The ScreenshotNeo documentation lists all options.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
8. Short FAQ
Can I use one locator for every Selenium date picker?
No. Native controls and custom widgets expose different interfaces, and custom markup varies by application. Inspect each implementation.
Should I assert the text visible in the calendar?
Use the control’s canonical value or application state. Visible text can be localized and may include duplicate days from adjacent months.
Is JavaScript click a reliable fallback?
It can bypass the real interaction path and event handling. Prefer WebDriver click and keyboard actions after making the element interactable.
What date format should Java send to a native input?
Use the normalized yyyy-MM-dd representation and verify the element’s value.
How do I test an unavailable date?
Assert that the day is disabled or rejected, then verify the validation message or unchanged field value.


