Все еще смущены метками времени Java и т. Д. С MySQL - PullRequest
7 голосов
/ 22 марта 2012

После поиска и чтения о том, как обрабатывать дату и время в / из Java и MySQL, я все еще в замешательстве.

Допустим, я создаю java.util.Date объект. Этот объект содержит время в UTC. Любое форматирование или разбор в другие часовые пояса может быть выполнено, например, с помощью java.text.SimpleDateFormat.

Теперь я хочу сохранить свой объект date в базе данных MySQL в UTC. Но когда я использую метод setTimestamp() в java.sql.PreparedStatement, я немного растерялся. Ниже приведен пример кода, в котором я тестирую MySQL DATETIME и TIMESTAMP в своей таблице. Я также вставляю даты с помощью методов setString() и setTimestamp().

java.sql.Connection conn = java.sql.DriverManager.getConnection("jdbc:mysql://localhost/test","user","password");
java.sql.Statement st = conn.createStatement();

String q = "DROP TABLE IF EXISTS tmp";
st.execute(q);
q = "CREATE TABLE tmp (dt_string TEXT, dt DATETIME, ts TIMESTAMP)";
st.execute(q);

java.sql.PreparedStatement pst = conn.prepareStatement("INSERT INTO tmp SET dt_string=?, dt=?, ts=?");

java.text.SimpleDateFormat utc = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
utc.setTimeZone(java.util.TimeZone.getTimeZone("UTC"));

java.util.TimeZone.setDefault(java.util.TimeZone.getTimeZone("EST"));

System.out.println("Default time zone: " + java.util.TimeZone.getDefault().getID());
java.util.Date d = new java.util.Date();
System.out.println("A date: " + d);
java.sql.Timestamp t = new java.sql.Timestamp( d.getTime() );
System.out.println("The timestamp: " + t);


pst.setString(1, utc.format(d) );
pst.setString(2, utc.format(d) );
pst.setString(3, utc.format(t) );
pst.execute();

pst.setTimestamp(2, t);
pst.setTimestamp(3, t);
pst.execute();

System.out.println("Use calendar: " + utc.getCalendar().getTimeZone() );
pst.setTimestamp(2, t, utc.getCalendar());
pst.setTimestamp(3, t, utc.getCalendar());
pst.execute();

conn.close();

Когда я запускаю вышеупомянутое, я получаю следующий вывод, который является ожидаемым.

Default time zone: EST
A date: Thu Mar 22 08:49:51 EST 2012
The timestamp: 2012-03-22 08:49:51.784
Use calendar: sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]

Но когда я проверяю таблицу в базе данных с помощью инструмента командной строки MySQL, я получаю:

mysql> select * from tmp;
+---------------------+---------------------+---------------------+
| dt_string           | dt                  | ts                  |
+---------------------+---------------------+---------------------+
| 2012-03-22 13:49:51 | 2012-03-22 13:49:51 | 2012-03-22 13:49:51 |
| 2012-03-22 13:49:51 | 2012-03-22 08:49:51 | 2012-03-22 08:49:51 |
| 2012-03-22 13:49:51 | 2012-03-22 08:49:51 | 2012-03-22 08:49:51 |
+---------------------+---------------------+---------------------+
3 rows in set (0.00 sec)

Первый столбец - это просто текстовый тип, в котором я храню дату в формате UTC.

В первой строке я сохранил даты, используя метод setString().

Во второй строке я сохранил дату, используя метод setTimestamp(i,t). Я предполагаю, что JDBC автоматически преобразует дату, используя часовой пояс по умолчанию (который я установил в EST), прежде чем он сохранит ее. Но TIMESTAMP не должен всегда автоматически сохраняться в UTC. Документация MySQL гласит: MySQL преобразует значения TIMESTAMP из текущего часового пояса в UTC для хранения и обратно из UTC в текущий часовой пояс для получения. (выпуск 1).

Наконец, для третьей строки я использовал pst.setTimestamp(2, t, utc.getCalendar()); для хранения даты с надеждой, что водитель должен использовать часовой пояс UTC. Но, видимо, нет (выпуск 2).

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

Ответы [ 2 ]

1 голос
/ 23 марта 2012

Наконец, я кое-что понимаю, но вместо этого использую PostgreSQL. Я в основном повторил код выше, используя

Connection conn = DriverManager.getConnection("jdbc:postgresql://localhost");

st.execute("CREATE TABLE tmp (ts_string TEXT, ts TIMESTAMP WITH TIME ZONE)");
//st.execute("CREATE TABLE tmp (ts_string TEXT, ts TIMESTAMP WITHOUT TIME ZONE)");

и запись даты в виде

pst.setString(1, utc.format(d));
pst.setTimestamp(2, t);
pst.execute();

pst.setTimestamp(2, t, utc.getCalendar());
pst.execute();

Когда используется TIMESTAMP WITH TIMEZONE, я получаю следующий вывод из psql

postgres=# select * from tmp;
        ts_string        |             ts             
-------------------------+----------------------------
 2012-03-23 12:48:28.057 | 2012-03-23 13:48:28.057+01
 2012-03-23 12:48:28.057 | 2012-03-23 13:48:28.057+01
(2 rows)

потому что он отслеживает часовой пояс системы (сервера), который является CET (+01). Даты правильные, хотя отображаются в CET.

Если я вместо этого использую TIMESTAMP WITHOUT TIME ZONE, я получу

postgres=# select * from tmp;
        ts_string        |           ts           
-------------------------+------------------------
 2012-03-23 12:49:04.120 | 2012-03-23 07:49:04.12
 2012-03-23 12:49:04.120 | 2012-03-23 12:49:04.12
(2 rows)

и в первом случае он использует часовой пояс по умолчанию (который я установил в EST), и дата, естественно, «неправильная», как в случае с MySQL. Однако во втором случае он использует UTC, потому что я передал это в качестве аргумента методу setTimestamp(), и это то, что я пытался сделать в MySQL. Мне кажется, что Java-драйвер MySQL просто игнорирует аргумент Calendar в setTimestamp(). Может быть, я что-то пропустил.

0 голосов
/ 23 марта 2012

Поля даты и времени в MySql просто великолепны, когда вы их оборачиваете.

используемый вами «SimpleDateFormat» не имеет привязанного к нему часового пояса, поэтому ваша база данных предполагает, что вы вводите дату в часовом поясе вашего сервера. Если вы находитесь в часовом поясе -0500, он добавляет к нему 5 часов, чтобы преобразовать его в UTC, а затем, когда вы получаете его, вычитает 5 часов, чтобы преобразовать его обратно в ваше местное время.

Когда вы преобразовали свою временную метку в utc до того, как вставили ее, вы все равно не прикрепили часовой пояс к строке. Ваша база данных выполнила ту же операцию на нем. 5 часов добавлено в 2012-03-22 13:49:51, поэтому ваша база данных хранится 2012-03-22 18:49:51 (все еще предполагается, что -0500).

Действительно забавная часть всего этого заключается в том, что вы можете установить часовой пояс для соединения с базой данных, и все даты, которые вы извлекаете или вставляете, будут сдвигаться вместе с вашим соединением!

...