Duke Guardian

En el artículo anterior Conociendo la nueva Date API en Java 8 (Parte I), se dio a conocer las nuevas clases que forman parte de la nueva Date API. En este artículo se revisará los siguientes tópicos:

Manipulación

Manipulando LocalDate

Haciendo uso de los métodos withYear(int year), withMonth(int month), withDayOfMonth(int dayOfMonth), with(TemporalField field, long newValue) se puede modificar el LocalDate.

LocalDate date = LocalDate.of(2016, Month.JULY, 25); //2016-07-25
LocalDate date1 = date.withYear(2017); //2017-07-25
LocalDate date2 = date.withMonth(8); //2016-08-25
LocalDate date3 = date.withDayOfMonth(27); //2016-07-27
LocalDate date4 = date.with(ChronoField.MONTH_OF_YEAR, 9); //2016-09-25

NOTA: Si en el último ejemplo usamos ChronoField.HOUR_OF_DAY la siguiente excepción será lanzada java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: HourOfDay.

Manipulando LocalTime

Haciendo uso de los métodos withHour(int hour), withMinute(int minute), withSecond(int second), withNano(int nanoOfSecond) se puede modificar el LocalTime.

LocalTime time = LocalTime.of(14, 30, 35); //14:30:35
LocalTime time1 = time.withHour(20); //20:30:35
LocalTime time2 = time.withMinute(25); //14:25:35
LocalTime time3 = time.withSecond(23); //14:30:23
LocalTime time4 = time.withNano(24); //14:30:35.000000024
LocalTime time5 = time.with(ChronoField.HOUR_OF_DAY, 23); //23:30:35

NOTA: Si en el último ejemplo usamos ChronoField.MONTH_OF_YEAR la siguiente excepción será lanzada java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: MonthOfYear.

Manipulando LocalDateTime

LocalDateTime provee los mismo métodos mencionados en las clases LocalDate y LocalTime.

LocalDateTime dateTime = LocalDateTime.of(2016, Month.JULY, 25, 22, 11, 30);
LocalDateTime dateTime1 = dateTime.withYear(2017);
LocalDateTime dateTime2 = dateTime.withMonth(8);
LocalDateTime dateTime3 = dateTime.withDayOfMonth(27);
LocalDateTime dateTime4 = dateTime.withHour(20);
LocalDateTime dateTime5 = dateTime.withMinute(25);
LocalDateTime dateTime6 = dateTime.withSecond(23);
LocalDateTime dateTime7 = dateTime.withNano(24);
LocalDateTime dateTime8 = dateTime.with(ChronoField.HOUR_OF_DAY, 23);

Uso de TemporalAdjusters

LocalDate, LocalTime y LocalDateTime proveen los siguientes métodos:

  • with(TemporalField field, long newValue)

  • with(TemporalAdjuster adjuster)

El primero de ellos ha sido revisado en los ejemplos anteriores. El segundo brinda una serie de métodos que son útiles para cálculos.

import static java.time.temporal.TemporalAdjusters.next;
import static java.time.temporal.TemporalAdjusters.firstDayOfNextMonth;
import static java.time.temporal.TemporalAdjusters.lastDayOfMonth;

LocalDateTime dateTime = LocalDateTime.of(2016, Month.JULY, 25, 22, 11, 30);
LocalDateTime dateTime1 = dateTime.with(next(DayOfWeek.SUNDAY)); (1)
LocalDateTime dateTime2 = dateTime.with(firstDayOfNextMonth()); (2)
LocalDateTime dateTime3 = dateTime.with(lastDayOfMonth()); (3)
  1. Retorna el próximo Domingo.

  2. Retorna el primer día del siguiente mes (Agosto).

  3. Retorna el último día del mes (Julio).

TemporalAdjuster es una FunctionalInterface, eso quiere decir que se puede crear una implementación propia haciendo uso de lambdas.

LocalDateTime dateTime = LocalDateTime.of(2016, Month.JULY, 28, 22, 11, 30);
TemporalAdjuster tempAdj = temp -> nextDayAfterHolidays(LocalDateTime.from(temp));
LocalDateTime dateTime1 = dateTime.with(tempAdj);

public Temporal nextDayAfterHolidays(LocalDateTime dateTime) {
  LocalDate[] holidays = {
    LocalDate.of(2016, Month.JULY, 28),
    LocalDate.of(2016, Month.JULY, 29)
  };

  LocalDate date = dateTime.toLocalDate();
  boolean isHoliday = Arrays.stream(holidays)
                            .anyMatch(holiday -> holiday.isEqual(date));
  if (isHoliday) {
    return nextDayAfterHolidays(dateTime.plus(1, ChronoUnit.DAYS));
  }
  return dateTime;
}

Operaciones

Operaciones con LocalDate

Realizar operaciones como suma o resta de días, meses, años, etc es muy fácil con la nueva Date API. Los siguientes métodos plus(long amountToAdd, TemporalUnit unit), minus(long amountToSubtract, TemporalUnit unit) proveen una manera general de realizar estas operaciones.

LocalDate date = LocalDate.of(2016, Month.JULY, 18);
LocalDate datePlusOneDay = date.plus(1, ChronoUnit.DAYS);
LocalDate dateMinusOneDay = date.minus(1, ChronoUnit.DAYS);

Asimismo, puede hacerse uso de las siguientes unidades ChronoUnit.YEARS, ChronoUnit.MONTHS.

NOTA: Si en los ejemplos usamos ChronoUnit.HOURS la siguiente excepción será lanzada java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: Hours.

También se puede hacer cálculos basados en un Period. En el siguiente ejemplo, se crea un Period de 1 día para poder realizar los cálculos.

