MySql datetime хранится не в формате UTC, а в часовом поясе сервера. - PullRequest
0 голосов
/ 15 января 2019

База данных Mysql: сохранение даты Java в столбце базы данных типа datetime, например,

    Table Foo
    id         time
   ---------------------
   bigint(20)   datetime

Дата

01/15/19 19:00:00 (Time Zone: UTC+1:00),

DateFormat df = new SimpleDateFormat('MM/dd/yy HH:mm:ss');
df.setTimeZone("Europe/Paris");   // UTC+1:00
Date date = df.parse("01/15/19 19:00:00");

PreparedStatement st = new PreparedStatement("insert into Foo (id, time) values (?, ?)";
st.setObject(1, 1);
st.setObject(2, date);
st.executeUpdate();  

, который, как ожидается, будет преобразован в

01/15/19 18:00:00 UTC

и хранится в базе данных.

Часовой пояс MySql - SYSTEM (UTC-6: 00). Часовой пояс JVM - UTC-6: 00. Оба работают на одной машине. Сохраненное значение: 15.01.1912: 00:00. Почему это хранится в Часовой пояс сервера (не UTC)?

выберите время из Foo;

time
-------------------
2019-01-15 12:00:00

Изменение часового пояса сервера не повлияло на выбранное значение.

mysql> set time_zone='+8:00';

select time from Foo;

    time
    -------------------
    2019-01-15 12:00:00

1 Ответ

0 голосов
/ 15 января 2019

ТЛ; др

Используйте современные java.time классы, никогда java.util.Date или java.sql.Date или java.sql.Timestamp.

myPreparedStatement                   // With JDBC 4.2, wa can directly exchange java.time objects with the database.
.setObject(                           // Pass a `OffsetDateTime` object representing the moment we want stored in the database.
    LocalDateTime.parse(              // Parse the input string lacking an indicator of offset or zone.
        "2019-01-19T19:00"            // When using strings in standard ISO 8601 format, no need to specify a formatting pattern.
    )                                 // Returns a `LocalDateTime`, an ambiguous value without real meaning.
    .atZone(                          // Apply a time zone to give meaning to the `LocalDateTime`. 
        ZoneId.of( "Europe/Paris" )   // Here we are saying "the 7 PM on that date as seen on the clock on the wall of someone standing in Paris France".
    )                                 // Returns a `ZonedDateTime` object.
    .toOffsetDateTime()               // Returns an `OffsetDateTime` object as demanded by the JDBC spec, stripping off the time zone to leave on the hours-minutes-seconds from UTC.
)

java.time

Вы используете ужасные классы даты и времени, которые были вытеснены несколько лет назад классами java.time . На самом деле нет смысла пытаться отлаживать / понимать унаследованные классы, так как они чертовски ужасны.

LocalDateTime

Разберите вашу входную строку как LocalDateTime, так как в ней отсутствует индикатор часовой пояс или смещение от UTC .

По возможности используйте стандартные форматы ISO 8601 для текста даты и времени. Для даты с временем суток будет YYYY-MM-DDTHH: MM: SS, где T отделяет часть даты от части времени.

String input = "2019-01-19T19:00" ;
LocalDateTime ldt = LocalDateTime.parse( input ) ; // No need to specify formatting pattern when using standard ISO 8601 formats.

ZonedDateTime

В классе LocalDateTime также отсутствует понятие зоны или смещения. Таким образом, объект этого класса не представляет момент, является не точкой на временной шкале. Он представляет потенциальные моменты в диапазоне около 26-27 часов, различные часовые пояса по всему земному шару.

Кажется, вы наверняка знаете, что эта дата и время должны были представлять момент в определенном часовом поясе. Поэтому примените часовой пояс, чтобы придать смысл нашему неоднозначному LocalDateTime объекту.

Укажите собственное имя часового пояса в формате Continent/Region, например America/Montreal, Africa/Casablanca или Pacific/Auckland. Никогда не используйте 2-4 буквенные сокращения, такие как EST или IST, так как они не истинные часовые пояса, не стандартизированы и даже не уникальны (!).

ZoneId z = ZoneId.of( "Europe/Paris" ) ;  

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

ZoneId z = ZoneId.systemDefault() ;  // Get JVM’s current default time zone.

Теперь мы можем применить ZoneId, чтобы получить ZonedDateTime.

ZonedDateTime zdt = ldt.atZone( z ) ;

OffsetDateTime

Ваш JDBC может принять объект ZonedDateTime, но спецификация JDBC 4.2 требует, чтобы он принимал OffsetDateTime. Какая разница? И ZonedDateTime, и OffsetDateTime представляют момент, точку на временной шкале. Смещение всего на несколько часов-минут-секунд вперед или назад по UTC. Часовой пояс гораздо больше. Часовой пояс - это история прошлых, настоящих и будущих изменений в смещении, используемом людьми определенного региона. Так что часовой пояс всегда предпочтительнее. За исключением случаев, когда мы используем JDBC для обмена объектом java.time с базой данных, мы используем OffsetDateTime для написания стандартного кода.

OffsetDateTime odt = zdt.toOffsetDateTime() ;  // Convert from moment with time zone to a moment with merely an offset-from-UTC.

Теперь мы можем передать этот момент вашему подготовленному заявлению.

myPreparedStatement( … , odt ) ;  // Pass a `OffsetDateTime` moment as the value for a placeholder in the SQL of your prepared statement.

индексирование.

OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;

Назначьте нужный часовой пояс.

ZoneId z = ZoneId.of( "Europe/Paris" ) ;
ZonedDateTime zdt = odt.atZone( z ) ;

И odt, и zdt, показанные в нескольких приведенных выше строках, представляют один и тот же момент одновременности, одну и ту же точку на временной шкале. Отличается только их время на настенных часах, так как большинство баз данных хранят и извлекают UTC, пока вы хотите видеть время по Парижу.


О java.time

Платформа java.time встроена в Java 8 и более поздние версии. Эти классы вытесняют проблемные старые устаревшие классы даты и времени, такие как java.util.Date, Calendar, & SimpleDateFormat.

Проект Joda-Time , теперь в режиме обслуживания , рекомендует выполнить переход на классы java.time .

Чтобы узнать больше, см. Oracle Tutorial . И поиск переполнения стека для многих примеров и объяснений. Спецификация JSR 310 .

Вы можете обмениваться java.time объектами напрямую с вашей базой данных. Используйте драйвер JDBC , совместимый с JDBC 4.2 или более поздней версии. Нет необходимости в строках, нет необходимости в java.sql.* классах.

Где получить классы java.time?

  • Java SE 8 , Java SE 9 , Java SE 10 , Java SE 11 и более поздние версии - часть стандартного Java API с связанной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и JavaSE 7
    • Большинство функций java.time перенесено в Java 6 & 7 в ThreeTen-Backport .
  • Android
    • Более поздние версии реализации связки Android java.time классы.
    • Для более ранних версий Android (<26) проект <a href="https://github.com/JakeWharton/ThreeTenABP" rel="nofollow noreferrer"> ThreeTenABP адаптируется ThreeTen-Backport (упомянуто выше).См. Как использовать ThreeTenABP… .

ThreeTen-Extra Проект расширяет java.time дополнительными классами.Этот проект является полигоном для возможных будущих дополнений к java.time.Здесь вы можете найти несколько полезных классов, таких как Interval, YearWeek, YearQuarter и more .

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