Analizar cualquier fecha en Java

79

Sé que esta pregunta se hace bastante y, obviamente, no se puede analizar ninguna fecha arbitraria. Sin embargo, encuentro que la biblioteca python-dateutil es capaz de analizar cada fecha que le lanzo, todo mientras no requiere absolutamente ningún esfuerzo para averiguar una cadena de formato de fecha. El tiempo de Joda siempre se vende como un gran analizador de fechas de Java, pero aún requiere que decidas en qué formato está tu fecha antes de elegir un formato (o crear el tuyo propio). No puede simplemente llamar a DateFormatter.parse (mydate) y mágicamente recuperar un objeto Date.

Por ejemplo, la fecha "Wed Mar 04 05:09:06 GMT-06: 00 2009" se analiza correctamente con python-dateutil:

import dateutil.parser
print dateutil.parser.parse('Wed Mar 04 05:09:06 GMT-06:00 2009')

pero la siguiente llamada de tiempo de Joda no funciona:

    String date = "Wed Mar 04 05:09:06 GMT-06:00 2009";
    DateTimeFormatter fmt = ISODateTimeFormat.dateTime();
    DateTime dt = fmt.parseDateTime(date);
    System.out.println(date);

Y crear su propio DateTimeFormatter frustra el propósito, ya que parece ser lo mismo que usar SimpleDateFormatter con la cadena de formato correcta.

¿Existe una forma comparable de analizar una fecha en Java, como python-dateutil? No me importan los errores, solo quiero que sea casi perfecto.

Max
fuente

Respuestas:

107

Lo mejor que puede hacer es pedir ayuda para que las expresiones regulares coincidan con el patrón de formato de fecha y / o hacer fuerza bruta.

Hace varios años escribí una pequeña DateUtilclase tonta que hizo el trabajo. Aquí hay un extracto de relevancia:

private static final Map<String, String> DATE_FORMAT_REGEXPS = new HashMap<String, String>() {{
    put("^\\d{8}$", "yyyyMMdd");
    put("^\\d{1,2}-\\d{1,2}-\\d{4}$", "dd-MM-yyyy");
    put("^\\d{4}-\\d{1,2}-\\d{1,2}$", "yyyy-MM-dd");
    put("^\\d{1,2}/\\d{1,2}/\\d{4}$", "MM/dd/yyyy");
    put("^\\d{4}/\\d{1,2}/\\d{1,2}$", "yyyy/MM/dd");
    put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}$", "dd MMM yyyy");
    put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}$", "dd MMMM yyyy");
    put("^\\d{12}$", "yyyyMMddHHmm");
    put("^\\d{8}\\s\\d{4}$", "yyyyMMdd HHmm");
    put("^\\d{1,2}-\\d{1,2}-\\d{4}\\s\\d{1,2}:\\d{2}$", "dd-MM-yyyy HH:mm");
    put("^\\d{4}-\\d{1,2}-\\d{1,2}\\s\\d{1,2}:\\d{2}$", "yyyy-MM-dd HH:mm");
    put("^\\d{1,2}/\\d{1,2}/\\d{4}\\s\\d{1,2}:\\d{2}$", "MM/dd/yyyy HH:mm");
    put("^\\d{4}/\\d{1,2}/\\d{1,2}\\s\\d{1,2}:\\d{2}$", "yyyy/MM/dd HH:mm");
    put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}\\s\\d{1,2}:\\d{2}$", "dd MMM yyyy HH:mm");
    put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}\\s\\d{1,2}:\\d{2}$", "dd MMMM yyyy HH:mm");
    put("^\\d{14}$", "yyyyMMddHHmmss");
    put("^\\d{8}\\s\\d{6}$", "yyyyMMdd HHmmss");
    put("^\\d{1,2}-\\d{1,2}-\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd-MM-yyyy HH:mm:ss");
    put("^\\d{4}-\\d{1,2}-\\d{1,2}\\s\\d{1,2}:\\d{2}:\\d{2}$", "yyyy-MM-dd HH:mm:ss");
    put("^\\d{1,2}/\\d{1,2}/\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "MM/dd/yyyy HH:mm:ss");
    put("^\\d{4}/\\d{1,2}/\\d{1,2}\\s\\d{1,2}:\\d{2}:\\d{2}$", "yyyy/MM/dd HH:mm:ss");
    put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd MMM yyyy HH:mm:ss");
    put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd MMMM yyyy HH:mm:ss");
}};

/**
 * Determine SimpleDateFormat pattern matching with the given date string. Returns null if
 * format is unknown. You can simply extend DateUtil with more formats if needed.
 * @param dateString The date string to determine the SimpleDateFormat pattern for.
 * @return The matching SimpleDateFormat pattern, or null if format is unknown.
 * @see SimpleDateFormat
 */
public static String determineDateFormat(String dateString) {
    for (String regexp : DATE_FORMAT_REGEXPS.keySet()) {
        if (dateString.toLowerCase().matches(regexp)) {
            return DATE_FORMAT_REGEXPS.get(regexp);
        }
    }
    return null; // Unknown format.
}

(tos, inicialización de doble corsé, tos, era solo para que todo encajara en una longitud máxima de 100 caracteres;))

Puede expandirlo fácilmente usted mismo con nuevos patrones de formato de fecha y expresión regular.

BalusC
fuente
3
¿Qué haces con fechas ambiguas? Por ejemplo, ¿qué 03/04/2010significa: 3 de abril de 2010 o 4 de marzo de 2010?
Jesper
3
Supongo que uno u otro (configurable)
Bozho
3
@Jesper: el /separador se usa comúnmente para denotar MM/dd/yyyy(se usa principalmente en configuraciones regionales de EE. UU. / Inglés). El -separador se usa comúnmente para denotar dd-MM-yyyy(se usa principalmente en configuraciones regionales europeas).
BalusC
3
@Jesper sí, tienes que decidir entre un mes o un día con el formato, de lo contrario, nunca llegarás a ningún lado.
Máximo
3
@kittylyst: Eso es correcto. Aún más, no existe un enfoque a prueba de balas para esto :)
BalusC
52

Hay una bonita biblioteca llamada Natty que creo que se adapta a tus propósitos:

Natty es un analizador de fechas en lenguaje natural escrito en Java. Dada una expresión de fecha, natty aplicará técnicas estándar de traducción y reconocimiento de idiomas para producir una lista de fechas correspondientes con información de sintaxis y análisis opcional.

¡También puedes probarlo en línea !

Cacovsky
fuente
¡Muchas gracias! Realmente parece una gran elección.
Raju Penumatsa
¡Guauu! Estoy muy impresionado con la capacidad de esta biblioteca para analizar cualquier fecha en cualquier formato. Necesita un poco de ayuda con los tiempos de análisis, sin embargo, lo he abordado en esta publicación en SoftwareRecs.SE: softwarerecs.stackexchange.com/questions/26556/…
Michael Plautz
1
esta es sin duda la mejor biblioteca, incluso probé cosas como: "el día antes de navidad de 2012" y la analizó correctamente
jjj
5
Falla con "13/02/2002", me sale el 22 de febrero, no parece muy internacional.
Ricardo Freitas
3
Sí, sorprendentemente Natty no puede manejar formatos de día, mes y año.
ConorD55
7

Lo que he visto es una clase de utilidad de fecha que contiene varios formatos de fecha típicos. Entonces, cuando se llama a DateUtil.parse (fecha), intenta analizar la fecha con cada formato de fecha internamente y solo arroja excepciones si ninguno de los formatos internos puede analizarlo.

Es básicamente un enfoque de fuerza bruta para su problema.

Robert Diana
fuente
Creo que este es el enfoque más sencillo y comprensible. Dado que una cadena de fechas de formato desconocido es ambigua por diseño, poner demasiada "inteligencia" en el intento de reconocer el formato probablemente resulte en resultados más "sorprendentes".
Erich Kitzmueller
Sí, pero creo que hay algunas suposiciones que puede hacer con un poco de información inicial (orden de día / mes / año en una fecha) para analizar correctamente las fechas más sensatas sin una gran tabla de búsqueda.
Máximo
Max, eso es cierto, y lo más probable es que haya un conjunto limitado de formatos de fecha que esté buscando. Puede hacer muy pocas suposiciones sobre el orden del día y el mes sin escribir un motor de análisis de fechas completo. ¿Existe un caso de uso específico para esto, porque eso podría ayudar a orientar a las personas en la dirección correcta? Por ejemplo, la mayoría de los formatos de fecha de varios servicios de redes sociales encajan en unos 10 formatos populares.
Robert Diana
Quizás estoy más interesado en el aspecto de usabilidad. "Analice la mayoría de las fechas sin tener que volver a tratar con una cadena de formato". Creo que realmente solo quiero ver una biblioteca como python-dateutil en Java, lo que supongo que significaría que debería hacerlo si lo quiero tanto.
Máximo
Supongo que nuestras definiciones de usabilidad también son diferentes. La clase de fecha que había visto podía analizar las fechas de alrededor de 30 servicios web diferentes. Usar la clase de fecha fue tan simple como analizar (fecha), por lo que como usuario de la utilidad no tuve que preocuparme por los formatos de fecha. El escritor de la utilidad se ocupó de mí.
Robert Diana
6

Puedes probar el analizador de fechas .

Puede reconocer cualquier Cadena automáticamente y analizarla en Fecha , Calendario , LocalDateTime , OffsetDateTime de forma correcta y rápida ( 1us~1.5us).

No se basa en ninguna natural language analyzero SimpleDateFormato regex.Pattern.

Con él, no tiene que preparar ningún patrón apropiado como yyyy-MM-dd'T'HH:mm:ss.SSSZo yyyy-MM-dd'T'HH:mm:ss.SSSZZ:

Date date = DateParserUtils.parseDate("2015-04-29T10:15:00.500+0000");
Calendar calendar = DateParserUtils.parseCalendar("2015-04-29T10:15:00.500Z");
LocalDateTime dateTime = DateParserUtils.parseDateTime("2015-04-29 10:15:00.500 +00:00");

Todo funciona bien, disfrútalo.

enfurruñarse
fuente
Acabo de echar un vistazo, parece que cubre una amplia variedad de formatos
Sankalp
0

No tengo idea sobre este análisis de cómo hacerlo en Python. En java podemos hacer así

SimpleDateFormat sdf1 = new SimpleDateFormat("dd-MM-yyyy");
  java.util.Date normalDate = null;
  java.sql.Date sqlDate = null;
  normalDate = sdf1.parse(date);
  sqlDate = new java.sql.Date(normalDate.getTime());
  System.out.println(sqlDate);

Creo que, como Java, algunas funciones predefinidas estarán allí en Python. Puedes seguir este método. Estos métodos analizan la fecha de la cadena en Sql Date (dd-MM-aaaa);

import java.text.SimpleDateFormat;
import java.text.ParseException;
public class HelloWorld{
     public static void main(String []args){
        String date ="26-12-2019";
         SimpleDateFormat sdf1 = new SimpleDateFormat("dd-MM-yyyy");
        java.util.Date normalDate = null;
        java.sql.Date sqlDate = null;
        if( !date.isEmpty()) {
            try {
                normalDate = sdf1.parse(date);
                sqlDate = new java.sql.Date(normalDate.getTime());
                System.out.println(sqlDate);
            } catch (ParseException e) {
            }
        }
     }
} 

ejecutar esto!

Shashidhar Reddy
fuente
1
Por favor, no enseñe a los jóvenes a usar la SimpleDateFormatclase obsoleta y notoriamente problemática . Al menos no como primera opción. Y no sin reserva alguna. Hoy tenemos mucho mejor en java.time, la API moderna de fecha y hora de Java, y su DateTimeFormatter.
Ole VV
Si sabemos cómo resolver el problema, buscaremos las últimas actualizaciones. Ahora que tenemos una solución, intentaremos conseguir una mucho mejor. De todos modos, ¡gracias por tu actualización!
Shashidhar Reddy
1
Hay un error tipográfico para mm que representa minutos. Deberíamos usar MM que representa meses.
Shashidhar Reddy
0
//download library:   org.ocpsoft.prettytime.nlp.PrettyTimeParser
String str = "2020.03.03";
Date date = new PrettyTimeParser().parseSyntax(str).get(0).getDates().get(0);
System.out.println(date)
Mahdi
fuente
1
Por favor, ponga su respuesta siempre en contexto en lugar de simplemente pegar el código. Consulte aquí para obtener más detalles.
gehbiszumeis