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

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

1. Вступление

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

2. GregorianCalendarс

GregorianCalendar - это конкретная реализация абстрактного классаjava.util.Calendar. Неудивительно, что григорианский календарьis the most widely used civil calendar in the world. 

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

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

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

Он может вернутьBuddhistCalendar для тайского языка илиJapaneseImperialCalendar для Японии. Незнание типа возвращаемого экземпляра может привести к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();

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

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

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

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

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.

Using getCalendarType() we can get the type of the calendar instance. Доступные типы календаря: «грегори», «буддийский» и «японский».

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

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

Calling toZonedDateTime() we can convert the calendar object into a ZonedDateTime object, который представляет ту же точку на временной шкале, что и этотGregorianCalendar.

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

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

The add() method allows us to add time to the calendar in a specified unit на основе внутреннего набора правил календаря:

@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);
}

We can directly set a calendar field to a specified value using the set() method. Значение времени календаря в миллисекундах не будет пересчитано до тех пор, пока не будет выполнен следующий вызов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() возвращаетyear of the week , представленный этим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() возвращает истину, если год високосный:

    GregorianCalendar calendar = new GregorianCalendar(2018 , 5, 28);
    assertTrue(false == calendar.isLeapYear(calendar.YEAR));

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

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

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