{"record":{"id":"99b0a6a543068dcb","repo":"hibernate/hibernate-orm","slug":"illegal-attempt-to-treat-java-sql-time-as-java","errorCode":null,"errorMessage":"Illegal attempt to treat `java.sql.Time` as `java.sql.Date`","messagePattern":"Illegal attempt to treat `java\\.sql\\.Time` as `java\\.sql\\.Date`","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java","lineNumber":173,"sourceCode":"\t\t\treturn type.cast( value.getTime() );\n\t\t}\n\n\t\tif ( String.class.isAssignableFrom( type ) ) {\n\t\t\treturn type.cast( toString( value ) );\n\t\t}\n\n\t\tif ( Calendar.class.isAssignableFrom( type ) ) {\n\t\t\tfinal var gregorianCalendar = new GregorianCalendar();\n\t\t\tgregorianCalendar.setTimeInMillis( value.getTime() );\n\t\t\treturn type.cast( gregorianCalendar );\n\t\t}\n\n\t\tif ( java.sql.Timestamp.class.isAssignableFrom( type ) ) {\n\t\t\treturn type.cast( new java.sql.Timestamp( value.getTime() ) );\n\t\t}\n\n\t\tif ( java.sql.Date.class.isAssignableFrom( type ) ) {\n\t\t\tthrow new IllegalArgumentException( \"Illegal attempt to treat `java.sql.Time` as `java.sql.Date`\" );\n\t\t}\n\n\t\tthrow unknownUnwrap( type );\n\t}\n\n\tprivate static LocalTime unwrapLocalTime(Time value) {\n\t\tfinal var localTime = value.toLocalTime();\n\t\tlong millis = value.getTime() % 1000;\n\t\tif ( millis == 0 ) {\n\t\t\treturn localTime;\n\t\t}\n\t\tif ( millis < 0 ) {\n\t\t\t// The milliseconds for a Time could be negative,\n\t\t\t// which usually means the time is in a different time zone\n\t\t\tmillis += 1_000L;\n\t\t}\n\t\treturn localTime.with( ChronoField.NANO_OF_SECOND, millis * 1_000_000L );\n\t}","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java#L155-L191","documentation":"JdbcTimeJavaType is Hibernate's descriptor for TIME-precision values (java.sql.Time). Its unwrap() converts a Time to Calendar, Timestamp, LocalTime and others, but deliberately rejects java.sql.Date: converting a time-only value into a date-only value discards the time and is not a valid conversion. The IllegalArgumentException is thrown whenever a caller asks to unwrap a Time value as java.sql.Date.","triggerScenarios":"unwrap(time, java.sql.Date.class, options) reached when an attribute or parameter has @Temporal(TemporalType.DATE) but the bound value is managed by the TIME descriptor (e.g. setParameter(name, javaSqlTimeValue, TemporalType.DATE)); a java.sql.Time value bound to a DATE-mapped parameter; a custom UserType calling unwrap on JdbcTimeJavaType with java.sql.Date.class.","commonSituations":"Legacy java.util.Date fields queried with mismatched TemporalType; Hibernate 5 to 6 migrations where temporal type resolution changed; shared DTOs that reuse one field for both time and date semantics.","solutions":["Align temporal precision: the same @Temporal on the entity field and every query parameter binding for it","Move to java.time types (LocalTime for times) which map unambiguously","If a Date is genuinely needed from a Time value, convert manually with new java.sql.Date(time.getTime()) instead of unwrap","Check custom UserType/AttributeConverter code for unwrap() calls requesting java.sql.Date while handling time values"],"exampleFix":"// before\n@Temporal(TemporalType.TIME)\nprivate java.util.Date start;\n...\nquery.setParameter(\"p\", entity.getStart(), TemporalType.DATE); // IllegalArgumentException\n\n// after\nquery.setParameter(\"p\", entity.getStart(), TemporalType.TIME);\n// better: private LocalTime start;","handlingStrategy":"type-guard","validationCode":"if (value instanceof java.sql.Time) {\n    // TIME-precision value: bind with TemporalType.TIME only, never DATE\n}","typeGuard":"static boolean isTimePrecision(Object v) {\n    return v instanceof java.util.Date && !(v instanceof java.sql.Date);\n}","tryCatchPattern":"try {\n    query.setParameter(\"p\", value, TemporalType.TIME);\n} catch (IllegalArgumentException e) {\n    throw new IllegalStateException(\"Temporal precision mismatch: \" + e.getMessage(), e);\n}","preventionTips":["Prefer java.time.LocalTime for time-only fields","Keep @Temporal consistent between field and parameters","Do not reuse one java.util.Date field for both date-only and time-only semantics"],"tags":["hibernate","type-conversion","java-sql-time","java-sql-date","unwrap","temporal"],"backgroundTag":"temporal-type-mismatch","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}