Для данного объекта Date зафиксируйте его значение, относящееся к часовому поясу GMT.

В моем приложении, работающем на Java 8, у меня есть объект Date. Часовой пояс для этого объекта зависит от местоположения клиента.

В какой-то момент мне нужно преобразовать эту дату в GMT, чтобы ее можно было сравнить со значением, к которому я обращаюсь из БД.

Я пробовал SimpleDateFormat и ZonedDateTime, но есть проблема. Эти API предоставляют мне время по Гринвичу в строковых значениях, что совершенно нормально. Однако, как только я проанализирую его и назначу объекту Date, он вернется к моему местному часовому поясу!

Для примера:

public class TimeZoneDemo {
    public static void main(String[] args) throws ParseException {
        Date istDate = Calendar.getInstance().getTime();

        ZonedDateTime gmtTime = istDate.toInstant().atZone(ZoneId.of("GMT"));
        System.out.println(gmtTime);

        Date gmtDate = Date.from(gmtTime.toInstant());
        System.out.println(gmtDate);
    }
}

В приведенном выше коде gmtTime отображает правильное значение, соответствующее часовому поясу GMT, но gmtDate (объект Date ) выводит значение в местном часовом поясе.

P.S.: Моя конечная цель – получить значение GMT ​​в объекте java.sql.TimeStamp.

Как это могло быть достигнуто?

ОБНОВЛЕНИЕ 1: После просмотра комментариев и ответов я понял, что объект Date просто содержит длинное значение, содержащее миллисекунды. НО я ожидаю, что когда эта строка будет выполнена:

Date gmtDate = Date.from(gmtTime.toInstant());

Какое бы время ни содержал объект gmtTime, мне нужно зафиксировать именно это время в объекте TimeStamp или Date. Мотив состоит в том, чтобы затем иметь возможность сравнивать со значением, сохраненным в базе данных. Я передаю дату в качестве параметра моему SQL-запросу.

Может ли кто-нибудь помочь мне понять, как этого можно достичь?


person Curious Coder    schedule 05.09.2019    source источник
comment
Однако, как только я проанализирую его и назначу объекту Date, он вернется к моему местному часовому поясу! Не совсем. Объекты даты не находятся ни в одном часовом поясе. Они просто представляют мгновения времени. См. codeblog.jonskeet.uk/2017/04/ 23/все о Java-утилите-дата   -  person Jon Skeet    schedule 05.09.2019
comment
Всегда и тем более при использовании Java 8 (или новее) избегайте классов Date и Timestamp. Они плохо спроектированы и давно устарели. Используйте java.time, современный API даты и времени Java.   -  person Ole V.V.    schedule 05.09.2019
comment
сравнение со значением, хранящимся в базе данных — как в равно или до или после? Вы предпочитаете сравнивать в SQL или в Java? Я уверен, что мы можем помочь.   -  person Ole V.V.    schedule 05.09.2019
comment
@OleV.V.: я передаю дату в качестве параметра своему SQL-запросу   -  person Curious Coder    schedule 06.09.2019


Ответы (3)


java.time и JDBC 4.2

Ответ в комментарии @BasilBourque: используйте OffsetDateTime.

    PreparedStatement yourPreparedStatement = yourDatabaseConnection.prepareStatement(
            "select smth from your_table where your_time_stamp_col < ?;");
    OffsetDateTime gmtTime = OffsetDateTime.now(ZoneOffset.UTC);
    yourPreparedStatement.setObject(1, gmtTime);

Для этого требуется драйвер JDBC, совместимый с JDBC 4.2, я думаю, что почти все мы его используем. Это хорошо, потому что позволяет нам обойти java.sql.Timestamp и другие типы даты и времени в java.sql. Все они плохо спроектированы и давно устарели.

Как уже говорили другие, ни один из устаревших классов Date и Timestamp не имеет часового пояса или смещения от UTC/GMT.

Некоторые связанные вопросы

person Ole V.V.    schedule 06.09.2019
comment
Скорее всего, это сработает, только если а) ваш драйвер поддерживает OffsetDateTime и б) вы используете тип данных TIMESTAMP WITH TIME ZONE - person Philippe Marschall; 06.09.2019
comment
@PhilippeMarschall Спасибо, вы правы. Драйвер, совместимый с JDBC 4.2, поддерживает OffsetDateTime по определению. Тип данных базы данных должен быть timestamp with time zone. Если это просто timestamp (без часового пояса), вместо этого нужно использовать LocalDateTime на стороне Java. Все остальное по-прежнему. - person Ole V.V.; 07.09.2019

