Saltar al contenido

Usando el nuevo Java 8 DateTimeFormatter para hacer un análisis de fecha estricto

Solución:

Usos de Java 8 uuuu por año, no yyyy. En Java 8, yyyy significa “año de la era” (BC o AD) y el mensaje de error indica que MonthOfYear, DayOfMonth y YearOfEra no son información suficiente para construir la fecha porque no se conoce la era.

Para arreglar eso, usa uuuu en su cadena de formato, por ejemplo DateTimeFormatter.ofPattern("uuuuMMdd")

O, si quieres seguir usando yyyy, puede establecer la era predeterminada, p. ej.

private static final DateTimeFormatter FORMAT = new DateTimeFormatterBuilder()
            .appendPattern("yyyyMMdd")
            .parseDefaulting(ChronoField.ERA, 1 /* era is AD */)
            .toFormatter()
            .withChronology(IsoChronology.INSTANCE)
            .withResolverStyle(ResolverStyle.STRICT);

Estoy editando para limitar qué tipo de cadena se considerará válida mediante el uso de un formateador personalizado creado con un DateTimeFormatterBuilder.

public class DateFormmaterTest {

    static DateTimeFormatter CUSTOM_BASIC_ISO_DATE = new DateTimeFormatterBuilder()
            .parseCaseInsensitive().appendValue(YEAR, 4)
            .appendValue(MONTH_OF_YEAR, 2).appendValue(DAY_OF_MONTH, 2)
            .optionalStart().toFormatter()
            .withResolverStyle(ResolverStyle.STRICT)
            .withChronology(IsoChronology.INSTANCE);

    public static void main(String[] args) {

        LocalDate date1 = LocalDate.parse("19800228-5000",
                CUSTOM_BASIC_ISO_DATE);

        System.out.println(date1);

    }
}

29/02/1982 no es válido y arrojaría lo siguiente:

Caused by: java.time.DateTimeException: Invalid date 'February 29' as '1982' is not a leap year
    at java.time.LocalDate.create(LocalDate.java:429)

Una fecha de 19800228-5000 funcionaría con BASIC_ISO_DATE porque permite el desplazamiento opcional que no desea que se permita. Mi formateador CUSTOM_BASIC_ISO_DATE no lo permitirá y arrojará lo siguiente:

Exception in thread "main" java.time.format.DateTimeParseException: Text '19800228-5000' could not be parsed, unparsed text found at index 8. 

Tenga en cuenta que si está seguro de la longitud de la cadena, aaaaMMdd, siempre puede trabajar con la subcadena de los primeros 8 caracteres para negar la necesidad del resolutor. Sin embargo, son dos cosas diferentes. El solucionador marcará los formatos de fecha no válidos en la entrada y la subcadena, por supuesto, simplemente eliminará los caracteres adicionales.

Recientemente tuve un requisito similar. Estaba revisando una aplicación heredada que se escribió cuando Java 5 era la versión más reciente. Necesitaba seguir analizando formatos de fecha y fecha / hora específicos, pero quería el mismo comportamiento ‘estricto’ que el antiguo SimpleDateFormat clase utilizada para proporcionar. También tuve la necesidad de convertir a / desde java.util.Date para compatibilidad con código existente más antiguo.

Aquí está mi clase de utilidad y pruebas unitarias:

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.ResolverStyle;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.util.Date;

/**
 * Static utility class for converting from java.util.Date to modern Java time classes,
 * and some predefined DateTimeFormatter instances to handle formats used in data files
 * that this application processes.
 */
public class DateTimeUtils {

    private DateTimeUtils() {}

    // Jim Tough - 2020-09-14
    // These two date/time formats are part of the data file specification.
    // We are stuck with these formats now.
    private static final String DTF_YYYYMMDDHHMMSS_FORMAT_STRING = "uuuuMMdd HH:mm:ss";
    private static final String DTF_YYYYMMDDHHMM_FORMAT_STRING = "uuuuMMddHHmm";
    private static final String DTF_YYYYMMDD_FORMAT_STRING = "uuuuMMdd";

    public static final DateTimeFormatter DTF_YYYYMMDDHHMMSS =
            DateTimeFormatter.ofPattern(DTF_YYYYMMDDHHMMSS_FORMAT_STRING)
                    .withResolverStyle(ResolverStyle.STRICT);

    public static final DateTimeFormatter DTF_YYYYMMDDHHMM =
            DateTimeFormatter.ofPattern(DTF_YYYYMMDDHHMM_FORMAT_STRING)
                    .withResolverStyle(ResolverStyle.STRICT);

