"Text '2023-07-15 14:30' could not be parsed at index 0" – ZonedDateTime conversion failure in Cucumber step definitions
0 reputation · 27 May 2023, 07:34 UTC
0 reputation · 27 May 2023, 07:34 UTC
When a Cucumber step definition expects a ZonedDateTime parameter and the step passes a date‑time string without time‑zone information (e.g., "2023-07-15 14:30"), the built‑in parameter converter throws a DateTimeParseException with the message "Text '2023-07-15 14:30' could not be parsed at index 0". The framework does not apply the JVM default time zone or a locale‑specific zone during conversion, leaving the developer to supply a custom @ParameterType or DataTableType converter. An open discussion in the cucumber‑jvm repository questions whether a zone‑aware fallback converter should be provided.
ZonedDateTime?1. Does Cucumber automatically apply the JVM default zone?
No. The built‑in converter for ZonedDateTime requires a zone or offset in the input string. A string such as "2023-07-15 14:30" is parsed with DateTimeFormatter.ISO_ZONED_DATE_TIME, which throws a DateTimeParseException because the pattern cannot resolve a zone.
2. Would a configurable zone‑aware fallback break existing reuse?
Yes. Existing step definitions that intentionally rely on the exception to catch malformed input would silently succeed, potentially masking bugs. A global fallback would also alter the semantics of any step that expects a specific zone, causing subtle time‑zone drift.
3. What criteria should govern a default fallback?
ZonedDateTime.• Cucumber‑jvm’s default ZonedDateTime converter uses DateTimeFormatter.ISO_ZONED_DATE_TIME.
• It does not fall back to ZoneId.systemDefault().
• The framework’s source and release notes (as of 2026‑10‑10) contain no mention of a zone‑aware fallback.
Verify the failure by running a scenario that passes a zone‑less string to a ZonedDateTime parameter. The exception message should read Text '2023-07-15 14:30' could not be parsed at index 0.
Define a custom parameter type:
@ParameterType("\d{4}-\d{2}-\d{2} \d{2}:\d{2}")
public ZonedDateTime zonedDateTime(String text) {
DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").withZone(ZoneId.systemDefault());
return ZonedDateTime.parse(text, formatter);
}
Alternatively, use a DataTableType if the value comes from a table.
If you prefer a global fallback, add a system property (e.g., cucumber.timezone.fallback=true) and implement a small extension that checks the property before delegating to the default converter.
To tailor the recommendation to your environment, could you confirm the cucumber-jvm version you are using? The current implementation may differ between 7.x and 8.x releases.
Use comments to ask for clarification. Post a solution as an answer.
32,275 reputation · 27 May 2023, 12:59 UTC
You can avoid writing a separate @ParameterType by using a DateTimeFormatterBuilder that supplies a default zone when it is missing:
@ParameterType(".*")
public ZonedDateTime zoneLess(String s) {
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
.appendPattern("yyyy-MM-dd HH:mm")
.parseDefaulting(ChronoField.ZONE_ID, ZoneId.systemDefault().getId())
.toFormatter();
return ZonedDateTime.parse(s, fmt);
}This keeps the converter thread‑safe because ZoneId.systemDefault() returns an immutable ID, and you can make the fallback configurable via a system property (e.g., -Dcucumber.zonedatetime.defaultZone=Europe/Paris) if you need a fixed zone across environments.