Как обрабатывать летнее время в Java с помощью ObjectOutputStream и ObjectInputStream?

Я передаю хэш-карту, которая содержит дату как

2019-03-08 00:00:00.0

к сервлету, и при чтении хэш-карты на стороне сервлета я получаю дату как

2019-03-07 23:00:00.0

Его преобразование на один час назад только в период летнего времени. Как я могу справиться с этим в java?

java.util.Date d = new java.util.Date();
d.setDate(06);
d.setMonth(02);
d.setYear(2018);
d.setHours(23);
d.setMinutes(59);
d.setSeconds(00);

ObjectInputStream is = new ObjectInputStream(request.getInputStream());
java.util.Date obj=(java.util.Date)is.readObject();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(response.getOutputStream());
Vector vector = new Vector();
vector.add(d);
objectOutputStream.writeObject(vector);
objectOutputStream.flush();
objectOutputStream.close();

Читая его из класса Java, я получаю на один час меньше.

обрабатывать что именно?

Eugene 12.03.2019 12:37

Включив часовой пояс в дату и логику кода

Joakim Danielson 12.03.2019 12:49

Мне нужно получить именно «2019-03-08 00:00:00.0» на стороне сервлета вместо «2019-03-07 23:00:00.0».

Srinivas 12.03.2019 12:50

Похоже, вы используете класс Timestamp? Этот класс плохо разработан и играет с вами такие трюки. К счастью, он также давно устарел. Используйте класс из java.time, современный API даты и времени Java для предсказуемого поведения.

Ole V.V. 12.03.2019 12:52

Почему вы используете классы и методы, которые устарели, начиная с Java 1.2?

Lothar 12.03.2019 12:57

Вы действительно нашли старый способ делать вещи. Date тоже давно устарел, и, как сказал @Lothar, методы, которые вы вызываете, устарели именно потому, что они не работают надежно в разных часовых поясах, как вы испытали. Никто из моих знакомых больше не использует Vector. Даже сериализация находится на пути к устареванию.

Ole V.V. 12.03.2019 12:59

В каком часовом поясе вы находитесь, где летнее время (DST) действует 8 марта?

Ole V.V. 12.03.2019 13:28

Справедливости ради следует отметить, что класс Date сам по себе не так уж и плох, если вы знаете, что это не что иное, как обертка вокруг эпохи и что в лучшем случае он имеет миллисекундное разрешение. Хотя с этим можно работать. Я думаю, что проблема здесь больше связана с недостаточным пониманием даты и времени в программировании, возможно, включая то, что можно ожидать от toString.

Sebastiaan van den Broek 12.03.2019 13:56

@Ole Да, это действует 8 марта, и для всех, поскольку наш проект является устаревшим, мы должны использовать этот устаревший код. Есть ли способ справиться с этой проблемой?

Srinivas 12.03.2019 14:45

Привет, Оле, я столкнулся с проблемой, когда мой сервлет выполняется в часовом поясе «США/Восточный». Если мой сервлет выполняется в часовом поясе «Азия/Калькутта», у меня не возникло никаких проблем. Я использую Timestamp в своем сервлете. Мне действительно нужны и время, и дата. Оставляя в стороне мой код, изменение даты полностью происходит только при чтении его с конца сервера. Например: если мы передаем дату со стороны клиента на сторону сервера, дата меняется. Здесь связь от клиента к серверу происходит через потоки... Извините за задержку ответа.

Srinivas 13.03.2019 03:57
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
10
94
1

Ответы 1

Вероятным объяснением того, как это может произойти, является то, что настройка часового пояса сервера не учитывает летнее время (DST). В качестве примера я беру часовой пояс Америка/Асунсьон. Асунсьон — крупнейший город и столица Парагвая. Парагвай находится со смещением -04:00 в стандартное время и в -03:00 летом. Летнее время действовало 8 марта и будет действовать до 24 марта.

Сначала демонстрация того, что идет не так:

    System.setProperty("user.timezone", ZoneId.ofOffset("GMT", ZoneOffset.ofHours(-4)).getId());

    // Send a timestamp from America/Asuncion time zone
    ZoneId zone = ZoneId.of("America/Asuncion");
    // Create the timestamp as March 8 at 00:00:00
    Instant testTime
            = ZonedDateTime.of(2019, 3, 8, 0, 0, 0, 0, zone).toInstant();
    Timestamp ts = Timestamp.from(testTime);

    try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
        try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(baos)) {
            objectOutputStream.writeObject(ts);
        }
        byte[] ba = baos.toByteArray();
        try (ObjectInputStream is = new ObjectInputStream(new ByteArrayInputStream(ba))) {
            java.util.Date obj = (java.util.Date) is.readObject();
            System.out.println(obj);
        }
    }

Для этой простой демонстрации нам не нужен отдельный клиентский и серверный код. Я создаю объект Timestamp с известной датой и временем в соответствующем часовом поясе, записываю его в ObjectOutputStream и считываю обратно из ObjectInputStream, используя те же базовые байты. В ответ я получаю Timestamp, представляющий тот же момент времени. Чтобы продемонстрировать, что происходит, когда получатель распечатывает его, я установил часовой пояс по умолчанию прямо вверху. Я использую постоянное смещение -04:00, чтобы продемонстрировать, что происходит, когда часовой пояс сервера не учитывает летнее время. Он печатает:

2019-03-07 23:00:00.0

Так что тот же результат, что вы получили. Это связано с тем, что — как ни странно — Timestamp.toString использует настройку часового пояса JVM для генерации строки.

Как исправить?

Когда мы знаем, что Timestamp был отправлен из часового пояса Америки/Асунсьона, мы можем явно использовать этот часовой пояс на сервере:

            java.util.Date obj = (java.util.Date) is.readObject();

            ZonedDateTime timeAsSent = obj.toInstant().atZone(zone);
            System.out.println(timeAsSent);

2019-03-08T00:00-03:00[America/Asuncion]

Теперь время совпадает с тем, что было изначально записано в поток вывода объекта.

Лучшее исправление

Если есть возможность, полностью избегайте старых классов Date и Timestamp. Эти классы плохо разработаны и играют с вами такие шутки. Они тоже давно устарели. Вместо этого напишите ZonedDateTime или Instant в поток объектов на стороне клиента и, конечно же, прочитайте тот же тип на стороне сервера. Тогда не будет места сюрпризам.

Другие вопросы по теме