Неправильная дата получена из Postgres - PullRequest
0 голосов
/ 02 июля 2018

У меня есть поле с именем deadline_date типа timestamp with time zone со значением 2018-05-03 19:00:00-05.

select deadline_date from invoice_details where file_name='BHDHSB.pdf';

     deadline_date      
------------------------
 2018-05-03 19:00:00-05
(1 row)

Но при запросе из моего кода Java он возвращает значение как 2018-05-04 05:30:00+05:30.

Я думаю, что это преобразование даты в соответствии с моим местным часовым поясом.

Есть ли способ получить значение, хранящееся в таблице?

Я использую следующую зависимость maven для Postgres

<dependency>
     <groupId>org.postgresql</groupId>
     <artifactId>postgresql</artifactId>
     <version>9.4.1212</version>
</dependency>

Test.java

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class Test {
    public static Connection connection;
    public void createConnection()
    {
        try {
            if (connection==null) {
                Class.forName("org.postgresql.Driver");
                connection = DriverManager.getConnection("jdbc:postgresql://localhost:5432/abc", "abc", "Welcome");
                connection.setSchema("iiv_location");
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println(e.getClass().getName() + ": " + e.getMessage());
        }
    }

   public static void main(String[] args) {
        try {
            test object = new test();
            object.createConnection();
            Statement statement = connection.createStatement();
            ResultSet resultSet = statement.executeQuery("select iiv_prescan_invoice_details.deadline_date from iiv_prescan_invoice_details");
            while (resultSet.next()) {
                System.out.println(resultSet.getString("deadline_date"));
           }
        } catch (Exception e) {
            e.printStackTrace();
        }
   }
}

Ответы [ 3 ]

0 голосов
/ 02 июля 2018

Не используйте getString() для получения метки времени (фактически: только используйте ее для строк. Не для чисел, не для дат, не для меток времени).

Если вы имеете дело с временными метками, которые поддерживают часовой пояс, вам следует использовать OffsetDateTime в Java.

Драйвер JDBC Postgres поддерживает это напрямую с помощью метода getObject():

OffsetDateTime deadline = resultSet.getObject("deadline_date", OffsetDateTime.class);
0 голосов
/ 03 июля 2018

ТЛ; др

Postgres всегда сохраняет момент в UTC в столбце типа TIMESTAMP WITH TIME ZONE.

  • Заходя в базу данных, настроен на UTC.
  • Ухожу, UTC.

Остерегайтесь инструмента доступа к базе данных : он может изменить полученное значение UTC, применяя настройку зоны / смещения. Это создает иллюзию того, что момент был сохранен в этой зоне / смещении, когда фактически он был сохранен в UTC.

Используйте объекты, а не строки для обмена значениями даты и времени с вашей базой данных.

Используйте только java.time классы, никогда не наследуйте Date / Calendar. Для моментов UTC используйте java.time.Instant класс.

Instant instant = Instant.now() ;
myPreparedStatement.setObject( … , instant ) ;

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

Instant instant = myResultSet.getObject( … , Instant.class ) ;

Умные объекты, а не тупые нити

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

Однако эти другие ответы неправильно рекомендуют неправильные классы для использования в Java.

UTC

Postgres сохраняет моменты только в UTC . Любая информация о часовом поясе или смещении от UTC, сопровождающая ввод в столбец базы данных типа TIMESTAMP WITH TIME ZONE, используется для корректировки значения в UTC. Затем значение UTC сохраняется в базе данных, а исходная зона / смещение забываются.

Аналогично, при извлечении значения из столбца базы данных типа TIMESTAMP WITH TIME ZONE вы всегда получаете значение в UTC.

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

Имейте в виду, что я описываю поведение Postgres здесь. Стандарт SQL определяет типы данных, которые я здесь обсуждаю, но не их поведение. К сожалению, стандарт SQL почти не затрагивает вопросы даты и времени. Таким образом, различные реализации базы данных отличаются по своему поведению. Поведение Postgres с учетом UTC имеет для меня смысл. Но учтите, что если вас волнует исходная зона / смещение значения даты и времени, вы должны хранить их в отдельном столбце.

На стороне Java просто используйте Instant для отправки и извлечения момента из столбца базы данных типа TIMESTAMP WITH TIME ZONE.

Отправка.

Instant instant = Instant.now() ;  // Capture the current moment in UTC.
myPreparedStatement.setObject( … , instant ) ;

Получение.

Instant instant = myResultSet.getObject( … , Instant.class );

Вы можете настроить UTC для определенного часового пояса.

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

Если вы работали в своей бизнес-логике с объектом OffsetDateTime или ZonedDateTime, извлеките Instant, вызвав их метод toInstant.

Instant instant = myZonedDateTime.toInstant() ;  // Extract an `Instant` object, effectively adjusting from some particular time zone to UTC.

незональный

Совершенно другой вид даты-времени - это тот, у которого намеренно отсутствует какая-либо концепция часового пояса или смещения от UTC. Таким образом, этот тип не представляет конкретный момент, является не точкой на временной шкале. Этот тип представляет потенциальных моментов в диапазоне около 26-27 часов (диапазон различных часовых поясов).

Для этого типа даты и времени:

  • В Java используйте LocalDateTime class.
  • В Postgres используйте тип TIMESTAMP WITHOUT TIME ZONE.

Когда входные данные отправляются в столбец этого типа в Postgres, любая сопровождающая информация о зоне / смещении игнорируется. Дата и время суток принимаются как есть и сохраняются непосредственно в базе данных. При получении вы получаете ту же дату и время суток, без настройки на какой-либо конкретный часовой пояс.


О 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?

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

0 голосов
/ 02 июля 2018

Вы можете сделать это в SQL, все, что вам нужно сделать, это изменить свой запрос.

select to_char(iiv_prescan_invoice_details.deadline_date, "yyyy:MM:dd HH24:MI:SS") as deadline_date 
from iiv_prescan_invoice_details

или вы можете сделать это в Java

LocalDateTime dateTime = resultSet.getTimestamp("deadline_date").toLocalDateTime();
System.out.println("Date Time: " + dateTime);
...