1. введение
Joda-Time-это наиболее широко используемая библиотека обработки даты и времени до выпуска Java 8. Его цель состояла в том, чтобы предложить интуитивно понятный API для обработки даты и времени, а также решить проблемы проектирования, которые существовали в API даты и времени Java.
Центральные концепции, реализованные в этой библиотеке, были введены в ядро JDK с выпуском версии Java 8. Новый API даты и времени находится в пакете java.time ( JSR-310 ). Обзор этих функций можно найти в этой статье .
После выпуска Java 8 авторы считают, что проект в основном завершен, и советуют по возможности использовать API Java 8.
2. Зачем использовать Время Джоды?
API даты и времени, существовавший до Java 8, создавал множество проблем с проектированием.
Среди проблем-тот факт, что классы Date и Simpledateformat не являются потокобезопасными. Чтобы решить эту проблему, Joda-Time использует неизменяемые классы для обработки даты и времени.
Класс Date не представляет фактическую дату, но вместо этого он указывает момент времени с точностью до миллисекунды. Год в Дате начинается с 1900 года, в то время как большинство операций с датой обычно используют время эпохи, которое начинается с 1 января 1970 года.
Кроме того, смещение дня, месяца и года Даты противоречит здравому смыслу. Дни начинаются с 0, а месяц начинается с 1. Чтобы получить доступ к любому из них, мы должны использовать класс Calendar . Joda-Time предлагает чистый и свободный API для обработки дат и времени.
Joda-Time также предлагает поддержку восьми календарных систем , в то время как Java предлагает только 2: Gregorian – java.util.GregorianCalendar и японский – java.util.JapaneseImperialCalendar .
3. Настройка
Чтобы включить функциональность библиотеки времени Joda, нам нужно добавить следующую зависимость от Maven Central :
joda-time joda-time 2.10
4. Обзор библиотеки
Joda-Time моделирует концепцию даты и времени с использованием классов в пакете org.joda.time .
Среди этих классов наиболее часто используются:
- LocalDate – представляет дату без времени
- LocalTime – представляет время без часового пояса
- LocalDateTime – представляет как дату, так и время без часового пояса
- Instant – представляет точный момент времени в миллисекундах от эпохи Java 1970-01-01T00:00:00Z
- Длительность – представляет длительность в миллисекундах между 2 точками времени
- Period – аналогично Duration , но позволяет получить доступ к отдельным компонентам объекта даты и времени, таким как годы, месяц, дни и т. Д.
- Интервал – представляет интервал времени между 2 мгновениями
Другими важными функциями являются анализаторы дат и форматтеры . Их можно найти в пакете org.joda.time.format .
Календарная система и часовой пояс конкретные классы можно найти в org.joda.time.chrono и org.joda.time.tz пакеты.
Давайте рассмотрим несколько примеров, в которых мы используем ключевые функции Joda-Time для обработки даты и времени.
5. Представление даты и времени
5.1. Текущая дата и время
Текущая дата без информации о времени может быть получена с помощью метода now() из класса LocalDate |:
LocalDate currentDate = LocalDate.now();
Когда нам нужно только текущее время, без информации о дате, мы можем использовать класс LocalTime :
LocalTime currentTime = LocalTime.now();
Чтобы получить представление текущей даты и времени без учета часового пояса, мы можем использовать LocalDateTime :
LocalDateTime currentDateAndTime = LocalDateTime.now();
Теперь, используя текущую дату и время , мы можем преобразовать его в другие типы объектов, моделирующих дату и время.
Мы можем получить объект DateTime (который учитывает часовой пояс) с помощью метода ToDateTime() . Когда время не нужно , мы можем преобразовать его в LocalDate с помощью метода toLocalDate () , а когда нам нужно только время, мы можем использовать ToLocalTime() для получения LocalTime объекта:
DateTime dateTime = currentDateAndTime.toDateTime(); LocalDate localDate = currentDateAndTime.toLocalDate(); LocalTime localTime = currentDateAndTime.toLocalTime();
Все вышеперечисленные методы имеют перегруженный метод, который принимает DateTimeZone object , чтобы помочь нам представить дату или время в указанном часовом поясе:
LocalDate currentDate = LocalDate.now(DateTimeZone.forID("America/Chicago"));
Кроме того, Joda-Time предлагает отличную интеграцию с API даты и времени Java. Конструкторы принимают java.util.Кроме того, мы можем использовать метод ToDate() для возврата java.util.Дата объект:
LocalDateTime currentDateTimeFromJavaDate = new LocalDateTime(new Date()); Date currentJavaDate = currentDateTimeFromJavaDate.toDate();
5.2. Пользовательская дата и время
Чтобы представить пользовательскую дату и время, Joda-Time предоставляет нам несколько конструкторов. Мы можем указать следующие объекты:
- an Мгновенный
- объект Java Дата
- a Строка представление даты и времени с использованием формата ISO
- части даты и времени: год, месяц, день, час, минута, секунда, миллисекунда
Date oneMinuteAgoDate = new Date(System.currentTimeMillis() - (60 * 1000)); Instant oneMinutesAgoInstant = new Instant(oneMinuteAgoDate); DateTime customDateTimeFromInstant = new DateTime(oneMinutesAgoInstant); DateTime customDateTimeFromJavaDate = new DateTime(oneMinuteAgoDate); DateTime customDateTimeFromString = new DateTime("2018-05-05T10:11:12.123"); DateTime customDateTimeFromParts = new DateTime(2018, 5, 5, 10, 11, 12, 123);
Другой способ, которым мы можем определить пользовательскую дату и время, – это анализ заданного String представления даты и времени в формате ISO:
DateTime parsedDateTime = DateTime.parse("2018-05-05T10:11:12.123");
Мы также можем анализировать пользовательские представления даты и времени, определяя пользовательский DateTimeFormatter :
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern("MM/dd/yyyy HH:mm:ss"); DateTime parsedDateTimeUsingFormatter = DateTime.parse("05/05/2018 10:11:12", dateTimeFormatter);
6. Работа с датой и временем
6.1. Использование Instant
Instant представляет количество миллисекунд с 1970-01-01T00:00:00Z до заданного момента времени. Например, текущий момент времени можно получить с помощью конструктора по умолчанию или метода now() :
Instant instant = new Instant(); Instant.now();
Чтобы создать Instant для пользовательского момента времени, мы можем использовать либо один из конструкторов, либо использовать методы ofEpochMilli() и ofEpochSecond() :
Instant instantFromEpochMilli = Instant.ofEpochMilli(milliesFromEpochTime); Instant instantFromEpocSeconds = Instant.ofEpochSecond(secondsFromEpochTime);
Конструкторы принимают Строку , представляющую дату и время в формате ISO, Java Дату или длинное значение, представляющее количество миллисекунд с 1970-01-01T00:00:00Z:
Instant instantFromString = new Instant("2018-05-05T10:11:12"); Instant instantFromDate = new Instant(oneMinuteAgoDate); Instant instantFromTimestamp = new Instant(System.currentTimeMillis() - (60 * 1000));
Когда дата и время представлены в виде Строки , у нас есть возможность проанализировать Строку , используя желаемый формат:
Instant parsedInstant = Instant.parse("05/05/2018 10:11:12", dateTimeFormatter);
Теперь, когда мы знаем, что представляет собой Instant и как мы можем его создать, давайте посмотрим, как его можно использовать.
Для сравнения с Instant объекты, которые мы можем использовать compareTo () , потому что он реализует интерфейс Comparable , но также мы можем использовать методы API времени Joda, предоставленные в интерфейсе ReadableInstant , который также реализует Instant :
assertTrue(instantNow.compareTo(oneMinuteAgoInstant) > 0); assertTrue(instantNow.isAfter(oneMinuteAgoInstant)); assertTrue(oneMinuteAgoInstant.isBefore(instantNow)); assertTrue(oneMinuteAgoInstant.isBeforeNow()); assertFalse(oneMinuteAgoInstant.isEqual(instantNow));
Еще одна полезная функция заключается в том, что Instant может быть преобразован в DateTime объект или событие Java Date :
DateTime dateTimeFromInstant = instant.toDateTime(); Date javaDateFromInstant = instant.toDate();
Когда нам нужно получить доступ к частям даты и времени, таким как год, час и т. Д., Мы можем использовать метод get() и указать поле DateTimeField :
int year = instant.get(DateTimeFieldType.year()); int month = instant.get(DateTimeFieldType.monthOfYear()); int day = instant.get(DateTimeFieldType.dayOfMonth()); int hour = instant.get(DateTimeFieldType.hourOfDay());
Теперь, когда мы рассмотрели класс Instant , давайте рассмотрим некоторые примеры того, как мы можем использовать Duration , Period и Interval .
6.2. Использование Длительности, Периода и Интервала
A Длительность представляет время в миллисекундах между двумя точками времени, или в этом случае это может быть два Мгновения . Мы будем использовать это, когда нам нужно добавить или вычесть определенное количество времени в или из другого Мгновенного без учета хронологии и часовых поясов :
long currentTimestamp = System.currentTimeMillis(); long oneHourAgo = currentTimestamp - 24*60*1000; Duration duration = new Duration(oneHourAgo, currentTimestamp); Instant.now().plus(duration);
Кроме того, мы можем определить, сколько дней, часов, минут, секунд или миллисекунд представляет продолжительность:
long durationInDays = duration.getStandardDays(); long durationInHours = duration.getStandardHours(); long durationInMinutes = duration.getStandardMinutes(); long durationInSeconds = duration.getStandardSeconds(); long durationInMilli = duration.getMillis();
Основное различие между Периодом и Продолжительностью заключается в том, что Период определяется в терминах его компонентов даты и времени (годы, месяцы, часы и т.д.) и не представляет точное количество миллисекунд . При использовании Периода вычислений даты и времени будет учитываться часовой пояс и переход на летнее время .
Например, добавление Периода 1 месяца к 1 февраля приведет к представлению даты 1 марта. При использовании Period библиотека будет учитывать високосные годы.
Если мы будем использовать Длительность , результат будет неверным, потому что Длительность представляет собой фиксированное количество времени, которое не учитывает хронологию или часовые пояса:
Period period = new Period().withMonths(1); LocalDateTime datePlusPeriod = localDateTime.plus(period);
Интервал , как указано в названии, представляет интервал даты и времени между двумя фиксированными точками времени, представленными двумя Мгновенными объектами:
Interval interval = new Interval(oneMinuteAgoInstant, instantNow);
Класс полезен, когда нам нужно проверить, перекрываются ли два интервала или вычислить промежуток между ними. Метод overlap() вернет перекрывающиеся Интервалы или null , если они не перекрываются:
Instant startInterval1 = new Instant("2018-05-05T09:00:00.000"); Instant endInterval1 = new Instant("2018-05-05T11:00:00.000"); Interval interval1 = new Interval(startInterval1, endInterval1); Instant startInterval2 = new Instant("2018-05-05T10:00:00.000"); Instant endInterval2 = new Instant("2018-05-05T11:00:00.000"); Interval interval2 = new Interval(startInterval2, endInterval2); Interval overlappingInterval = interval1.overlap(interval2);
Разница между интервалами может быть вычислена с помощью метода gap () , и когда мы хотим знать, равен ли конец интервала началу другого интервала, мы можем использовать метод abuts() :
assertTrue(interval1.abuts(new Interval( new Instant("2018-05-05T11:00:00.000"), new Instant("2018-05-05T13:00:00.000"))));
6.3. Операции с датой и временем
Некоторые из наиболее распространенных операций-сложение, вычитание и преобразование даты и времени. Библиотека предоставляет конкретные методы для каждого из классов LocalDate , LocalTime , LocalDateTime и Date Time . Важно отметить, что эти классы являются неизменяемыми, так что каждый вызов метода будет создавать новый объект своего типа.
Давайте возьмем LocalDateTime для текущего момента и попробуем изменить его значение:
LocalDateTime currentLocalDateTime = LocalDateTime.now();
Чтобы добавить дополнительный день в currentLocalDateTime , мы используем метод plus Days() :
LocalDateTime nextDayDateTime = currentLocalDateTime.plusDays(1);
Мы также можем использовать метод plus() для добавления Периода или Продолжительности в наш currentLocalDateTime:
Period oneMonth = new Period().withMonths(1); LocalDateTime nextMonthDateTime = currentLocalDateTime.plus(oneMonth);
Методы аналогичны для других компонентов даты и времени, например, plus Years() для добавления дополнительных лет, plusSeconds() для добавления дополнительных секунд и так далее.
Чтобы вычесть день из нашего currentLocalDateTime , мы можем использовать метод minus Days() :
LocalDateTime previousDayLocalDateTime = currentLocalDateTime.minusDays(1);
Кроме того, выполняя вычисления с датой и временем, мы также можем установить отдельные части даты или времени. Например, установка часа на 10 может быть достигнута с помощью метода withHourOfDay () . Другие методы, начинающиеся с префикса “с” , могут использоваться для установки компонентов этой даты или времени:
LocalDateTime currentDateAtHour10 = currentLocalDateTime .withHourOfDay(0) .withMinuteOfHour(0) .withSecondOfMinute(0) .withMillisOfSecond(0);
Еще одним важным аспектом является то, что мы можем преобразовать тип класса даты и времени в другой. Для этого мы можем использовать специальные методы, предоставляемые библиотекой:
- ToDateTime() – преобразует LocalDateTime в DateTime объект
- toLocalDate() – преобразует LocalDateTime в LocalDate объект
- ToLocalTime() – преобразует LocalDateTime в объект LocalTime
- ToDate() – преобразует LocalDateTime в объект Java Date
7. Работа с часовыми поясами
Joda-Time позволяет нам легко работать с различными часовыми поясами и переключаться между ними. У нас есть абстрактный класс Date TimeZone , который используется для представления всех аспектов, касающихся часового пояса.
Часовой пояс по умолчанию, используемый Joda-Time, выбирается из системного свойства user.timezone Java. API библиотеки позволяет нам указать индивидуально для каждого класса или расчета, какой часовой пояс следует использовать. Например, мы можем создать объект LocalDateTime
Когда мы знаем, что будем использовать определенный часовой пояс во всем приложении, мы можем установить часовой пояс по умолчанию:
DateTimeZone.setDefault(DateTimeZone.UTC);
Отныне все операции с датой и временем, если не указано иное, будут представлены в часовом поясе UTC.
Чтобы увидеть все доступные часовые пояса, мы можем использовать метод getAvailableIDs():
DateTimeZone.getAvailableIDs()
Когда нам нужно представить дату или время в определенном часовом поясе, мы можем использовать любой из классов LocalTime , LocalDate , LocalDateTime , DateTime и указать в конструкторе объект DateTimeZone :
DateTime dateTimeInChicago = new DateTime(DateTimeZone.forID("America/Chicago")); DateTime dateTimeInBucharest = new DateTime(DateTimeZone.forID("Europe/Bucharest")); LocalDateTime localDateTimeInChicago = new LocalDateTime(DateTimeZone.forID("America/Chicago"));
Кроме того, при преобразовании между этими классами мы можем указать нужный часовой пояс. Метод ToDateTime() принимает DateTimeZone объект и to Date() принимает java.util.Объект часового пояса:
DateTime convertedDateTime = localDateTimeInChicago.toDateTime(DateTimeZone.forID("Europe/Bucharest")); Date convertedDate = localDateTimeInChicago.toDate(TimeZone.getTimeZone("Europe/Bucharest"));
8. Заключение
Joda-Time-это фантастическая библиотека, которая началась с главной цели-устранить проблемы в JDK, связанные с операциями даты и времени. Вскоре она стала библиотекой de facto для обработки даты и времени, и недавно основные концепции из нее были введены в Java 8.
Важно отметить, что автор считает это “это будет в значительной степени законченный проект” и рекомендует перенести существующий код для использования реализации Java 8.
Исходный код статьи доступен на GitHub .