Я передаю хэш-карту, которая содержит дату как
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, я получаю на один час меньше.
Включив часовой пояс в дату и логику кода
Мне нужно получить именно «2019-03-08 00:00:00.0» на стороне сервлета вместо «2019-03-07 23:00:00.0».
Похоже, вы используете класс Timestamp? Этот класс плохо разработан и играет с вами такие трюки. К счастью, он также давно устарел. Используйте класс из java.time, современный API даты и времени Java для предсказуемого поведения.
Почему вы используете классы и методы, которые устарели, начиная с Java 1.2?
Вы действительно нашли старый способ делать вещи. Date тоже давно устарел, и, как сказал @Lothar, методы, которые вы вызываете, устарели именно потому, что они не работают надежно в разных часовых поясах, как вы испытали. Никто из моих знакомых больше не использует Vector. Даже сериализация находится на пути к устареванию.
В каком часовом поясе вы находитесь, где летнее время (DST) действует 8 марта?
Справедливости ради следует отметить, что класс Date сам по себе не так уж и плох, если вы знаете, что это не что иное, как обертка вокруг эпохи и что в лучшем случае он имеет миллисекундное разрешение. Хотя с этим можно работать. Я думаю, что проблема здесь больше связана с недостаточным пониманием даты и времени в программировании, возможно, включая то, что можно ожидать от toString.
@Ole Да, это действует 8 марта, и для всех, поскольку наш проект является устаревшим, мы должны использовать этот устаревший код. Есть ли способ справиться с этой проблемой?
Привет, Оле, я столкнулся с проблемой, когда мой сервлет выполняется в часовом поясе «США/Восточный». Если мой сервлет выполняется в часовом поясе «Азия/Калькутта», у меня не возникло никаких проблем. Я использую Timestamp в своем сервлете. Мне действительно нужны и время, и дата. Оставляя в стороне мой код, изменение даты полностью происходит только при чтении его с конца сервера. Например: если мы передаем дату со стороны клиента на сторону сервера, дата меняется. Здесь связь от клиента к серверу происходит через потоки... Извините за задержку ответа.




Вероятным объяснением того, как это может произойти, является то, что настройка часового пояса сервера не учитывает летнее время (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 в поток объектов на стороне клиента и, конечно же, прочитайте тот же тип на стороне сервера. Тогда не будет места сюрпризам.
обрабатывать что именно?