hibernate/hibernate-orm · error · HibernateException
could not parse timestamp string %s
Error message
could not parse timestamp string %s
What it means
OffsetDateTimeJavaType.fromEncodedString() parses the text Hibernate stored for OffsetDateTime on databases without full timezone support. PARSE_FORMATTER accepts ISO_LOCAL_DATE_TIME with an optional offset ('+HH:MM:ss' or 'Z'); if no offset is present the value is assumed UTC. Text with a space separator, compact offsets like '+0100', trailing characters, or a bare date fails DateTimeParseException and is rethrown as HibernateException("could not parse timestamp string ...").
Source
Thrown at hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaType.java:118
}
@Override
public OffsetDateTime fromString(CharSequence string) {
return OffsetDateTime.from( ISO_OFFSET_DATE_TIME.parse( string ) );
}
@Override
public OffsetDateTime fromEncodedString(CharSequence charSequence, int start, int end) {
try {
final var temporalAccessor = PARSE_FORMATTER.parse( subSequence( charSequence, start, end ) );
return temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS )
? OffsetDateTime.from( temporalAccessor )
// For databases that don't have timezone support,
// we encode timestamps at UTC, so allow parsing
: LocalDateTime.from( temporalAccessor ).atOffset( ZoneOffset.UTC );
}
catch ( DateTimeParseException pe) {
throw new HibernateException( "could not parse timestamp string "
+ subSequence( charSequence, start, end ), pe );
}
}
@Override
public <X> X unwrap(OffsetDateTime offsetDateTime, Class<X> type, WrapperOptions options) {
if ( offsetDateTime == null ) {
return null;
}
if ( OffsetDateTime.class.isAssignableFrom( type ) ) {
return type.cast( offsetDateTime );
}
if ( ZonedDateTime.class.isAssignableFrom( type ) ) {
return type.cast( offsetDateTime.toZonedDateTime() );
}
View on GitHub (pinned to fad1729dce)
Solutions
- Re-encode the stored text to ISO local date-time with optional numeric offset ('2024-12-31T10:15:30' or '...+01:00')
- Prefer a native TIMESTAMP_WITH_TIMEZONE column and TimeZoneStorageType.NATIVE where the dialect supports it
- Keep hibernate.timezone.default_storage identical between the code that wrote and reads the data; rewrite data when it changes
- Add an AttributeConverter for the affected attribute that parses the actual legacy format
Example fix
// before: encoded column holds '2024-12-31 10:15:30 +01:00' // load -> HibernateException: could not parse timestamp string ... // after UPDATE meetings SET ts_text = REPLACE(ts_text, ' ', 'T'); -- '2024-12-31T10:15:30+01:00' parses (space only between date/time removed; offset kept compact fails -> use +01:00 form)
Defensive patterns
Strategy: validation
Validate before calling
static final java.time.format.DateTimeFormatter F = new java.time.format.DateTimeFormatterBuilder()
.append(java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.optionalStart().appendOffset("+HH:MM:ss", "Z").optionalEnd().toFormatter();
static boolean isParsableOffsetDateTimeText(CharSequence s) {
try { F.parse(s); return true; } catch (java.time.format.DateTimeParseException e) { return false; }
} Type guard
static java.time.OffsetDateTime tryOffsetDateTime(String s) {
try { return java.time.OffsetDateTime.parse(s); } catch (Exception e) { return null; }
} Try / catch
catch (HibernateException e) {
if (e.getCause() instanceof DateTimeParseException)
throw new IllegalArgumentException("Stored timestamp text is not ISO date-time[offset]: '" + text + "'", e);
throw e;
} Prevention
- Pin hibernate.timezone.default_storage at project start and never change it without a data migration
- Prefer native timestamptz columns (TimeZoneStorageType.NATIVE)
- Validate stored text in bulk after imports or storage-strategy changes
When it happens
Trigger: Dialect without native timezone support stores OffsetDateTime as text and the column content is not ISO-8601 (space separator, 'Z' variants like 'UTC'); changing hibernate.timezone.default_storage (e.g. NORMAL vs NORMALIZE_UTC) after data was written so the read format no longer matches; rows imported from CSV with re-formatted timestamps
Common situations: Migrating between databases with different timestamptz handling; storage-strategy configuration changes without data rewrite; hand-edited or externally loaded timestamp text
Related errors
- could not parse time string %s
- could not parse timestamp string %s
- Insert conflict 'do update' clause with constraint name is n
- field type not supported on Derby: " + unit
- format() function not supported on Derby
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/52800dacb3d5a666.
Report an issue: GitHub.