Что не так с Java Date & Time API?
Очень часто я сталкиваюсь с негативными отзывами о Java Date
и других классах, связанных с датой и временем. Будучи разработчиком .NET, я не могу полностью (не используя их) понять, что с ними на самом деле не так.
Кто-нибудь может пролить некоторый свет на это?
Переведено автоматически
Ответ 1
Ах, класс Java Date
. Пожалуй, один из лучших примеров того, как ничего не делать на любом языке и в любом месте. С чего мне начать?
Чтение JavaDoc может навести на мысль, что у разработчиков действительно есть несколько хороших идей. Здесь подробно рассказывается о разнице между UTC и GMT, несмотря на тот факт, что разница между ними в основном заключается в високосных секундах (что случается довольно редко).
Однако дизайнерские решения действительно сводят на нет всякую мысль о том, что это хорошо спроектированный API. Вот некоторые из любимых ошибок:
- Несмотря на то, что он был разработан в последнее десятилетие тысячелетия, с 1900 года годы в нем исчисляются двузначными числами. В мире Java в результате этого банального решения буквально существуют миллионы обходных решений, выполняющих 1900+ (или 1900-).
- Месяцы индексируются нулем, чтобы учесть поразительно необычный случай наличия массива месяцев и отсутствия работы с массивом из тринадцати элементов, первый из которых содержит
null
. В результате мы имеем 0 ..11 (и сегодня 11 месяц 109 года). Аналогичное количество ++ и -- в месяцах для преобразования в строку. - Они изменяемы. В результате каждый раз, когда вы хотите вернуть дату обратно (скажем, в виде структуры экземпляра), вам нужно возвращать клон этой даты вместо самого объекта date (поскольку в противном случае люди могут изменить вашу структуру).
Calendar
, предназначенный для "исправления" этого, на самом деле допускает те же ошибки. Они по-прежнему изменяемы.Date
представляет собойDateTime
, но для того, чтобы использовать те, что есть в SQL land, существует другой подклассjava.sql.Date
, который представляет один день (хотя и без связанного с ним часового пояса).- Нет
TimeZone
s, связанных с aDate
, и поэтому диапазоны (например, "целый день") часто представляются как полночь-midnight (часто в каком-то произвольном часовом поясе)
Наконец, стоит отметить, что високосные секунды обычно корректируются по сравнению с хорошими системными часами, которые обновляются с помощью ntp в течение часа (см. Ссылки выше). Вероятность того, что система все еще будет запущена при введении двух високосных секунд (минимум каждые шесть месяцев, практически каждые несколько лет), довольно маловероятна, особенно учитывая тот факт, что вам приходится время от времени повторно развертывать новые версии вашего кода. Даже использование динамического языка, который регенерирует классы, или чего-то вроде WAR engine загрязнит пространство классов и в конечном итоге исчерпает permgen.
Ответ 2
JSR 310, который заменил старые классы даты и времени на java.time в Java 8, оправдывает себя в оригинальном JSR следующим образом:
2.5 Какие потребности сообщества Java будут удовлетворены с помощью предлагаемой спецификации?
В настоящее время Java SE имеет два отдельных API для даты и времени - java.util.Date и java.util.Календарь. Оба API последовательно описываются разработчиками Java как сложные в использовании в блогах и форумах. Примечательно, что оба используют нулевой индекс для месяцев, что является причиной многих ошибок. Календарь также страдал от множества ошибок и проблем с производительностью на протяжении многих лет, в первую очередь из-за внутреннего хранения его состояния двумя разными способами.
Одна классическая ошибка (4639407) не позволила создать определенные даты в объекте Calendar. Может быть написана последовательность кода, которая может создавать дату в одни годы, но не в другие, что приведет к тому, что некоторые пользователи не смогут ввести правильные даты своего рождения. Это было вызвано тем, что класс Calendar допускал переход на летнее время только летом на один час, тогда как исторически во время второй мировой войны оно составляло плюс 2 часа. Хотя эта ошибка сейчас исправлена, если в какой-то момент в будущем страна решит ввести переход на летнее время, увеличивающийся на плюс три часа летом, то класс Calendar снова будет нарушен.
Текущий Java SE API также страдает в многопоточных средах. Известно, что неизменяемые классы по своей сути потокобезопасны, поскольку их состояние не может измениться. Однако и Дата, и календарь изменяемы, что требует от программистов явного рассмотрения возможности клонирования и многопоточности. Кроме того, отсутствие потокобезопасности в DateTimeFormat не широко известно, и это было причиной многих трудноотслеживаемых проблем с потоковой обработкой.
Помимо проблем с классами, которые есть в Java SE для datetime, в нем нет классов для моделирования других концепций. Даты или времена, длительности, периоды и интервалы вне часовых поясов не имеют классового представления в Java SE. В результате разработчики часто используют int для представления продолжительности времени, при этом javadoc указывает единицу измерения.
Отсутствие всеобъемлющей модели даты и времени также приводит к тому, что многие обычные операции становятся сложнее, чем они должны быть. Например, вычисление количества дней между двумя датами в настоящее время является особенно сложной проблемой.
Этот JSR решит проблему полной модели даты и времени, включая даты и время (с часовыми поясами и без них), длительности и периоды времени, интервалы, форматирование и синтаксический анализ.
Ответ 3
- Экземпляры Date изменяемы, что почти всегда неудобно.
- Они имеют двойную природу. Они представляют как временную метку, так и календарную дату. Оказывается, это проблематично при выполнении вычислений по датам.
- Числовые представления данных календаря во многих случаях нелогичны. Например:
getMonth()
основано на нуле,getYear()
основано на 1900 (т. Е. 2009 год представлен как 109). - В них отсутствует большая функциональность, которую вы ожидаете от
Date
класса.
Ответ 4
Я сочувствую вам ... как бывший программист .NET, я задавал те же вопросы, time API в .NET (временные интервалы, перегрузка оператора) очень удобен.
Во-первых, для создания определенной даты вы используете либо устаревший API, либо:
Calendar c = Calendar.getInstance();
c.set(2000, 31, 12)
Чтобы вычесть день, вы делаете злые вещи, такие как
Date firstDate = ...
Calendar c = Calendar.getInstance();
c.setTime(fistDate);
c.add(Calendar.DATE,-1);
Date dayAgo = c.getTime();
или хуже
Date d = new Date();
Date d2 = new Date(d.getTime() - 1000*60*60*24);
Чтобы узнать, сколько времени прошло между двумя датами (в днях / неделях / месяцах)... становится еще хуже.
Однако DateUtils от apache (org.apache.commons.lang.time.DateUtils
) предлагают несколько удобных методов, и в последнее время я обнаружил, что использую только их
Как писал Брэбстер, Joda Time также является хорошей внешней библиотекой, но apache кажется более "распространенным", чем что-либо другое...