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