    public static final DateTimeFormatter DTF_YYYYMMDD =
            DateTimeFormatter.ofPattern(DTF_YYYYMMDD_FORMAT_STRING)
                    .withResolverFields(ChronoField.YEAR, ChronoField.MONTH_OF_YEAR, ChronoField.DAY_OF_MONTH)
                    .withResolverStyle(ResolverStyle.STRICT);

    //-------------------------------------------------------------------------

    public static LocalDate convertToLocalDateViaInstant(Date dateToConvert) {
        if (dateToConvert == null) {
            return null;
        }
        return dateToConvert.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDate();
    }

    public static LocalDateTime convertToLocalDateTimeViaInstant(Date dateToConvert) {
        if (dateToConvert == null) {
            return null;
        }
        return dateToConvert
                .toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDateTime();
    }

    public static Date convertToDateViaInstant(LocalDate dateToConvert) {
        if (dateToConvert == null) {
            return null;
        }
        return java.util.Date.from(dateToConvert.atStartOfDay()
                .atZone(ZoneId.systemDefault())
                .toInstant());
    }

    public static Date convertToDateViaInstant(LocalDateTime dateToConvert) {
        if (dateToConvert == null) {
            return null;
        }
        return java.util.Date.from(
                dateToConvert.atZone(ZoneId.systemDefault()).toInstant()
        );
    }

    //-------------------------------------------------------------------------

    /**
     * Parse the supplied string using the supplied {@code DateTimeFormatter} and return the result
     * as a {@code LocalDate}
     * @param dtf Non-null
     * @param s String, or null value
     * @return {@code LocalDate}, or null if the supplied string parameter is null
     * @throws java.time.DateTimeException when supplied string cannot be parsed by the
     *   {@code DateTimeFormatter} and converted to a {@code LocalDate}
     */
    public static LocalDate parseAsLocalDate(DateTimeFormatter dtf, String s) {
        if (s == null) {
            return null;
        }
        TemporalAccessor ta = dtf.parse(s);
        return LocalDate.from(ta);
    }

    /**
     * Parse the supplied string using the supplied {@code DateTimeFormatter} and return the result
     * as a {@code LocalDateTime}
     * @param dtf Non-null
     * @param s String, or null value
     * @return {@code LocalDateTime}, or null if the supplied string parameter is null
     * @throws java.time.DateTimeException when supplied string cannot be parsed by the
     *   {@code DateTimeFormatter} and converted to a {@code LocalDateTime}
     */
    public static LocalDateTime parseAsLocalDateTime(DateTimeFormatter dtf, String s) {
        if (s == null) {
            return null;
        }
        TemporalAccessor ta = dtf.parse(s);
        return LocalDateTime.from(ta);
    }

}

pruebas unitarias …

import org.junit.jupiter.api.Test;

import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Month;

import static org.junit.jupiter.api.Assertions.*;

/**
 * @author JTough
 */
public class DateTimeUtilsTest {

    @Test
    void testConvertToLocalDateViaInstant_nominal() {
        LocalDate ld = DateTimeUtils.convertToLocalDateViaInstant(new java.util.Date());

        assertNotNull(ld);
    }

    @Test
    void testConvertToLocalDateViaInstantWithNullValue() {
        assertNull(DateTimeUtils.convertToLocalDateViaInstant(null));
    }

    //-------------------------------------------------------------------------

    @Test
    void testConvertToLocalDateTimeViaInstant_nominal() {
        LocalDateTime ldt = DateTimeUtils.convertToLocalDateTimeViaInstant(new java.util.Date());

        assertNotNull(ldt);
    }

    @Test
    void testConvertToLocalDateTimeViaInstantWithNullValue() {
        assertNull(DateTimeUtils.convertToLocalDateTimeViaInstant(null));
    }

    //-------------------------------------------------------------------------

    @Test
    void testConvertToDateViaInstant_nominal_A() {
        java.util.Date d = DateTimeUtils.convertToDateViaInstant(LocalDate.now());

        assertNotNull(d);
    }

    @Test
    void testConvertToDateViaInstantWithNullValueA() {
        assertNull(DateTimeUtils.convertToDateViaInstant((LocalDate)null));
    }

    //-------------------------------------------------------------------------

    @Test
    void testConvertToDateViaInstant_nominal_B() {
        java.util.Date d = DateTimeUtils.convertToDateViaInstant(LocalDateTime.now());

        assertNotNull(d);
    }

    @Test
    void testConvertToDateViaInstantWithNullValueB() {
        assertNull(DateTimeUtils.convertToDateViaInstant((LocalDate)null));
        assertNull(DateTimeUtils.convertToDateViaInstant((LocalDateTime)null));
    }

    //-------------------------------------------------------------------------

