Почему мой Java Calendar.setTime() время от времени устанавливает неправильное время?

Используя этот код ниже, я заметил, что иногда дата форматируется неправильно. И чтобы сделать это еще более странным, иногда у timeStamp будет правильная дата, а у timeStampCopy неправильная дата, и наоборот.

   public static Timestamp method(String date, DateFormat dateFormat) throws Exception {           

        // date is always "2017-02-17"     

        // original
        GregorianCalendar gCal = new GregorianCalendar();
        gCal.setTime(dateFormat.parse(date));
        Timestamp timeStamp = new Timestamp(gCal.getTimeInMillis());

        // copy
        GregorianCalendar gCalCopy= new GregorianCalendar();
        gCalCopy.setTime(dateFormat.parse(date));
        Timestamp timeStampCopy = new Timestamp(gCalCopy.getTimeInMillis());

        if (!timeStamp.toString().contains("2017-02-17"))
            System.out.println(timeStamp.toString());   
        if (!timeStampCopy.toString().contains("2017-02-17"))
            System.out.println(timeStampCopy.toString());   

        return timeStamp;

    }

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

Ниже приведены некоторые значения, которые я получаю:

timeStamp is:       2017-02-17 00:00:00.0
timeStampCopy is:   1700-02-17 00:00:00.0

2 ответа

Решение

Вы говорите, что вы разделяете DateFormat экземпляр между потоками.

Согласно Javadoc:

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

Обратите внимание, что это относится к внешней синхронизации доступа к DateFormat экземпляр, а не метод. Делать метод synchronized только исправит эту проблему, если нет другого использования DateFormat пример.

Вы также можете:

  • Явно синхронизировать весь код, используя DateFormat экземпляр (стоит добавить @GuardedBy аннотация к переменной, чтобы задокументировать, что вы ожидаете, что блокировка будет удерживаться перед ее использованием);
  • Измените тип переменной на ThreadLocal<DateFormat> (и соответствующим образом инициализировать переменную общего доступа), которая гарантирует, что каждый поток имеет свою собственную копию DateFormat,

Последний подход имеет меньшую конкуренцию, потому что каждый поток может продолжаться независимо от других. Это также означает, что вы не можете случайно пропустить синхронизацию.

Но есть лучшие библиотеки для обработки дат и времени, которые были разработаны с учетом таких проблем, как DateFormatотсутствие безопасности потоков. В Java 8 есть java.time API; для более ранних версий Java есть Jodatime.

Ответ Тернера является правильным и должен быть принят.

java.time поточно-ориентированный

Классы java.time решают эту проблему, используя неизменяемые объекты и делая их поточно-ориентированными.

LocalDate ld = LocalDate.of( "2017-02-17" );
ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ld.atStartOfDay( z );

Генерация строки в стандартном формате ISO 8601 путем вызова toString, Для других форматов используйте DateTimeFormatter учебный класс. Поиск переполнения стека для многих примеров и обсуждений. Не беспокойтесь о потоках, все потокобезопасны.

Для значения в UTC извлеките Instant,

Instant instant = zdt.toInstant() ;

Не нужно использовать java.sql.Timestamp, Современные драйверы JDBC могут обрабатывать типы java.time с помощью методов toObject и setObject. Для старых драйверов конвертировать, используя новые методы, добавленные к старым классам.


О java.time

Инфраструктура java.time встроена в Java 8 и более поздние версии. Эти классы вытесняют проблемные старые классы даты и времени, такие как java.util.Date, Calendar & SimpleDateFormat,

Проект Joda-Time, находящийся сейчас в режиме обслуживания, рекомендует перейти на классы java.time.

Чтобы узнать больше, смотрите Oracle Tutorial. И поиск переполнения стека для многих примеров и объяснений. Спецификация JSR 310.

Где взять классы java.time?

  • Java SE 8 и SE 9 и позже
    • Встроенный.
    • Часть стандартного Java API со встроенной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и SE 7
    • Большая часть функциональности java.time перенесена на Java 6 и 7 в ThreeTen-Backport.
  • Android

Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к java.time. Вы можете найти некоторые полезные классы здесь, такие как Interval, YearWeek, YearQuarter и многое другое.

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