java.text.ParseException: Fecha no analizable: java.text.DateFormat.parse(DateFormat.java:579)

Resuelto Piotr asked hace 54 años • 0 respuestas

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?

Piotr avatar Jan 01 '70 08:01 Piotr
Aceptado

Especifique siempre a Localecon a SimpleDateFormaty a DateTimeFormatterpara formatos personalizados.

Dado que la fecha y hora proporcionada está en inglés, debe utilizarla Locale.ENGLISHcon 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.utily su API de formato SimpleDateFormatestá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#ofPatternutiliza 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).

Arvind Kumar Avinash avatar Jan 02 '2021 21:01 Arvind Kumar Avinash

Su deptdtcontiene Octlo 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.ENGLISHo Locale.US:

SimpleDateFormat dtfmt=new SimpleDateFormat("dd MMM yyyy hh:mm a", Locale.ENGLISH);
Date dt=dtfmt.parse(deptdt);
Thomas Fritsch avatar Jun 22 '2017 18:06 Thomas Fritsch

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.Localeclass para asegurarme de que analiza los nombres de los meses en inglés, y para org.threeten.bp.format.DateTimeFormatterBuilderasegurarme 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.DateTimeUtilsclase. 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_Pauloo Europe/Berlin). Evite el uso de abreviaturas de 3 letras (como CSTo 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:31en la zona horaria de Londres. Pero si lo que quieres es 2016-10-24T19:31en 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());
 avatar Jun 22 '2017 18:06