    @Test
    void testParseAsLocalDate_nominal_A() {
        LocalDate ld = DateTimeUtils.parseAsLocalDate(DateTimeUtils.DTF_YYYYMMDD, "20201225");

        assertNotNull(ld);
        assertEquals(2020, ld.getYear());
        assertEquals(Month.DECEMBER, ld.getMonth());
        assertEquals(25, ld.getDayOfMonth());
    }

    @Test
    void testParseAsLocalDate_nominal_B() {
        LocalDate ld = DateTimeUtils.parseAsLocalDate(DateTimeUtils.DTF_YYYYMMDD, "20200229");

        assertNotNull(ld);
        assertEquals(2020, ld.getYear());
        assertEquals(Month.FEBRUARY, ld.getMonth());
        assertEquals(29, ld.getDayOfMonth());
    }

    @Test
    void testParseAsLocalDateWithInvalidDayOfMonth() {
        assertThrows(DateTimeException.class, ()->DateTimeUtils.parseAsLocalDate(DateTimeUtils.DTF_YYYYMMDD, "20200230"));
    }

    @Test
    void testParseAsLocalDateWithGarbageInputString() {
        assertThrows(DateTimeException.class, ()->DateTimeUtils.parseAsLocalDate(DateTimeUtils.DTF_YYYYMMDD, "garbage"));
    }

    //-------------------------------------------------------------------------

    @Test
    void testParseAsLocalDateTime_withoutseconds_nominal_A() {
        LocalDateTime ldt = DateTimeUtils.parseAsLocalDateTime(DateTimeUtils.DTF_YYYYMMDDHHMM, "202012251359");

        assertNotNull(ldt);
        assertEquals(2020, ldt.getYear());
        assertEquals(Month.DECEMBER, ldt.getMonth());
        assertEquals(25, ldt.getDayOfMonth());
        assertEquals(13, ldt.getHour());
        assertEquals(59, ldt.getMinute());
    }

    @Test
    void testParseAsLocalDateTime_withseconds_nominal_A() {
        LocalDateTime ldt = DateTimeUtils.parseAsLocalDateTime(DateTimeUtils.DTF_YYYYMMDDHHMMSS, "20201225 13:59:33");

        assertNotNull(ldt);
        assertEquals(2020, ldt.getYear());
        assertEquals(Month.DECEMBER, ldt.getMonth());
        assertEquals(25, ldt.getDayOfMonth());
        assertEquals(13, ldt.getHour());
        assertEquals(59, ldt.getMinute());
        assertEquals(33, ldt.getSecond());
    }

    @Test
    void testParseAsLocalDateTime_withoutseconds_nominal_B() {
        LocalDateTime ldt = DateTimeUtils.parseAsLocalDateTime(DateTimeUtils.DTF_YYYYMMDDHHMM, "202002291234");

        assertNotNull(ldt);
        assertEquals(2020, ldt.getYear());
        assertEquals(Month.FEBRUARY, ldt.getMonth());
        assertEquals(29, ldt.getDayOfMonth());
        assertEquals(12, ldt.getHour());
        assertEquals(34, ldt.getMinute());
    }

    @Test
    void testParseAsLocalDateTime_withseconds_nominal_B() {
        LocalDateTime ldt = DateTimeUtils.parseAsLocalDateTime(DateTimeUtils.DTF_YYYYMMDDHHMMSS, "20200229 12:34:56");

        assertNotNull(ldt);
        assertEquals(2020, ldt.getYear());
        assertEquals(Month.FEBRUARY, ldt.getMonth());
        assertEquals(29, ldt.getDayOfMonth());
        assertEquals(12, ldt.getHour());
        assertEquals(34, ldt.getMinute());
        assertEquals(56, ldt.getSecond());
    }

    @Test
    void testParseAsLocalDateTimeWithInvalidDayOfMonth() {
        assertThrows(DateTimeException.class, ()->DateTimeUtils.parseAsLocalDateTime(DateTimeUtils.DTF_YYYYMMDDHHMM, "202002301234"));
    }

    @Test
    void testParseAsLocalDateTimeWithInvalidHour() {
        assertThrows(DateTimeException.class, ()->DateTimeUtils.parseAsLocalDateTime(DateTimeUtils.DTF_YYYYMMDDHHMM, "202012252500"));
    }

    @Test
    void testParseAsLocalDateTimeWithInvalidMinute() {
        assertThrows(DateTimeException.class, ()->DateTimeUtils.parseAsLocalDateTime(DateTimeUtils.DTF_YYYYMMDDHHMM, "202012251366"));
    }

    @Test
    void testParseAsLocalDateTimeWithGarbageInputString() {
        assertThrows(DateTimeException.class, ()->DateTimeUtils.parseAsLocalDateTime(DateTimeUtils.DTF_YYYYMMDD, "garbage"));
    }

}
¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *