Является ли java.sql.Timestamp часовым поясом конкретным?

56

Мне нужно хранить UTC dateTime в БД.
Я преобразовал dateTime, заданный в определенный часовой пояс, в UTC. для этого я следовал нижеприведенному коду.
Мой ввод dateTime - "20121225 10:00:00 Z" часовой пояс "Азия/Калькутта"
Мой сервер/БД (оракул) работает в одном и том же часовом поясе (IST) "Азия/Калькутта"

Получить объект Date в этом конкретном часовом поясе

        String date = "20121225 10:00:00 Z";
        String timeZoneId = "Asia/Calcutta";
        TimeZone timeZone = TimeZone.getTimeZone(timeZoneId);

        DateFormat dateFormatLocal = new SimpleDateFormat("yyyyMMdd HH:mm:ss z");
                    //This date object is given time and given timezone
        java.util.Date parsedDate = dateFormatLocal.parse(date + " "  
                         + timeZone.getDisplayName(false, TimeZone.SHORT));

        if (timeZone.inDaylightTime(parsedDate)) {
            // We need to re-parse because we don't know if the date
            // is DST until it is parsed...
            parsedDate = dateFormatLocal.parse(date + " "
                    + timeZone.getDisplayName(true, TimeZone.SHORT));
        }

       //assigning to the java.sql.TimeStamp instace variable
        obj.setTsSchedStartTime(new java.sql.Timestamp(parsedDate.getTime()));

Сохранить в БД

        if (tsSchedStartTime != null) {
            stmt.setTimestamp(11, tsSchedStartTime);
        } else {
            stmt.setNull(11, java.sql.Types.DATE);
        }

OUTPUT

DB (oracle) сохранил тот же заданный dateTime: "20121225 10:00:00 не в UTC.

Я подтвердил из ниже sql.

     select to_char(sched_start_time, 'yyyy/mm/dd hh24:mi:ss') from myTable

Мой сервер БД также работает в том же часовом поясе "Азия/Калькутта"

Он дает мне следующие проявления

  • Date.getTime() не находится в UTC
  • Или Timestamp имеет влияние часовой пояс при сохранении в DB Что я здесь делаю неправильно?

Еще один вопрос:

Будет ли печать timeStamp.toString() в локальном часовом поясе, например java.util.date? Не UTC?

  • 3
    Чтобы ответить на вопрос в вашем заголовке, нет, java.sql.Timestamp основан на UTC. Отображение метки времени зависит от часового пояса.
  • 0
    @ Гилберт Ле Блан Спасибо! Не могли бы вы ответить мне, что "новый java.sql.Timestamp (parsedDate.getTime ())" вернет объект отметки времени GMT для моего ввода?
Показать ещё 3 комментария
Теги:
datetime
date
jdbc

4 ответа

75
Лучший ответ

Хотя это явно не указано для setTimestamp(int parameterIndex, Timestamp x), драйверы должны следовать правилам, установленным setTimestamp(int parameterIndex, Timestamp x, Calendar cal) javadoc:

Устанавливает назначенный параметр в заданное значение java.sql.Timestamp, используя данный объект Calendar. Драйвер использует объект Calendar для построения значения SQL TIMESTAMP, которое затем отправляет драйвер в базу данных. С помощью объекта Calendar драйвер может вычислять временную метку с учетом пользовательского часового пояса. Если объект Calendar не указан, драйвер использует часовой пояс по умолчанию, который используется для виртуальной машины, на которой запущено приложение.

При вызове с помощью setTimestamp(int parameterIndex, Timestamp x) драйвер JDBC использует часовой пояс виртуальной машины для расчета даты и времени временной метки в этом часовом поясе. Эта дата и время - это то, что хранится в базе данных, и если столбец базы данных не хранит информацию о часовом поясе, тогда любая информация о зоне теряется (что означает, что она зависит от приложения (ов), использующего базу данных, чтобы использовать в той же временной зоне последовательно или придумать другую схему для определения временной зоны (т.е. сохранить в отдельной колонке).

Например: локальный часовой пояс GMT + 2. Вы храните "2012-12-25 10:00:00 UTC". Фактическое значение, хранящееся в базе данных, - "2012-12-25 12:00:00". Вы возвращаете его снова: вы возвращаете его снова как "2012-12-25 10:00:00 UTC" (но только если вы его извлекаете с помощью getTimestamp(..)), но когда другое приложение обращается к базе данных в часовом поясе GMT ​​+ 0, он будет получать временную метку как "2012-12-25 12:00:00 UTC".

Если вы хотите сохранить его в другом часовом поясе, вам нужно использовать setTimestamp(int parameterIndex, Timestamp x, Calendar cal) с экземпляром Calendar в требуемом часовом поясе. Просто убедитесь, что вы также используете эквивалентный геттер с тем же часовым поясом при получении значений (если вы используете TIMESTAMP без информации о часовом поясе в своей базе данных).

Итак, если вы хотите сохранить фактический часовой пояс GMT, вам нужно использовать:

Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
stmt.setTimestamp(11, tsSchedStartTime, cal);

С JDBC 4.2 совместимый драйвер должен поддерживать java.time.LocalDateTimejava.time.LocalTime) для TIMESTAMPTIME) через get/set/updateObject. Классы java.time.Local* не имеют временных зон, поэтому преобразование не требуется (хотя это может открыть новый набор проблем, если ваш код действительно принял определенный часовой пояс).

  • 0
    Я хочу хранить свое время в GMT. Я считаю, что моя отметка времени в GMT. И что же мне делать? это setTimestamp (int параметрIndex, отметка времени tzGmtObj, календарь calGmtObj) ??
  • 0
    @KanagaveluSugumar посмотреть мое обновление
