Во-первых, я прочитал, что вы не можете в вашем случае, но для других читателей я хотел бы заявить, что общая рекомендация - запускать все в UTC, по крайней мере, когда вы охватываете более одного часового пояса.Так что это было бы лучшим решением вашей проблемы.
Во-вторых, как я и Горд Томпсон упомянули в комментариях, второе лучшее решение - обрабатывать даты как LocalDate
, а не java.sql.Date
.Хотя последний только делает вид, что не имеет времени суток, у него действительно есть проблемы с дизайном, которые затрудняют решение вашей проблемы.LocalDate
действительно - это дата без времени дня и без часового пояса, поэтому ставка должна быть намного безопаснее (за исключением того, что драйверы баз данных, которые неправильно конвертируют в LocalDate
и обратно, были услышаныиз; я держу пальцы скрещенными; повторный запуск всего в UTC тоже устранит эти ошибки). Редактировать: Предполагая, что вы можете изменить свою пользовательскую библиотеку JDBC, вот как получить LocalDate
из ResultSet
:
LocalDate correctDateDirectlyFromDatabase
= yourResultSet.getObject("yourDateColumn", LocalDate.class);
Требуется как минимум JDBC 4.2, у вас, вероятно, естьчто.
Если ничего из вышеперечисленного вам недоступно, то здесь можно исправить неправильный Date
, полученный вами из базы данных.Это что-то вроде хака, но сработает.
import java.sql.Date;
// …
// Modern ID of the time zone previously known as US/Eastern
ZoneId datebaseTimeZone = ZoneId.of("America/New_York");
Date dateFromDatabase = new Date(TimeUnit.SECONDS.toMillis(1_550_034_000));
System.out.println("Date as retrieved from database (or pretending): " + dateFromDatabase);
long epochMillis = dateFromDatabase.getTime();
ZonedDateTime dateTime = Instant.ofEpochMilli(epochMillis)
.atZone(datebaseTimeZone);
LocalDate realDate = dateTime.toLocalDate();
// Sanity check
if (! realDate.atStartOfDay(datebaseTimeZone).equals(dateTime)) {
throw new IllegalStateException("Failed to convert date correctly from " + datebaseTimeZone + " time zone");
}
System.out.println("Date is " + realDate);
Когда я запустил это в часовом поясе Америки / Чикаго, он напечатал:
Date as retrieved from database (or pretending): 2019-02-12
Date is 2019-02-13
Iпытался запустить его в других часовых поясах.В некоторых часовых поясах первая строка печатает 2019-02-12
, в других 2019-02-13
.Последняя строка печатает 2019-02-13
во всех часовых поясах, которые я пробовал.
Теперь я дал вам LocalDate
.Это хорошо, это то, что вы должны использовать при дальнейшей обработке.Если вам нужен java.sql.Date
для другого унаследованного API, который вы пока не хотите менять, преобразуйте обратно в правильный java.sql.Date
следующим образом:
Date oldfashionedJavaSqlDate = Date.valueOf(realDate);
System.out.println("Date converted back to " + oldfashionedJavaSqlDate);
Дата преобразована обратно в 2019-02-13
И когда я говорю «правильно», требуется, чтобы никто не вмешивался в часовой пояс по умолчанию вашей JVM, что легко для любой программы, работающей вJVM для выполнения.
Ссылка: Учебное пособие по Oracle: Дата и время , объясняющее, как использовать java.time.