Перейти к основному содержимому

Руководство по java.util.GregorianCalendar

· 7 мин. чтения

1. Введение

В этом уроке мы кратко рассмотрим класс GregorianCalendar .

2. Григорианский календарь

GregorianCalendar — это конкретная реализация абстрактного класса java.util.Calendar . Неудивительно, что григорианский календарь является наиболее широко используемым гражданским календарем в мире.

2.1. Получение экземпляра

Есть два варианта получения экземпляра GregorianCalendar: Calendar.getInstance() и использование одного из конструкторов.

Использование статического фабричного метода Calendar.getInstance() не является рекомендуемым подходом, поскольку он вернет экземпляр, зависящий от языкового стандарта по умолчанию.

Он может вернуть буддийский календарь для тайского языка или японский имперский календарь для Японии. Незнание типа возвращаемого экземпляра может привести к ClassCastException :

@Test(expected = ClassCastException.class)
public void test_Class_Cast_Exception() {
TimeZone tz = TimeZone.getTimeZone("GMT+9:00");
Locale loc = new Locale("ja", "JP", "JP");
Calendar calendar = Calendar.getInstance(loc);
GregorianCalendar gc = (GregorianCalendar) calendar;
}

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

Давайте разберемся с различными конструкторами, с помощью которых можно создать экземпляр объекта GregorianCalendar .

Конструктор по умолчанию инициализирует календарь текущей датой и временем в часовом поясе и локали операционной системы:

new GregorianCalendar();

Мы можем указать год, месяц, день месяца, час дня, минуту и секунду для часового пояса по умолчанию с локалью по умолчанию:

new GregorianCalendar(2018, 6, 27, 16, 16, 47);

Обратите внимание, что нам не нужно указывать hourOfDay, минуты и секунды , так как есть другие конструкторы без этих параметров.

Мы можем передать часовой пояс в качестве параметра, чтобы создать календарь в этом часовом поясе с локалью по умолчанию:

new GregorianCalendar(TimeZone.getTimeZone("GMT+5:30"));

Мы можем передать локаль в качестве параметра для создания календаря в этой локали с часовым поясом по умолчанию:

new GregorianCalendar(new Locale("en", "IN"));

Наконец, мы можем передать как часовой пояс, так и локаль в качестве параметров:

new GregorianCalendar(TimeZone.getTimeZone("GMT+5:30"), new Locale("en", "IN"));

2.2. Новые методы с Java 8

В Java 8 в GregorianCalendar появились новые методы . ``

Метод from() получает экземпляр GregorianCalendar с локалью по умолчанию из объекта ZonedDateTime.

Используя getCalendarType() , мы можем получить тип экземпляра календаря. Доступные типы календарей: «григорий», «буддийский» и «японский».

Мы можем использовать это, например, чтобы убедиться, что у нас есть календарь определенного типа, прежде чем переходить к логике нашего приложения:

@Test
public void test_Calendar_Return_Type_Valid() {
Calendar calendar = Calendar.getInstance();
assert ("gregory".equals(calendar.getCalendarType()));
}

Вызывая toZonedDateTime() , мы можем преобразовать объект календаря в объект ZonedDateTime , который представляет ту же точку на временной шкале, что и этот GregorianCalendar.

2.3. Изменение дат

Поля календаря можно изменить с помощью методов add() , roll() и set() .

Метод add() позволяет нам добавлять время в календарь в указанной единице на основе внутреннего набора правил календаря:

@Test
public void test_whenAddOneDay_thenMonthIsChanged() {
int finalDay1 = 1;
int finalMonthJul = 6;
GregorianCalendar calendarExpected = new GregorianCalendar(2018, 5, 30);
calendarExpected.add(Calendar.DATE, 1);
System.out.println(calendarExpected.getTime());

assertEquals(calendarExpected.get(Calendar.DATE), finalDay1);
assertEquals(calendarExpected.get(Calendar.MONTH), finalMonthJul);
}

Мы также можем использовать метод add() для вычитания времени из объекта календаря:

@Test
public void test_whenSubtractOneDay_thenMonthIsChanged() {
int finalDay31 = 31;
int finalMonthMay = 4;
GregorianCalendar calendarExpected = new GregorianCalendar(2018, 5, 1);
calendarExpected.add(Calendar.DATE, -1);

assertEquals(calendarExpected.get(Calendar.DATE), finalDay31);
assertEquals(calendarExpected.get(Calendar.MONTH), finalMonthMay);
}

