[Перевод] Способы сравнения объектов дат в Java


Аннотация от переводчика

Статья вкратце затрагивает трудности работы с датами в Яве на простом примере сравнения дат. Рассчитана для начинающих программистов которые уже более менее знакомых с работой с датами в Java.

Итак, давайте обрисуем проблему — сравнение объектов Date в Java известный источник ошибок.
Это появляется в стандартном коде, но также бывает и в тестирующем коде, где нам регулярно нужно создавать объекты Date которые отмечают определенный момент времени на который потом будем ссылаться в сравнении.

Старый добрый устаревший и не рекомендуемый путь

В тестирующем коде я не переживал что использую устаревшие (deprecated) методы. Поэтому я использовал старый конструктор Date чтобы инициализировать даты, после чего я сравнивал их с другим объектами дат через метод сравнения equals():

Date date = new Date(112, 5, 3);
Date userCreatedDate = user.getCreatedDate();
if (userCreatedDate.equals(date)) { // Если даты равны…
  // делаем что нибудь…
}

Преимущества: это лаконично. Недостатки: это весьма не очевидно и вам нужны хорошие знания Java API чтобы знать что первый параметр это год минус 1900 и второй это индекс месяца который начинается с ноля для Января. И это сюрприз когда вы узнаёте что последний параметр это просто… просто день месяца.

Канонический способ

Начиная с Java 1.1 в Java API был добавлен класс Calendar чтобы разделить момент во времени (т.е. дату) от её представления в специфическом справочнике (календарь). Следующий фрагмент кода (snippet) это способ получения такого же результата как выше.

Calendar calendar = Calendar.getInstance();
calendar.set(YEAR, 2012);
calendar.set(MONTH, JUNE);
calendar.set(DAY_OF_MONTH, 3);

Это не только более многословно, тут также есть ошибка: часы, минут и остальное не равно нолю (берётся от момента непосредственного создания календаря), поэтому сравнение через equals() будет возвращать false. Вот правильный код:

Calendar calendar = Calendar.getInstance();
calendar.set(YEAR, 2012);
calendar.set(MONTH, JUNE);
calendar.set(DAY_OF_MONTH, 3);
calendar.set(HOUR_OF_DAY, 0);
calendar.set(MINUTE, 0);
calendar.set(SECOND, 0);
calendar.set(MILLISECOND, 0);

По меньшей мере это ухудшает краткость 😉

Apache Commons Lang

Apache Commons изначально предоставляет различные утилитные библиотеки которые облегчают разработку в Java. Одна из таких библиотек Apache Commons Lang которая предоставляет функционал который заслуживает быть частью Java API. В нашем случае класс DateUtils позволит нам сократить код сохранив при этом его читаемость:

Calendar calendar = Calendar.getInstance();
calendar.set(YEAR, 2012);
calendar.set(MONTH, JUNE);
calendar.set(DAY_OF_MONTH, 3);
calendar = DateUtils.truncate(calendar, DAY_OF_MONTH); 

Даже лучше, DateUtils позволяет нам работать непосредственно с объектами Date в таком альтернативном виде:

Date date = new Date();
date = DateUtils.setYears(date, 2012);
date = DateUtils.setMonths(date, JUNE);
date = DateUtils.setDays(date, 3);
date = DateUtils.truncate(date, DAY_OF_MONTH);

Обратите внимание что он оставляет параметры нетронутыми, достигая неизменяемости (immutability) по принципа функционального программирования.
Преимущества: мы используем стандартное Java API.
Недостатки: да никаких. И ещё, не будет ли полностью своеобразный DSL казаться чем-то более подходящим?

Joda Time

Последний вариант это использование библиотеки Joda Time, которая нацелена стать заменой для Date и Calendar. Она также породила JSR-310 — новый и улучшенный API для манипуляции с датой и временем, который должен стать частью Java 8 (он был изначально запланирован для Java 7). Joda Time стоит посвятить отдельную статью (или даже мини-руководство). Для наших текущих нужд следующий фрагмент кода может выгодно заменить наш изначальный:

DateMidnight dm = new DateMidnight(2012, 6, 3);

Если сравнивать с первым примером, такой код кажется чище и лаконичней. И ещё, параметры самоописывающие, нет реальной нужды регулярно проверять JavaDocs чтобы узнать как инициализируется год. Кроме того, семантика имён классов ясна. И наконец, метод toDate() даёт нам мост к стандартному Java API.

Заключение

Вывод делайте сами. Лично я обычно использую Apache Commons Lang, но в последнее время склоняюсь к Joda Time. Архив с примерами кода доступен для скачивания тут в виде Maven проекта под Eclipse.

Автор: Nicolas Fränkel
Оригинал Ways of comparing Date Objects in Java.

Реклама

One comment

  1. Уведомление: JodaTime станет частью Java8 | Серёжа Пономарёв aka stokito

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s