LocalDate转Oracle DATE时区问题求助(America/Asuncion夏令时场景)
Alright, let's tackle this problem you're hitting with Hibernate 5.4.6, Oracle 12g, and the America/Asuncion timezone's DST switch dates. First, let's confirm: this is indeed a known issue in older Hibernate versions (including 5.4.x) related to how timezone-aware date conversions handle edge cases like DST transitions.
Why This Happens
The America/Asuncion timezone has a DST transition where clocks jump forward by 1 hour (typically in early October). When Hibernate maps a LocalDate to Oracle's DATE type, it defaults to converting the local date to the midnight time in your configured timezone. On DST switch dates, that midnight time doesn't actually exist—the clock skips from 23:00 (day before) straight to 01:00 (switch day). Hibernate's default conversion logic adjusts this invalid time to the next valid timestamp (01:00 on the switch day), which gets stored in the Oracle DATE field (since Oracle DATE includes time). This leads to the unexpected time offset you're seeing.
Fixes to Try
1. Use a Custom Attribute Converter
The most reliable fix is to implement a custom AttributeConverter to handle the conversion between LocalDate and java.sql.Date explicitly, accounting for DST gaps. This lets you control exactly how invalid midnight times are resolved.
Here's an example converter that handles the DST gap by using the earliest valid offset for overlapping times:
import javax.persistence.AttributeConverter; import javax.persistence.Converter; import java.time.LocalDate; import java.time.ZoneId; import java.time.ZonedDateTime; import java.sql.Date; @Converter(autoApply = true) public class LocalDateToSqlDateDSTConverter implements AttributeConverter<LocalDate, Date> { private static final ZoneId TARGET_ZONE = ZoneId.of("America/Asuncion"); @Override public Date convertToDatabaseColumn(LocalDate localDate) { if (localDate == null) { return null; } // Resolve DST gaps by using the earliest valid time on the date ZonedDateTime zonedDateTime = localDate.atStartOfDay(TARGET_ZONE) .withEarlierOffsetAtOverlap(); return Date.from(zonedDateTime.toInstant()); } @Override public LocalDate convertToEntityAttribute(Date sqlDate) { if (sqlDate == null) { return null; } return sqlDate.toInstant() .atZone(TARGET_ZONE) .toLocalDate(); } }
- Set
autoApply = trueto apply this converter to allLocalDatefields in your entities, or annotate specific fields with@Convert(converter = LocalDateToSqlDateDSTConverter.class)if you only need it for certain cases.
2. Upgrade Hibernate to a Newer Version
Hibernate fixed several timezone-related bugs in later releases, particularly in the 5.5.x and 6.x branches. Upgrading to Hibernate 5.5.0 or higher should resolve this DST edge case out of the box. Just be aware of potential breaking changes (e.g., API adjustments in 6.x) and test thoroughly before upgrading in production.
3. Adjust Timezone Configuration (Alternative Approach)
If changing code or versions isn't feasible, you could try setting hibernate.jdbc.time_zone to UTC instead of America/Asuncion. This makes Hibernate use UTC for all date/time conversions, avoiding DST-related gaps entirely. However, you need to ensure your Oracle database is also configured to handle UTC consistently, and that your application logic accounts for the timezone shift when displaying dates to users.
Final Notes
The core issue stems from Hibernate's default handling of invalid timestamp values during DST transitions. The custom converter gives you the most control, while upgrading is the cleanest long-term solution.
内容的提问来源于stack exchange,提问作者Martin Irigaray

