hibernate/hibernate-orm · error · HibernateException

could not parse time string %s

Error message

could not parse time string %s

What it means

JdbcTimeJavaType.fromString() parses time text with DateTimeFormatter.ISO_LOCAL_TIME, which requires 24-hour HH:mm:ss (fractional seconds allowed). Any other shape - 12-hour clock with AM/PM, missing seconds, trailing spaces, empty string - throws DateTimeParseException, rethrown as HibernateException("could not parse time string ..."). This path is used for HQL time literals and string-to-Time conversion.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java:259

				: LocalTime.ofInstant( value.toInstant(), ZoneOffset.systemDefault() );
	}

	@Override
	public String toString(Time value) {
		return LITERAL_FORMATTER.format( fromDate( value ) );
	}

	@Override
	public Time fromString(CharSequence string) {
		try {
			final var temporalAccessor = LITERAL_FORMATTER.parse( string );
			final var localTime = LocalTime.from( temporalAccessor );
			final var time = Time.valueOf( localTime );
			time.setTime( time.getTime() + localTime.getNano() / 1_000_000 );
			return time;
		}
		catch ( DateTimeParseException pe) {
			throw new HibernateException( "could not parse time string " + string, pe );
		}
	}

	@Override
	public Time fromEncodedString(CharSequence charSequence, int start, int end) {
		try {
			final var temporalAccessor = ENCODED_FORMATTER.parse( subSequence( charSequence, start, end ) );
			return Time.valueOf( temporalAccessor.query( LocalTime::from ) );
		}
		catch ( DateTimeParseException pe) {
			throw new HibernateException( "could not parse time string " + subSequence( charSequence, start, end ), pe );
		}
	}

	@Override
	public void appendEncodedString(SqlAppender sb, Time value) {
		LITERAL_FORMATTER.formatTo( fromDate( value ), sb );
	}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Convert times to 24-hour HH:mm:ss before binding or storing
  2. Use typed parameters (LocalTime or java.sql.Time) instead of string literals in queries
  3. Add an AttributeConverter that parses the legacy 12-hour format with a matching formatter
  4. Trim inputs and validate with LocalTime.parse before persisting

Example fix

// before
em.createQuery("select s from Shift s where s.begin = '3:04 PM'") // HibernateException

// after
em.createQuery("select s from Shift s where s.begin = '15:04:05'")
// or: query.setParameter("begin", LocalTime.of(15,4,5));
Defensive patterns

Strategy: validation

Validate before calling

static boolean isIsoTime(CharSequence s) {
    try { java.time.LocalTime.parse(s); return true; }
    catch (java.time.format.DateTimeParseException e) { return false; }
}

if (!isIsoTime(text.trim())) throw new IllegalArgumentException("Expected HH:mm:ss: " + text);

Type guard

static java.time.LocalTime tryIsoTime(String s) {
    try { return java.time.LocalTime.parse(s.trim()); } catch (Exception e) { return null; }
}

Try / catch

try { Time t = JdbcTimeJavaType.INSTANCE.fromString(text); }
catch (HibernateException e) {
    if (e.getCause() instanceof DateTimeParseException)
        throw new IllegalArgumentException("Bad time text: '" + text + "' (expected HH:mm:ss)", e);
    throw e;
}

Prevention

When it happens

Trigger: HQL literal like ... where e.start = '3:04 PM' or '10:15' (only '15:04:05' or '15:04:05.123' parse); fromString called with data exported from UI time pickers using 12-hour format; a String value coerced into a java.sql.Time attribute.

Common situations: Front-end time pickers producing 'h:mm a' strings; CSV/Excel imports with times like '3:04:05 PM'; locale-formatted time strings bound as literals in JPQL.

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/8d873a4cc64695a2. Report an issue: GitHub.