Несоответствие часового пояса в mysql и java

у меня есть запрос в mysql, который сравнивает 2 даты, как это

convert_tz(updatedDate,'+05:30','-05:00') < ?

функция convert возвращает значение столбца createddate в US Time. когда я запускаю этот запрос в браузере запросов mysql, как

convert_tz(updatedDate,'+05:30','-05:00') < '2013-04-14 09:30:00'

это дает мне правильные значения, например

product    count
-------    ------
    A        123
    B        7

теперь я устанавливаю это на java, используя PreparedStatement как это

pst.setTimestamp(1, new java.sql.Timestamp(end.getTimeInMillis()));

                rs=pst.executeQuery();
                System.out.println("=====new Open Tickets Query executed=====");
                System.out.println(pst);

последняя строка печатает весь запрос и набор значений is

convert_tz(updatedDate,'+05:30','-05:00') < '2013-04-14 09:30:00'

но это дает мне разные значения такой

product    count
-------    ------
    A        155
    B        19

Итак, я подозревал, что проблема часового пояса я изменил свой код на

end.setTimeZone(TimeZone.getTimeZone("America/New York"));
pst.setTimestamp(1, new java.sql.Timestamp(end.getTimeInMillis()));

                rs=pst.executeQuery();
                System.out.println("=====new Open Tickets Query executed=====");
                System.out.println(pst);

но это все равно дает тот же неверный результат.

дополнительная информация: как я устанавливаю переменную конца календаря

у меня есть веб-приложение, которое дает мне строку даты "2013-04-14 09:30:00"

            DateFormat df1=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
                Calendar end=Calendar.getInstance();
                end.setTime(df1.parse(endString));
                end.set(Calendar.HOUR, 9);
                end.set(Calendar.MINUTE, 30);
                end.set(Calendar.SECOND, 0);

кроме того, для эксперимента я попытался с java.утиль.Дата объект он дает мне правильный результат, следующий код

SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
end.setTime(sdf.parse("2012-10-01 00:00:00"));
pst.setTimestamp(1, new java.sql.Timestamp(end.getTime()));

обновление :- Если я использую устаревший метод ответ правильный

 pst.setTimestamp(1, new java.sql.Timestamp(octDate.get(Calendar.YEAR)-1900,octDate.get(Calendar.MONTH),octDate.get(Calendar.DATE),octDate.get(Calendar.HOUR),octDate.get(Calendar.MINUTE),octDate.get(Calendar.SECOND),0));
pst.setTimestamp(2, new java.sql.Timestamp(end.get(Calendar.YEAR)-1900,end.get(Calendar.MONTH),end.get(Calendar.DATE),end.get(Calendar.HOUR),end.get(Calendar.MINUTE),end.get(Calendar.SECOND),0));

обновление 2:- После предложения первого ответа я сделал это

1) выполняется SELECT NOW() в MySQL и он вернулся '2013-04-22 11:56:08'

2) выполняется

System.out.println(new Date(System.currentTimeMillis()));

выход : Mon Apr 22 11:56:25 IST 2013

означает, что обе системы имеют одинаковый часовой пояс

5 ответов


фон: удивительно распространенным-и большим-заблуждением, разделяемым даже блестящими программистами, является представление о том, что сохраненные метки времени (в вашей базе данных, дате, календаре, метке времени и т. д.) каким-то образом имеют информацию о часовом поясе. у них нет. метка времени (до Java 8, во всяком случае) хранится как количество миллисекунд с полуночи 1 января 1970 UTC. Конец предложения. Единственное, что делает настройка часового пояса, - это предоставить компьютеру достаточно информации для преобразования отметка времени в удобочитаемом формате, и наоборот.

ответ: когда вы подозревали, что это проблема часового пояса,ты был прав. Но код, который вы использовали для проверки этого, также имеет проблему:

end.setTimeZone(TimeZone.getTimeZone("America/New York"));
pst.setTimestamp(1, new java.sql.Timestamp(end.getTimeInMillis()));

это setTimeZone заявление нет эффекта на время, хранящееся в end, потому что время уже установлено. Это имело бы эффект, только если бы вы сохранили время после этого, и только если бы вы использовали один из Методы календаря, которые преобразовали время из читаемого человеком формата (а не setTimeInMillis).

при использовании getTimeInMillis чтобы передать отметку времени в подготовленное заявление, вы получаете отметку времени напрямую. Поскольку вы не конвертируете его в человеческий формат, информация о часовом поясе снова игнорируется.

при попытке

SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
end.setTime(sdf.parse("2012-10-01 00:00:00"));
pst.setTimestamp(1, new java.sql.Timestamp(end.getTime()));

и

