Guida a java.util.GregorianCalendar

1. Introduzione

In questo tutorial, daremo una rapida occhiata alla classe GregorianCalendar .

2. GregorianCalendar

GregorianCalendar è un'implementazione concreta della classe astratta java.util.Calendar . Non a caso, il calendario gregoriano è il calendario civile più utilizzato al mondo.

2.1. Ottenere un'istanza

Sono disponibili due opzioni per ottenere un'istanza di GregorianCalendar: Calendar.getInstance () e utilizzando uno dei costruttori.

L'utilizzo del metodo factory statico Calendar.getInstance () non è un approccio consigliato in quanto restituirà un'istanza soggettiva alla locale predefinita.

Potrebbe restituire un BuddhistCalendar per Thai o JapaneseImperialCalendar per il Giappone. Non conoscere il tipo di istanza restituita può portare a un'eccezione 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; }

Utilizzando uno dei sette costruttori sovraccaricati, possiamo inizializzare l' oggetto Calendar con la data e l'ora predefinite a seconda della locale del nostro sistema operativo oppure possiamo specificare una combinazione di data, ora, locale e fuso orario.

Comprendiamo i diversi costruttori con cui è possibile istanziare un oggetto GregorianCalendar .

Il costruttore predefinito inizializzerà il calendario con la data e l'ora correnti nel fuso orario e nelle impostazioni locali del sistema operativo:

new GregorianCalendar();

Possiamo specificare anno, mese, dayOfMonth, hourOfDay, minuti e secondi per il fuso orario predefinito con le impostazioni internazionali predefinite:

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

Si noti che non è necessario specificare hourOfDay, minute e second in quanto esistono altri costruttori senza questi parametri.

Possiamo passare il fuso orario come parametro per creare un calendario in questo fuso orario con le impostazioni internazionali predefinite:

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

Possiamo passare le impostazioni locali come parametro per creare un calendario in queste impostazioni internazionali con il fuso orario predefinito:

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

Infine, possiamo passare sia il fuso orario che le impostazioni locali come parametri:

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

2.2. Nuovi metodi con Java 8

Con Java 8, sono stati introdotti nuovi metodi in GregorianCalendar.

Il metodo from () ottiene un'istanza di GregorianCalendar con la locale predefinita da un oggetto ZonedDateTime.

Usando getCalendarType () possiamo ottenere il tipo di istanza del calendario. I tipi di calendario disponibili sono "gregory", "buddhist" e "japanese".

Possiamo usarlo, ad esempio, per assicurarci di avere un calendario di un certo tipo prima di passare alla logica dell'applicazione:

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

Chiamando toZonedDateTime () possiamo convertire l'oggetto calendario in un oggetto ZonedDateTime che rappresenta lo stesso punto sulla timeline di questo GregorianCalendar.

2.3. Modifica delle date

I campi del calendario possono essere modificati utilizzando i metodi add () , roll () e set () .

Il metodo add () ci consente di aggiungere tempo al calendario in un'unità specificata in base alle regole interne del calendario:

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

Possiamo anche usare il metodo add () per sottrarre l'ora dall'oggetto calendario:

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

L'esecuzione del metodo add () forza un ricalcolo immediato dei millisecondi del calendario e di tutti i campi.

Si noti che l'uso di add () può anche modificare i campi del calendario superiori (MESE in questo caso).

Il metodo roll () aggiunge un importo con segno al campo del calendario specificato senza modificare i campi più grandi. Un campo più grande rappresenta un'unità di tempo più ampia. Ad esempio, DAY_OF_MONTH è maggiore di HOUR.

Vediamo un esempio di come arrotolare i mesi.

In questo caso, YEAR essendo un campo più grande non verrà incrementato:

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

Allo stesso modo, possiamo abbassare i mesi:

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