Выполнение метода add() приводит к немедленному пересчету миллисекунд календаря и всех полей.

Обратите внимание, что использование add() может также изменить верхние поля календаря (в данном случае MONTH).

Метод roll() добавляет сумму со знаком в указанное поле календаря без изменения больших полей. Большее поле представляет большую единицу времени. Например, DAY_OF_MONTH больше, чем HOUR.

Давайте посмотрим на пример того, как свернуть месяцы.

В этом случае YEAR , будучи большим полем, не будет увеличиваться:

@Test
public void test_whenRollUpOneMonth_thenYearIsUnchanged() {
int rolledUpMonthJuly = 7, orginalYear2018 = 2018;
GregorianCalendar calendarExpected = new GregorianCalendar(2018, 6, 28);
calendarExpected.roll(Calendar.MONTH, 1);

assertEquals(calendarExpected.get(Calendar.MONTH), rolledUpMonthJuly);
assertEquals(calendarExpected.get(Calendar.YEAR), orginalYear2018);
}

Точно так же мы можем свернуть месяцы:

@Test
public void test_whenRollDownOneMonth_thenYearIsUnchanged() {
int rolledDownMonthJune = 5, orginalYear2018 = 2018;
GregorianCalendar calendarExpected = new GregorianCalendar(2018, 6, 28);
calendarExpected.roll(Calendar.MONTH, -1);

assertEquals(calendarExpected.get(Calendar.MONTH), rolledDownMonthJune);
assertEquals(calendarExpected.get(Calendar.YEAR), orginalYear2018);
}

Мы можем напрямую установить для поля календаря указанное значение, используя метод set() . Значение времени календаря в миллисекундах не будет пересчитываться до тех пор, пока не будет сделан следующий вызов get() , getTime() , add() или roll() .

Таким образом, многократные вызовы set() не вызывают ненужных вычислений.

Давайте посмотрим на пример, в котором для поля месяца будет установлено значение 3 (т.е. апрель):

@Test
public void test_setMonth() {
GregorianCalendarExample calendarDemo = new GregorianCalendarExample();
GregorianCalendar calendarActual = new GregorianCalendar(2018, 6, 28);
GregorianCalendar calendarExpected = new GregorianCalendar(2018, 6, 28);
calendarExpected.set(Calendar.MONTH, 3);
Date expectedDate = calendarExpected.getTime();

assertEquals(expectedDate, calendarDemo.setMonth(calendarActual, 3));
}

2.4. Работа с XMLGregorianCalendar

JAXB позволяет отображать классы Java в XML-представления. Тип javax.xml.datatype.XMLGregorianCalendar может помочь в отображении основных типов схемы XSD, таких как xsd:date , xsd:time и xsd:dateTime .

Давайте рассмотрим пример преобразования типа GregorianCalendar в тип XMLGregorianCalendar :

@Test
public void test_toXMLGregorianCalendar() throws Exception {
GregorianCalendarExample calendarDemo = new GregorianCalendarExample();
DatatypeFactory datatypeFactory = DatatypeFactory.newInstance();
GregorianCalendar calendarActual = new GregorianCalendar(2018, 6, 28);
GregorianCalendar calendarExpected = new GregorianCalendar(2018, 6, 28);
XMLGregorianCalendar expectedXMLGregorianCalendar = datatypeFactory
.newXMLGregorianCalendar(calendarExpected);

assertEquals(
expectedXMLGregorianCalendar,
alendarDemo.toXMLGregorianCalendar(calendarActual));
}

После преобразования объекта календаря в формат XML его можно использовать в любых случаях использования, требующих сериализации даты, таких как обмен сообщениями или вызовы веб-служб.

Давайте посмотрим на пример того, как преобразовать тип XMLGregorianCalendar обратно в GregorianCalendar :

@Test
public void test_toDate() throws DatatypeConfigurationException {
GregorianCalendar calendarActual = new GregorianCalendar(2018, 6, 28);
DatatypeFactory datatypeFactory = DatatypeFactory.newInstance();
XMLGregorianCalendar expectedXMLGregorianCalendar = datatypeFactory
.newXMLGregorianCalendar(calendarActual);
expectedXMLGregorianCalendar.toGregorianCalendar().getTime();
assertEquals(
calendarActual.getTime(),
expectedXMLGregorianCalendar.toGregorianCalendar().getTime() );
}

2.5. Сравнение дат

