Почему мой 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
- Проект ThreeTenABP адаптирует ThreeTen-Backport (упомянутый выше) специально для Android.
- Смотрите Как использовать ThreeTenABP….
Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к java.time. Вы можете найти некоторые полезные классы здесь, такие как Interval
, YearWeek
, YearQuarter
и многое другое.