Possiamo impostare direttamente un campo del calendario su un valore specificato utilizzando il metodo set () . Il valore dell'ora del calendario in millisecondi non verrà ricalcolato fino alla successiva chiamata a get () , getTime () , add () o roll () .

Pertanto, più chiamate a set () non attivano calcoli non necessari.

Vediamo un esempio che imposterà il campo del mese su 3 (cioè aprile):

@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. Lavorare con XMLGregorianCalendar

JAXB consente di associare classi Java a rappresentazioni XML. Il tipo javax.xml.datatype.XMLGregorianCalendar può aiutare a mappare i tipi di schema XSD di base come xsd: date , xsd: time e xsd: dateTime .

Diamo un'occhiata a un esempio per convertire dal tipo GregorianCalendar nel tipo 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)); }

Once the calendar object has been translated into XML format, it can be used in any use cases that require a date to be serialized, like messaging or web service calls.

Let's see an example on how to convert from XMLGregorianCalendar type back into 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. Comparing Dates

We can use the Calendar classes' compareTo() method to compare dates. The result will be positive if the base date is in the future and negative if the base data is in the past of the date we compare it to:

@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. Formatting Dates

We can convert GregorianCalendar into a specific format by using a combination of ZonedDateTime and DateTimeFormatter to get the desired output:

@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. Getting Information About the Calendar

GregorianCalendar provides several get methods which can be used to fetch different calendar attributes. Let's look at the different options we have:

  • getActualMaximum(int field) returns the maximum value for the specified calendar field taking into consideration the current time values. The following example will return value 30 for the DAY_OF_MONTH field because June has 30 days:
    GregorianCalendar calendar = new GregorianCalendar(2018 , 5, 28); assertTrue(30 == calendar.getActualMaximum(calendar.DAY_OF_MONTH));
  • getActualMinimum(int field) returns the minimum value for the specified calendar field taking into consideration the current time values:
    GregorianCalendar calendar = new GregorianCalendar(2018 , 5, 28); assertTrue(1 == calendar.getActualMinimum(calendar.DAY_OF_MONTH));
  • getGreatestMinimum(int field) returns the highest minimum value for the given calendar field:
    GregorianCalendar calendar = new GregorianCalendar(2018 , 5, 28); assertTrue(1 == calendar.getGreatestMinimum(calendar.DAY_OF_MONTH));
  • getLeastMaximum(int field) Returns the lowest maximum value for the given calendar field. For the DAY_OF_MONTH field this is 28, because February may have only 28 days:
    GregorianCalendar calendar = new GregorianCalendar(2018 , 5, 28); assertTrue(28 == calendar.getLeastMaximum(calendar.DAY_OF_MONTH));
  • getMaximum(int field) returns the maximum value for the given calendar field:
    GregorianCalendar calendar = new GregorianCalendar(2018 , 5, 28); assertTrue(31 == calendar.getMaximum(calendar.DAY_OF_MONTH));
  • getMinimum(int field) returns the minimum value for the given calendar field:
    GregorianCalendar calendar = new GregorianCalendar(2018 , 5, 28); assertTrue(1 == calendar.getMinimum(calendar.DAY_OF_MONTH));
  • getWeekYear() returns the year of the week represented by this GregorianCalendar:
    GregorianCalendar calendar = new GregorianCalendar(2018 , 5, 28); assertTrue(2018 == calendar.getWeekYear());
  • getWeeksInWeekYear() returns the number of weeks in the week year for the calendar year:
    GregorianCalendar calendar = new GregorianCalendar(2018 , 5, 28); assertTrue(52 == calendar.getWeeksInWeekYear());
  • isLeapYear() returns true if the year is a leap year:
    GregorianCalendar calendar = new GregorianCalendar(2018 , 5, 28); assertTrue(false == calendar.isLeapYear(calendar.YEAR));

3. Conclusion

In questo articolo, abbiamo esplorato alcuni aspetti di GregorianCalendar .

Come sempre, il codice di esempio è disponibile su GitHub.