Мы можем использовать метод compareTo() классов Calendar для сравнения дат. Результат будет положительным, если базовая дата находится в будущем, и отрицательным, если базовые данные находятся в прошлом даты, с которой мы их сравниваем: ``

@Test
public void test_Compare_Date_FirstDate_Greater_SecondDate() {
GregorianCalendar firstDate = new GregorianCalendar(2018, 6, 28);
GregorianCalendar secondDate = new GregorianCalendar(2018, 5, 28);
assertTrue(1 == firstDate.compareTo(secondDate));
}

@Test
public void test_Compare_Date_FirstDate_Smaller_SecondDate() {
GregorianCalendar firstDate = new GregorianCalendar(2018, 5, 28);
GregorianCalendar secondDate = new GregorianCalendar(2018, 6, 28);
assertTrue(-1 == firstDate.compareTo(secondDate));
}

@Test
public void test_Compare_Date_Both_Dates_Equal() {
GregorianCalendar firstDate = new GregorianCalendar(2018, 6, 28);
GregorianCalendar secondDate = new GregorianCalendar(2018, 6, 28);
assertTrue(0 == firstDate.compareTo(secondDate));
}

2.6. Форматирование дат

Мы можем преобразовать GregorianCalendar в определенный формат, используя комбинацию ZonedDateTime и DateTimeFormatter , чтобы получить желаемый результат:

@Test
public void test_dateFormatdMMMuuuu() {
String expectedDate = new GregorianCalendar(2018, 6, 28).toZonedDateTime()
.format(DateTimeFormatter.ofPattern("d MMM uuuu"));
assertEquals("28 Jul 2018", expectedDate);
}

2.7. Получение информации о календаре

GregorianCalendar предоставляет несколько методов получения, которые можно использовать для получения различных атрибутов календаря. Давайте посмотрим на различные варианты, которые у нас есть:

  • getActualMaximum(int field) возвращает максимальное значение для указанного поля календаря с учетом текущих значений времени. В следующем примере будет возвращено значение 30 для поля DAY_OF_MONTH, поскольку в июне 30 дней:
GregorianCalendar calendar = new GregorianCalendar(2018 , 5, 28);
assertTrue(30 == calendar.getActualMaximum(calendar.DAY_OF_MONTH));
  • getActualMinimum(int field) возвращает минимальное значение для указанного поля календаря с учетом текущих значений времени:
GregorianCalendar calendar = new GregorianCalendar(2018 , 5, 28);
assertTrue(1 == calendar.getActualMinimum(calendar.DAY_OF_MONTH));
  • getGreatestMinimum(int field) возвращает максимальное минимальное значение для данного поля календаря:
GregorianCalendar calendar = new GregorianCalendar(2018 , 5, 28);
assertTrue(1 == calendar.getGreatestMinimum(calendar.DAY_OF_MONTH));
  • getLeastMaximum(int field) возвращает наименьшее максимальное значение для данного поля календаря. Для поля DAY_OF_MONTH это 28, потому что в феврале может быть только 28 дней:
GregorianCalendar calendar = new GregorianCalendar(2018 , 5, 28);
assertTrue(28 == calendar.getLeastMaximum(calendar.DAY_OF_MONTH));
  • getMaximum(int field) возвращает максимальное значение для данного поля календаря:
GregorianCalendar calendar = new GregorianCalendar(2018 , 5, 28);
assertTrue(31 == calendar.getMaximum(calendar.DAY_OF_MONTH));
  • getMinimum(int field) возвращает минимальное значение для данного поля календаря:
GregorianCalendar calendar = new GregorianCalendar(2018 , 5, 28);
assertTrue(1 == calendar.getMinimum(calendar.DAY_OF_MONTH));
  • getWeekYear() возвращает год недели , представленный этим GregorianCalendar :
GregorianCalendar calendar = new GregorianCalendar(2018 , 5, 28);
assertTrue(2018 == calendar.getWeekYear());
  • getWeeksInWeekYear() возвращает количество недель в недельном году для календарного года:
GregorianCalendar calendar = new GregorianCalendar(2018 , 5, 28);
assertTrue(52 == calendar.getWeeksInWeekYear());
  • isLeapYear() возвращает true, если год високосный:
GregorianCalendar calendar = new GregorianCalendar(2018 , 5, 28);
assertTrue(false == calendar.isLeapYear(calendar.YEAR));

3. Заключение

В этой статье мы рассмотрели некоторые аспекты GregorianCalendar .

Как всегда, пример кода доступен на GitHub .