java.text.ParseException: Fecha no analizable: java.text.DateFormat.parse(DateFormat.java:579)
Tengo un problema con SimpleDateFormat
.
SimpleDateFormat dtfmt=new SimpleDateFormat("dd MMM yyyy hh:mm a", Locale.getDefault());
Date dt=dtfmt.parse(deptdt);
En Android Emulator funciona bien pero en el teléfono tengo este error:
W/System.err: java.text.ParseException: fecha no analizable: "24 de octubre de 2016 7:31 pm" (en el desplazamiento 3) W/System.err: en java.text.DateFormat.parse(DateFormat.java:579)
¿Alguna solución?
Especifique siempre a Locale
con a SimpleDateFormat
y a DateTimeFormatter
para formatos personalizados.
Dado que la fecha y hora proporcionada está en inglés, debe utilizarla Locale.ENGLISH
con su analizador de fecha y hora; de lo contrario, el análisis fallará en un sistema (computadora, teléfono, etc.) que utilice un tipo de configuración regional que no sea inglés.
Además, tenga en cuenta que la API de fecha y hora java.util
y su API de formato SimpleDateFormat
están desactualizadas y son propensas a errores. Se recomienda dejar de usarlos por completo y cambiar a la API moderna de fecha y hora .
- Por cualquier motivo, si tiene que ceñirse a Java 6 o Java 7, puede usar ThreeTen-Backport , que soporta la mayor parte de la funcionalidad java.time a Java 6 y 7.
- Si está trabajando en un proyecto de Android y su nivel de API de Android aún no es compatible con Java-8, consulte las API de Java 8+ disponibles mediante desugaring y Cómo usar ThreeTenABP en el proyecto de Android .
Manifestación:
public class Main {
public static void main(String[] args) {
final String strDateTime = "24 Oct 2016 7:31 pm";
DateTimeFormatter dtf = new DateTimeFormatterBuilder()
.parseCaseInsensitive() // For case-insensitive (e.g. am, Am, AM) parsing
.appendPattern("d MMM uuuu h:m a") // Pattern conforming to the date-time string
.toFormatter(Locale.ENGLISH); // Locale
LocalDateTime ldt = LocalDateTime.parse(strDateTime, dtf);
System.out.println(ldt);
}
}
Producción:
2016-10-24T19:31
ONLINE DEMO
De forma predeterminada, DateTimeFormatter#ofPattern
utiliza la configuración regional FORMAT predeterminada que la JVM establece durante el inicio según el entorno del host. Lo mismo ocurre con SimpleDateFormat
. Intenté ilustrar el problema a través de la siguiente demostración:
public class Main {
public static void main(String[] args) {
final String strDateTime = "24 Oct 2016 7:31 pm";
DateTimeFormatter dtfWithDefaultLocale = null;
System.out.println("JVM's Locale: " + Locale.getDefault());
// Using DateTimeFormatter with the default Locale
dtfWithDefaultLocale = getDateTimeFormatterWithDefaultLocale();
System.out.println("DateTimeFormatter's Locale: " + dtfWithDefaultLocale.getLocale());
System.out.println(
"Parsed with JVM's default locale: " + LocalDateTime.parse(strDateTime, dtfWithDefaultLocale));
// Setting the JVM's default locale to Locale.FRANCE
Locale.setDefault(Locale.FRANCE);
// Using DateTimeFormatter with Locale.ENGLISH explicitly (recommended)
DateTimeFormatter dtfWithEnglishLocale = getDateTimeFormatterWithEnglishLocale();
System.out.println("JVM's Locale: " + Locale.getDefault());
System.out.println("DateTimeFormatter's Locale: " + dtfWithEnglishLocale.getLocale());
LocalDateTime zdt = LocalDateTime.parse(strDateTime, dtfWithEnglishLocale);
System.out.println("Parsed with Locale.ENGLISH: " + zdt);
System.out.println("JVM's Locale: " + Locale.getDefault());
// Using DateTimeFormatter with the default Locale
dtfWithDefaultLocale = getDateTimeFormatterWithDefaultLocale();
System.out.println("DateTimeFormatter's Locale: " + dtfWithDefaultLocale.getLocale());
System.out.println(
"Parsed with JVM's default locale: " + LocalDateTime.parse(strDateTime, dtfWithDefaultLocale));
}
static DateTimeFormatter getDateTimeFormatterWithDefaultLocale() {
return new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendPattern("d MMM uuuu h:m a")
.toFormatter(); // Using default Locale
}
static DateTimeFormatter getDateTimeFormatterWithEnglishLocale() {
return new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendPattern("d MMM uuuu h:m a")
.toFormatter(Locale.ENGLISH); // Using Locale.ENGLISH
}
}
Producción:
JVM's Locale: en_GB
DateTimeFormatter's Locale: en_GB
Parsed with JVM's default locale: 2016-10-24T19:31
JVM's Locale: fr_FR
DateTimeFormatter's Locale: en
Parsed with Locale.ENGLISH: 2016-10-24T19:31
JVM's Locale: fr_FR
DateTimeFormatter's Locale: fr_FR
Exception in thread "main" java.time.format.DateTimeParseException: Text '24 Oct 2016 7:31 pm' could not be parsed at index 3
at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2046)
at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
at java.base/java.time.LocalDateTime.parse(LocalDateTime.java:492)
at Main.main(Main.java:34)
ONLINE DEMO
La siguiente demostración, utilizando SimpleDateFormat
, es solo para completar:
public class Main {
public static void main(String[] args) throws ParseException {
final String strDateTime = "24 Oct 2016 7:31 pm";
SimpleDateFormat sdf = new SimpleDateFormat("d MMM yyyy h:m a", Locale.ENGLISH);
Date date = sdf.parse(strDateTime);
System.out.println(date);
}
}
Producción:
Mon Oct 24 19:31:00 BST 2016
ONLINE DEMO
Nota : Los formateadores predefinidos no aceptan la configuración regional (es decir, funcionan con la configuración regional del sistema Locale
).
Su deptdt
contiene Oct
lo que parece el nombre de un mes en inglés. Pero Locale.getDefault()
probablemente proporciones una configuración regional que no esté en inglés. Reemplácelo por Locale.ENGLISH
o Locale.US
:
SimpleDateFormat dtfmt=new SimpleDateFormat("dd MMM yyyy hh:mm a", Locale.ENGLISH);
Date dt=dtfmt.parse(deptdt);
Probablemente suceda porque la configuración regional predeterminada del teléfono no es el inglés y el nombre del mes en su entrada es ( Oct
).
La solución es utilizar explícitamente la configuración regional en inglés:
SimpleDateFormat dtfmt = new SimpleDateFormat("dd MMM yyyy hh:mm a", Locale.ENGLISH);
Date dt = dtfmt.parse("24 Oct 2016 7:31 pm");
En lugar de trabajar directamente con SimpleDateFormat
(ya que esta antigua API tiene muchos problemas y problemas de diseño ), puede usar ThreeTen Backport , un excelente backport para las nuevas clases de fecha/hora de Java 8. Para usarlo en Android , también necesitarás ThreeTenABP (más información sobre cómo usarlo aquí ).
Las clases principales que se utilizarán son org.threeten.bp.LocalDateTime
(parece la mejor opción, ya que tiene campos de fecha y hora en su entrada) y org.threeten.bp.format.DateTimeFormatter
(para analizar la entrada). También uso java.util.Locale
class para asegurarme de que analiza los nombres de los meses en inglés, y para org.threeten.bp.format.DateTimeFormatterBuilder
asegurarme de que analiza pm
(haga que no distinga entre mayúsculas y minúsculas, ya que el valor predeterminado es PM
):
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// case insensitive to parse "pm"
.parseCaseInsensitive()
// pattern
.appendPattern("dd MMM yyyy h:mm a")
// use English locale to parse month name (Oct)
.toFormatter(Locale.ENGLISH);
// parse input
LocalDateTime dt = LocalDateTime.parse("24 Oct 2016 7:31 pm", fmt);
System.out.println(dt); // 2016-10-24T19:31
La salida será:
2016-10-24T19:31
Si necesita convertir esto a java.util.Date
, puede usar la org.threeten.bp.DateTimeUtils
clase. Pero también necesitas saber qué zona horaria se utilizará para convertir esto. En el siguiente ejemplo, estoy usando "UTC":
Date date = DateTimeUtils.toDate(dt.atZone(ZoneOffset.UTC).toInstant());
Para cambiar a una zona diferente, puedes hacer:
Date date = DateTimeUtils.toDate(dt.atZone(ZoneId.of("Europe/London")).toInstant());
Tenga en cuenta que la API utiliza nombres de zonas horarias de la IANA (siempre en el formato Continent/City
, me gusta America/Sao_Paulo
o Europe/Berlin
). Evite el uso de abreviaturas de 3 letras (como CST
o PST
) porque son ambiguas y no estándar . Para encontrar la zona horaria que mejor se adapte a cada región, utilice el ZoneId.getAvailableZoneIds()
método y compruebe cuál se adapta mejor a sus casos de uso.
PD: el último ejemplo anterior ( dt.atZone(ZoneId.of("Europe/London"))
) creará la fecha/hora 2016-10-24T19:31
en la zona horaria de Londres. Pero si lo que quieres es 2016-10-24T19:31
en UTC y luego convertirlo a otra zona horaria, entonces deberías hacer:
Date date = DateTimeUtils.toDate(dt
// first convert it to UTC
.toInstant(ZoneOffset.UTC)
// then convert to LondonTimezone
.atZone(ZoneId.of("Europe/London")).toInstant());