LocalDate date = LocalDate.of(2016, Month.JULY, 18);
LocalDate datePlusOneDay = date.plus(Period.ofDays(1));
LocalDate dateMinusOneDay = date.minus(Period.ofDays(1));

Finalmente, haciendo uso de métodos explícitos como plusDays(long daysToAdd) y minusDays(long daysToSubtract) se puede indicar el valor a incrementar o reducir.

LocalDate date = LocalDate.of(2016, Month.JULY, 18);
LocalDate datePlusOneDay = date.plusDays(1);
LocalDate dateMinusOneDay = date.minusDays(1);

Operaciones con LocalTime

La nueva Date API perimite realizar operaciones como suma y resta de horas, minutos, segundos, etc. Al igual que LocalDate, los siguientes métodos plus(long amountToAdd, TemporalUnit unit), minus(long amountToSubtract, TemporalUnit unit) proveen una manera general de realizar estas operaciones.

LocalTime time = LocalTime.of(15, 30);
LocalTime timePlusOneHour = time.plus(1, ChronoUnit.HOURS);
LocalTime timeMinusOneHour = time.minus(1, ChronoUnit.HOURS);

Asimismo, puede hacerse uso de las siguientes unidades ChronoUnit.MINUTES, ChronoUnit.SECONDS, ChronoUnit.NANOS.

NOTA: Si en los ejemplos usamos ChronoUnit.DAYS la siguiente excepción será lanzada java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: Days.

También se puede hacer cálculos basados en un Duration. En el siguiente ejemplo, se crea un Duration de 1 hora para poder realizar los cálculos.

LocalTime time = LocalTime.of(15, 30);
LocalTime timePlusOneHour = time.plus(Duration.ofHours(1));
LocalTime timeMinusOneHour = time.minus(Duration.ofHours(1));

Finalmente, haciendo uso de métodos explícitos como plusHours(long hoursToAdd) y minusHours(long hoursToSubtract) se puede indicar el valor a incrementar o reducir.

LocalTime time = LocalTime.of(15, 30);
LocalTime timePlusOneHour = time.plusHours(1);
LocalTime timeMinusOneHour = time.minusHours(1);

Operaciones con LocalDateTime

LocalDateTime, al ser una clase compuesta por LocalDate y LocalTime ofrece los mismos métodos para realizar operaciones.

LocalDateTime dateTime = LocalDateTime.of(2016, Month.JULY, 28, 14, 30);
LocalDateTime dateTime1 = dateTime.plus(1, ChronoUnit.DAYS)
                                  .plus(1, ChronoUnit.HOURS);
LocalDateTime dateTime2 = dateTime.minus(1, ChronoUnit.DAYS)
                                  .minus(1, ChronoUnit.HOURS);

En el siguiente ejemplo, se hace uso de Period y Duration.

LocalDateTime dateTime = LocalDateTime.of(2016, Month.JULY, 28, 14, 30);
LocalDateTime dateTime1 = dateTime.plus(Period.ofDays(1))
                                  .plus(Duration.ofHours(1));
LocalDateTime dateTime2 = dateTime.minus(Period.ofDays(1))
                                  .minus(Duration.ofHours(1));

Finalmente, haciendo uso de los métodos plusX(long xToAdd) o minusX(long xToSubtract)

LocalDateTime dateTime = LocalDateTime.of(2016, Month.JULY, 28, 14, 30);
LocalDateTime dateTime1 = dateTime.plusDays(1)
                                  .plusHours(1);
LocalDateTime dateTime2 = dateTime.minusDays(1)
                                  .minusHours(1);

Además, métodos como isBefore, isAfter, isEequal están disponibles para comparar las siguientes clases LocalDate, LocalTime y LocalDateTime.

LocalDate date1 = LocalDate.of(2016, Month.JULY, 28);
LocalDate date2 = LocalDate.of(2016, Month.JULY, 29);

boolean isBefore = date1.isBefore(date2); //true
boolean isAfter = date2.isAfter(date1); //true
boolean isEqual = date1.isEqual(date2); //false

Formatos

Cuando se trabaja con fechas, en ocasiones se requiere de un formato personalizado. Java 8 ofrece la clase java.time.format.DateTimeFormatter la cual provee algunos formatos.

LocalDate date = LocalDate.of(2016, Month.JULY, 25);
String date1 = date.format(DateTimeFormatter.BASIC_ISO_DATE); //20160725
String date2 = date.format(DateTimeFormatter.ISO_DATE); //2016-07-25

NOTA: Si en el último ejemplo usamos DateTimeFormatter.ISO_DATE_TIME la siguiente excepción será lanzada java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: HourOfDay.

También se puede usar el método ofPattern(String pattern), para definir un formato en particular.

LocalDate date = LocalDate.of(2016, Month.JULY, 25);
String date1 = date.format(DateTimeFormatter.ofPattern("yyyy/MM/dd")); //2016/07/25

Por último, se puede hacer uso de la clase java.time.format.DateTimeFormatterBuilder.

LocalDateTime dateTime = LocalDateTime.of(2016, Month.JULY, 25, 15, 30);
OffsetDateTime offsetDateTime = dateTime.atOffset(ZoneOffset.ofHours(-5));
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
                                  .appendText(ChronoField.DAY_OF_MONTH)
                                  .appendLiteral(" ")
                                  .appendText(ChronoField.MONTH_OF_YEAR)
                                  .appendLiteral(" ")
                                  .appendText(ChronoField.YEAR)
                                  .appendOffsetId()
                                  .toFormatter();
String dateTime1 = offsetDateTime.format(formatter); //25 July 2016-05:00

Gracias a Takipi por dejarme usar sus imágenes.


Eddú Meléndez

Java Software Engineer, Open Source Contributor