Прежде всего, класс Date является частью старой устаревшей (без каламбура) инфраструктуры. Если это вообще возможно, избавьтесь от него и просто используйте пакет java.time. Но если вы должны работать с Date, то ваша проблема с часовым поясом не проблема. ваша строка System.out.println(gmtDate); печатает только с вашим местным часовым поясом, так как система предполагает, что это лучший вариант. Но независимо от этого Date содержит определенный момент времени в миллисекундах с 1 января 1970 года, 00:00:00 по Гринвичу. Класс Date имеет методы compareTo(), после() и before(), которые позволяют сравнивать 2 даты. Также Date имеет метод getTime(), который возвращает вам количество миллисекунд с 1 января 1970 года, 00:00:00 по Гринвичу, представленное этой датой. Таким образом, вы можете сравнить значения long. Но опять же, лучше всего переключиться на java.time пакет и классы Instant и ZonedDateTime (и другие) имеют методы compareTo(), isAfter() и isBefore().

person Michael Gantman    schedule 05.09.2019
comment
Спасибо за объяснение, не могли бы вы обратиться к обновленному заголовку и «Обновлению 1» в вопросе выше и помочь мне с некоторыми указателями для его выполнения? - person Curious Coder; 05.09.2019
comment
Я просто отпустил офис и не смогу отвечать сегодня вечером. - person Michael Gantman; 05.09.2019

Вы неправильно понимаете семантику классов даты и времени. java.util.Date — это конкретный момент времени, мгновение, с ним не связан часовой пояс. Однако, если у вас есть часовой пояс, вы можете запросить часовой пояс для времени этого java.util.Date.

java.sql.TimeStamp эквивалентен java.time.LocalDateTime. Это не мгновение, и с ним не связан часовой пояс.

person Philippe Marschall    schedule 05.09.2019
comment
Спасибо за объяснение, не могли бы вы обратиться к обновленному заголовку и «Обновлению 1» в вопросе выше и помочь мне с некоторыми указателями для его выполнения? - person Curious Coder; 05.09.2019
comment
Неверно Date. Класс java.util.Date представляет момент в UTC, определяемый как количество миллисекунд с 1970-01-01T00:00Z, где Z означает UTC (смещение нуля часов-минут-секунд). Без контекста UTC или какой-либо подобной ссылки вы не можете представить момент. - person Basil Bourque; 05.09.2019
comment
Неверно в java.sql.Timestamp. Этот класс на самом деле является подклассом java.util.Date, неуклюже добавляя доли секунды к наносекундам, сохраняя при этом миллисекунды Date. Поскольку Timestamp является числом Date, оно также представляет момент в формате UTC. Это, безусловно, не эквивалентно LocalDateTime, в котором намеренно отсутствуют концепции часового пояса или смещения от UTC. См.: В чем разница между Instant и LocalDateTime? - person Basil Bourque; 05.09.2019
comment
java.sql.Timestamp был заменен на OffsetDateTime в JDBC 4.2 и более поздних версиях (а не LocalDateTime, как неправильно указано в этом ответе). См. мой комментарий на Вопрос например код. - person Basil Bourque; 05.09.2019
comment
@BasilBourque на самом деле нет, java.sql.Timestamp является подклассом, но не подтипом, поэтому не java.util.Date. java.sql.Timestamp, в отличие от java.util.Date, находится в часовом поясе JVM. Вы можете проверить это сами, значение Timestamp.valueOf(2019-09-06 17:47:11).getTime() зависит от часового пояса JVM. Также обратите внимание на методы преобразования java.time.LocalDateTime на java.sql.Timestamp, так как они эквивалентны. - person Philippe Marschall; 06.09.2019
comment
То, что два таких знающих человека могут расходиться во мнениях относительно того, что вообще такое java.sql.Timestamp, должно послать четкий сигнал: Держитесь подальше от этого плохо спроектированного класса. - person Ole V.V.; 08.09.2019
comment
@BasilBourque, что касается java.util.Date и часового пояса UTC, я полагаюсь на Джона Скита codeblog.jonskeet.uk/2017/04/23/all-about-java-util-date - person Philippe Marschall; 08.09.2019