Дата на один день назад после выбора из БД MySQL - PullRequest
0 голосов
/ 13 февраля 2019

У меня проблема с часовым поясом.

У меня есть программа в базе данных JAVA и MySql.Я не использую ORM, но у меня есть специальная библиотека jdbc, которая использует MySQL-коннектор java v 6.0.4

. Я отправляю настройки как часть строки подключения как:

 serverTimezone - America/Chicago
 useTimezone - true
 useJDBCCompliantTimezoneShift - false
 useLegacyDatetimeCode - false

Я пытаюсьчтобы выбрать дату из базы данных, например, 2019-02-13.

База данных расположена в часовом поясе США / Восточная, а сервер, на котором запущена Java-программа, находится в часовом поясе Америка / Чикаго.

Нет способаизменить местоположение / часовой пояс любого из серверов.

В Java я получаю дату на один день назад (2019-02-12).Проблема вызвана из-за отметки времени -

1550034000 равно 2019-02-13 00:00:00 в США / восточном

, но

1550034000 равно 2019-02-12 23:00:00 в Америке / Чикаго

Итак, в результате у меня есть java.sql.Date объект с датой 2019-02-12.Добавить смещение часового пояса не помогает, поскольку информация о времени отсекается от даты.

Можете ли вы предложить какое-нибудь решение о том, как получить правильную дату без смещения часового пояса?

Редактировать: Я использую настройку serverTimezone, но я не уверен, должно ли значение быть часовым поясом, что база данныхиспользуя или просто переопределяя часовой пояс JVM / сервера, на котором запущено приложение.

Ответы [ 2 ]

0 голосов
/ 13 февраля 2019

Я использую настройку serverTimezone, но я не уверен, должно ли значение быть часовым поясом, используемым базой данных, или это просто переопределение часового пояса JVM / сервера, на котором выполняется приложение.

serverTimezone=America/Chicago означает «интерпретировать результаты с сервера так, как если бы сервер использовал часовой пояс America/Chicago независимо от часового пояса по умолчанию, который сервер настроил для использования».Поэтому, если вы используете этот параметр в строке подключения, вы получите Timestamp значения, преобразованные в America/Chicago, даже если часовой пояс по умолчанию для сервера, по-видимому, America/New_York.

Однако, прежде чем перейти к такому подходуВы хотели бы подтвердить, что сервер действительно использует America/New_York (что, по-видимому, будет переключаться назад и вперед между Восточным стандартным временем и Восточным дневным временем), а не фиксированное смещение, как UTC-5 (которое всегда будет оставаться на "Восточном стандарте".Время ").

0 голосов
/ 13 февраля 2019

Во-первых, я прочитал, что вы не можете в вашем случае, но для других читателей я хотел бы заявить, что общая рекомендация - запускать все в 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...