Показать ещё 6 комментариев
21

Я думаю, что правильным ответом должен быть java.sql.Timestamp не является конкретным часовым поясом. Временная метка представляет собой составную часть java.util.Date и отдельную наносекунду. В этом классе нет информации о часовом поясе. Таким образом, так же, как Date, этот класс просто содержит миллисекунды с 1 января 1970 года, 00:00:00 GMT + nanos.

В PreparedStatement.setTimestamp(int parameterIndex, Timestamp x, Calendar cal) Календарь используется драйвером для изменения часовой пояс по умолчанию. Но Timestamp по-прежнему занимает миллисекунды в GMT.

API

неясно, как именно JDBC-драйвер должен использовать Calendar. Провайдеры, похоже, не знают, как их интерпретировать, например. последний раз, когда я работал с MySQL 5.5 Calendar, драйвер просто игнорировал Calendar как в PreparedStatement.setTimestamp, так и в ResultSet.getTimestamp.

  • 0
    Интересует комментарий о MySQL 5.5
5

Он специфичен для вашего драйвера. Вам нужно указать параметр в вашей программе Java, чтобы указать часовой пояс, который вы хотите использовать.

java -Duser.timezone="America/New_York" GetCurrentDateTimeZone

Далее:

to_char(new_time(sched_start_time, 'CURRENT_TIMEZONE', 'NEW_TIMEZONE'), 'MM/DD/YY HH:MI AM')

Может также иметь значение при правильном обращении с преобразованием. Взято из здесь

  • 0
    Вы хотели, чтобы я следовал stackoverflow.com/questions/2858182/… или Вы хотите настроить мое приложение JVM для работы в GMT?
  • 0
    @KanagaveluSugumar хорошо, что зависит. Если вы хотите изменить временную метку для КАЖДОГО запроса, установите ее при запуске, если вы хотите изменить только НЕКОТОРЫЕ (что означает что-то меньшее, чем КАЖДЫЙ), то измените каждый запрос в соответствии с тем, как изложено в сообщении, на которое вы ссылались.
4

Для Mysql мы имеем ограничение. В драйвер Mysql doc:

Ниже перечислены некоторые известные проблемы и ограничения для MySQL Разъем /J: Когда соединитель /J извлекает временные метки для дневного света (DST), используя метод getTimeStamp() на некоторые из возвращаемых значений могут быть неправильными. Ошибки могут избегайте использования следующих параметров подключения при подключении к базе данных:

useTimezone=true
useLegacyDatetimeCode=false
serverTimezone=UTC

Итак, когда мы не используем эти параметры, и мы вызываем setTimestamp or getTimestamp с календарем или без календаря, у нас есть временная метка в часовом поясе jvm.

Пример:

Часовой пояс jvm - GMT + 2. В базе данных есть временная метка: 1461100256 = 19/04/16 21:10: 56,000000000 GMT

Properties props = new Properties();
props.setProperty("user", "root");
props.setProperty("password", "");
props.setProperty("useTimezone", "true");
props.setProperty("useLegacyDatetimeCode", "false");
props.setProperty("serverTimezone", "UTC");
Connection con = DriverManager.getConnection(conString, props);
......
Calendar nowGMT = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
Calendar nowGMTPlus4 = Calendar.getInstance(TimeZone.getTimeZone("GMT+4"));
......
rs.getTimestamp("timestampColumn");//Oracle driver convert date to jvm timezone and Mysql convert date to GMT (specified in the parameter)
rs.getTimestamp("timestampColumn", nowGMT);//convert date to GMT 
rs.getTimestamp("timestampColumn", nowGMTPlus4);//convert date to GMT+4 timezone

Первый метод возвращает: 1461100256000 = 19/04/2016 - 21:10:56 GMT

Второй метод возвращает: 1461100256000 = 19/04/2016 - 21:10:56 GMT

Третий метод возвращает: 1461085856000 = 19/04/2016 - 17:10:56 GMT

Вместо Oracle, когда мы используем одни и те же вызовы, мы имеем:

Первый метод возвращает: 1461093056000 = 19/04/2016 - 19:10:56 GMT

Второй метод возвращает: 1461100256000 = 19/04/2016 - 21:10:56 GMT

Третий метод возвращает: 1461085856000 = 19/04/2016 - 17:10:56 GMT

Примечание: Нет необходимости указывать параметры для Oracle.

  • 1
    Я думаю, вы могли бы для оракула установить часовой пояс сеанса в UTC, чтобы добиться того же преобразования, что и MySQL для случая, не относящегося к календарю. " ALTER SESSION SET TIME_ZONE = 'UTC' ".

Ещё вопросы

Сообщество Overcoder
Наверх
Меню