pst.setTimestamp(1, new java.sql.Timestamp(octDate.get(Calendar.YEAR)-1900,octDate.get(Calendar.MONTH),octDate.get(Calendar.DATE),octDate.get(Calendar.HOUR),octDate.get(Calendar.MINUTE),octDate.get(Calendar.SECOND),0));
pst.setTimestamp(2, new java.sql.Timestamp(end.get(Calendar.YEAR)-1900,end.get(Calendar.MONTH),end.get(Calendar.DATE),end.get(Calendar.HOUR),end.get(Calendar.MINUTE),end.get(Calendar.SECOND),0));

вещи появляется для работы, потому что теперь вы используете методы которые преобразуют в / из читаемого человеком формата, и поэтому используется указанная информация о часовом поясе. Однако, это лишь прикрывает реальную проблему. Реальная проблема заключается в том, что время было неправильно преобразовано, когда вы проанализировали его из endString. То есть часовой пояс, который endString было выражено в не соответствует часовому поясу, установленному в df1 во время анализа даты.

короткий ответ: перед этой строкой:

end.setTime(df1.parse(endString));

вам нужно кому:

  • выяснить, какой часовой пояс время в endString высказал в.
  • Set df1 и не end в тот же часовой пояс. С df1 это то, что преобразует дату из человеческого формата, это информация о часовом поясе, которая используется.

Ура!


какое значение возвращается из своего TimeZone.getDefault().getID() и из часового пояса MySQL по умолчанию?

BTW вы можете попробовать использовать такую утилиту, как эта, чтобы конвертировать datetime через часовые пояса:

public Calendar convertDateToServerTimeZone(Date dateTime, String timeZone) {
    Calendar userDefinedTime = Calendar.getInstance();
    userDefinedTime.setTime(dateTime);
    if(!TimeZone.getDefault().getID().equalsIgnoreCase(timeZone)) {
    System.out.println        ("original defined time: " + userDefinedTime.getTime().toString() + " on tz:" + timeZone);
    Calendar quartzStartDate = new GregorianCalendar(TimeZone.getTimeZone(timeZone));
    quartzStartDate.set(Calendar.YEAR, userDefinedTime.get(Calendar.YEAR));
    quartzStartDate.set(Calendar.MONTH, userDefinedTime.get(Calendar.MONTH));
    quartzStartDate.set(Calendar.DAY_OF_MONTH, userDefinedTime.get(Calendar.DAY_OF_MONTH));
    quartzStartDate.set(Calendar.HOUR_OF_DAY, userDefinedTime.get(Calendar.HOUR_OF_DAY));
    quartzStartDate.set(Calendar.MINUTE, userDefinedTime.get(Calendar.MINUTE));
    quartzStartDate.set(Calendar.SECOND, userDefinedTime.get(Calendar.SECOND));
    quartzStartDate.set(Calendar.MILLISECOND, userDefinedTime.get(Calendar.MILLISECOND));
    System.out.println("adapted time for " + TimeZone.getDefault().getID() + ": " + quartzStartDate.getTime().toString());
    return quartzStartDate;
    } else {
    return userDefinedTime;
    }
}

Я работал с этой функцией здесь: Java Quartz-планировщик по часовому поясу

надеюсь его помощь.


Не видя значений, которые совпадают во втором запросе, а не в первом, трудно быть абсолютно уверенным. Но следует иметь в виду, что часовой пояс-это не то же самое, что смещение. Пожалуйста, прочитайте "часовой пояс != Смещение " раздел тег часового пояса wiki.

например, вы говорите, что преобразование America/New_York часовой пояс. Эта зона иногда находится в UTC-05: 00 (для Восточного стандартного времени), а иногда в UTC-04:00 (для Восточного дневного света Время.) Вполне возможно, что некоторые из ваших данных собираются из-за смещения -4, действующего в летнее время.

когда вы жестко кодируете смещение -5, вы не принимаете во внимание правила часового пояса. Что объясняет расхождение.


информация о часовом поясе не применяется к датам / времени, если вы не установите useTimeZone=true в URL-адресе подключения. Вы можете использовать getTimestamp() метод также, которые принимают календари в качестве аргумента.


попробуйте.

установить параметр timestamp, как вы устанавливаете сейчас

pst.setTimestamp(1, new java.sql.Timestamp(end.getTimeInMillis()));

.

во время вызова базы данных драйвер JDBC может уже преобразовывать входные значения даты/времени в GMT. Таким образом, значение даты, с которым имеет дело база данных, не будет учитывать часовые пояса входной даты в соображениях, которые могут отличаться от одного клиента к другому.

вместо from_tz as '+05:30'

convert_tz(updatedDate,'+05:30','-05:00')

Set from_tz как '00:00'

convert_tz(updatedDate,'00:00','-05:00')