SimpleDateFormat, указывающий неправильную дату и время после некоторого времени развертывания - PullRequest
0 голосов
/ 08 февраля 2019

У меня 2 файла В моем коде:

Файл 1 Содержимое:

public static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
public static final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");

Файл 2 Содержимое:

sdf.format(formatter.parse("2015-02-02")));

Проблема: строка выше в файле 2печатает «2015-02-02 12:00:00» первоначально в течение нескольких часов, но после этого печатает «2015-02-01 06:00:00».Любая идея, в чем может быть проблема здесь.

Дополнительная информация: Мой сервер работает на некоторой облачной машине, расположенной в США.new java.util.Date( ) правильно отображает значение часового пояса UTC.Сервер запускается с помощью команды java -jar xyz.jar.Есть и другие файлы, которые используют переменные sdf и formatter.Я не могу воспроизвести это на локальной машине.Как только проблема начинает возникать на серверах, она показывает неправильное время до перезагрузки сервера.

Ответы [ 3 ]

0 голосов
/ 10 февраля 2019

Часовой пояс был установлен для sdf с помощью некоторого куска кода в другом API, который вызывал проблему. Вот пример примера для локальной репликации проблемы:

import java.text.SimpleDateFormat;
import java.util.TimeZone;

public class SimpleDateFormatTExample {

private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
private static SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
private static String timeZone = "CST";

public static void main(String[] args) {


    //-Duser.timezone=UTC


    try {
        String dateTimeString1 = sdf.format(formatter.parse("2018-01-01"));
        System.out.println("Thread Main->> " + dateTimeString1);
//output : Thread Main->> 2018-01-01 12:00:00

    } catch (Exception e) {
        e.printStackTrace();
    }


    new Thread(() -> {
        try {
            //timezone is changed by another thread
            sdf.setTimeZone(TimeZone.getTimeZone(timeZone));
            String dateTimeString = sdf.format(formatter.parse("2018-01-01"));
            System.out.println("Thread child->> " + dateTimeString);
//output : Thread child->> 2017-12-31 06:00:00
        } catch (Exception e) {
            e.printStackTrace();
        }
    }).start();


    try {
        Thread.sleep(1000);
        String dateTimeString1 = sdf.format(formatter.parse("2018-02-15"));
        System.out.println("Thread Main:After timezone changes by another thread->> " + dateTimeString1);
//output : Thread Main:After timezone changes by another thread->> 2018-02-14 06:00:00

    } catch (Exception e) {
        e.printStackTrace();
    }


}

}

0 голосов
/ 10 февраля 2019

Комментарии Натана Хьюза и меня достаточно хороши, чтобы их можно было объединить в ответ: используйте java.time, современный API даты и времени, в частности его DateTimeFormatter.

public static final DateTimeFormatter printFormatter
        = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");

Теперь ваше форматированиенапример, может выглядеть так:

    String stringToPrint = LocalDate.parse("2015-02-02")
            .atStartOfDay(ZoneOffset.UTC)
            .format(printFormatter);

    System.out.println(stringToPrint);

Это печатает:

2015-02-02 00: 00: 00

В форматекод преобразования Я пользуюсь тем фактом, что ваша исходная строка 2015-02-02 в формате даты соответствует стандарту ISO 8601.LocalDate анализирует этот формат как формат по умолчанию, то есть без какого-либо явного средства форматирования.

Что пошло не так в вашем коде?

Из вашего вопроса может показаться, что есть два вероятных объяснениянаблюдаемое вами поведение:

  1. Один из других классов программы на сервере, использующий два модуля форматирования, устанавливает часовой пояс одного из них, например, Америка / Чикаго.
  2. Два или более потоков используют форматы одновременно, что приводит к тому, что один из них ведет себя некорректно.

Наблюдаемое поведение, ошибка 6 часов, когда после включения он продолжаетдо перезапуска сервера кажется более совместимым с первым объяснением, которое вы также подтвердили в своем собственном ответе, и спасибо за это.

В отличие от SimpleDateFormat современный DateTimeFormatter является поточно-ориентированным, чтопредотвращает любые проблемы с потоками и является неизменным, что не позволяет другим классам изменять форматер.Так что это решает вашу проблему в обоих случаях.

В качестве отступления, я думаю, вы знаете о неправильном использовании строчных букв hh в строке шаблона формата.hh - для часов в пределах AM или PM с 01 по 12, тогда как вам нужно прописные буквы HH для часов дня с 00 до 23 (это относится как к SimpleDateFormat, так и к DateTimeFormatter).

Ссылка: Обучающая программа Oracle: Дата и время , объясняющая, как использовать java.time.

0 голосов
/ 08 февраля 2019

Если вы посмотрите официальную документацию Oracle, там написано, что

Форматы даты не синхронизированы.Рекомендуется создавать отдельные экземпляры формата для каждого потока.Если несколько потоков обращаются к формату одновременно, он должен быть синхронизирован извне.

Глядя на свой код, вы, похоже, повторно используете один и тот же экземпляр в нескольких потоках.Это неверно !!!

Либо поддерживайте пул форматеров, либо синхронизируйте доступ (не рекомендуется), либо вы можете каждый раз создавать новый экземпляр.

...