Как обрабатывать переход на летнее время в Java, используя ObjectOutputStream и ObjectInputStream? - PullRequest
0 голосов
/ 12 марта 2019

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

2019-03-08 00: 00: 00.0

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

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, я получаю на час меньше.

1 Ответ

0 голосов
/ 14 марта 2019

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

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

Лучше исправить

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

...