Analysieren Sie jedes Datum in Java

78

Ich weiß, dass diese Frage ziemlich oft gestellt wird, und offensichtlich können Sie kein beliebiges Datum analysieren. Ich finde jedoch, dass die Python-Dateutil-Bibliothek in der Lage ist, jedes Datum zu analysieren, das ich darauf wirfe, während es absolut keinen Aufwand erfordert, eine Datumsformatzeichenfolge herauszufinden. Joda-Zeit wird immer als großartiger Java-Datums-Parser verkauft, aber Sie müssen immer noch entscheiden, in welchem ​​Format Ihr Datum vorliegt, bevor Sie ein Format auswählen (oder ein eigenes erstellen). Sie können nicht einfach DateFormatter.parse (mydate) aufrufen und auf magische Weise ein Date-Objekt zurückerhalten.

Zum Beispiel wird das Datum "Mi Mar 04 05:09:06 GMT-06: 00 2009" ordnungsgemäß mit Python-Dateutil analysiert:

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

Der folgende Joda-Zeitaufruf funktioniert jedoch nicht:

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

Das Erstellen eines eigenen DateTimeFormatter macht den Zweck zunichte, da dies mit der Verwendung von SimpleDateFormatter mit der richtigen Formatzeichenfolge identisch zu sein scheint.

Gibt es eine vergleichbare Möglichkeit, ein Datum in Java zu analysieren, wie z. B. Python-Dateutil? Fehler sind mir egal, ich möchte nur, dass sie größtenteils perfekt sind.

Max
quelle

Antworten:

107

Ihre beste Wette ist es, wirklich um Hilfe zu bitten, um eine Regex zu erstellen, die dem Datumsformatmuster entspricht, und / oder um Brute Forcing durchzuführen.

Vor einigen Jahren schrieb ich eine kleine dumme DateUtilKlasse, die den Job machte. Hier ist ein relevanter Auszug:

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.
}

(Husten, Initialisierung der doppelten Zahnspange, Husten, es war nur, um alles in eine maximale Länge von 100 Zeichen zu bringen;))

Sie können es ganz einfach selbst mit neuen Regex- und Datumsformatmustern erweitern.

BalusC
quelle
3
Was machst du mit mehrdeutigen Daten? Was bedeutet zum Beispiel 03/04/2010- 3. April 2010 oder 4. März 2010?
Jesper
3
Ich nehme an, der eine oder andere (konfigurierbar)
Bozho
3
@Jesper: Das /Trennzeichen wird häufig zur Bezeichnung verwendet MM/dd/yyyy(hauptsächlich in US / Englisch-Gebietsschemas). Das -Trennzeichen wird üblicherweise zur Bezeichnung verwendet dd-MM-yyyy(hauptsächlich in europäischen Ländern verwendet).
BalusC
3
@Jesper Ja, Sie müssen sich zwischen einem Monat oder einem Tag mit dem Format entscheiden, sonst kommen Sie nie weiter.
Max
3
@kittylyst: Das stimmt.
Darüber hinaus
52

Es gibt eine schöne Bibliothek namens Natty, die meiner Meinung nach zu Ihren Zwecken passt:

Natty ist ein in Java geschriebener Datums-Parser in natürlicher Sprache. Bei einem Datumsausdruck wendet natty Standardtechniken zur Spracherkennung und -übersetzung an, um eine Liste der entsprechenden Datumsangaben mit optionalen Analyse- und Syntaxinformationen zu erstellen.

Sie können es auch online ausprobieren !

Cacovsky
quelle
Danke vielmals! Es scheint wirklich eine gute Wahl zu sein.
Raju Penumatsa
Beeindruckend! Ich bin sehr beeindruckt von der Fähigkeit dieser Bibliothek, jedes Datum in jedem Format zu analysieren. Es braucht ein wenig Hilfe beim Parsen von Zeiten, aber ich habe das in diesem Beitrag auf SoftwareRecs.SE angesprochen: softwarerecs.stackexchange.com/questions/26556/…
Michael Plautz
1
Dies ist zweifellos die beste Bibliothek. Ich habe sogar Dinge wie "am Tag vor Weihnachten 2012"
ausprobiert
5
Es scheitert mit "13/02/2002", ich bekomme 22. Februar, nicht sehr international, wie es scheint.
Ricardo Freitas
3
Ja, erstaunlicherweise kann Natty nicht mit Tag-Monat-Jahr-Formaten umgehen.
ConorD55
7

Was ich gesehen habe, ist eine Date util-Klasse, die mehrere typische Datumsformate enthält. Wenn DateUtil.parse (Datum) aufgerufen wird, versucht es, das Datum mit jedem Datumsformat intern zu analysieren, und löst nur dann Ausnahmen aus, wenn keines der internen Formate es analysieren kann.

Es ist im Grunde ein Brute-Force-Ansatz für Ihr Problem.

Robert Diana
quelle
Ich denke, dies ist der einfachste und verständlichste Ansatz. Da eine Datumszeichenfolge mit unbekanntem Format von Natur aus nicht eindeutig ist, führt die Verwendung von zu viel "Intelligenz" beim Versuch, das Format zu erkennen, wahrscheinlich zu "überraschenderen" Ergebnissen.
Erich Kitzmüller
Ja, aber ich denke, es gibt einige Annahmen, die Sie anhand einiger Startinformationen (Reihenfolge von Tag / Monat / Jahr in einem Datum) treffen können, um die meisten vernünftigen Daten ohne eine große Nachschlagetabelle korrekt zu analysieren.
Max
Max, das stimmt, und höchstwahrscheinlich gibt es eine begrenzte Anzahl von Datumsformaten, nach denen Sie suchen würden. Sie können nur sehr wenige Annahmen über die Reihenfolge von Tag und Monat treffen, ohne eine vollständige Datumsanalyse-Engine zu schreiben. Gibt es dafür einen bestimmten Anwendungsfall, da dies dazu beitragen könnte, Menschen in die richtige Richtung zu weisen. Beispielsweise passen die meisten Datumsformate von verschiedenen Social-Media-Diensten in etwa 10 beliebte Formate.
Robert Diana
Vielleicht interessiert mich der Usability-Aspekt mehr. "Analysieren Sie die meisten Daten, ohne sich jemals wieder mit einer Formatzeichenfolge zu befassen". Ich denke, ich möchte wirklich nur eine Bibliothek wie Python-Dateutil in Java sehen, was wohl bedeuten würde, dass ich es schaffen sollte, wenn ich es so sehr will!
Max
Ich denke, unsere Definitionen von Usability sind auch unterschiedlich. Die Datumsklasse, die ich gesehen hatte, konnte Daten von ungefähr 30 verschiedenen Webdiensten analysieren. Die Verwendung der Datumsklasse war so einfach wie das Parsen (Datum), sodass ich mich als Benutzer des Dienstprogramms nicht um Datumsformate kümmern musste. Der Verfasser des Dienstprogramms hat sich um mich gekümmert.
Robert Diana
6

Sie könnten Dateparser versuchen .

Es kann jeden String automatisch erkennen und ihn korrekt und schnell in Date , Calendar , LocalDateTime , OffsetDateTime analysieren ( 1us~1.5us).

Es basiert nicht auf einem natural language analyzeroder SimpleDateFormatoder regex.Pattern.

Damit müssen Sie keine geeigneten Muster wie yyyy-MM-dd'T'HH:mm:ss.SSSZoder vorbereiten 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");

Alles funktioniert gut, bitte genießen Sie es.

Sulin
quelle
Habe
0

Ich habe keine Ahnung, wie man in Python analysiert. In Java können wir das so machen

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

Ich denke, wie Java werden einige vordefinierte Funktionen in Python vorhanden sein. Sie können dieser Methode folgen. Diese Methode analysiert das String-Datum in SQL-Datum (TT-MM-JJJJ).

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) {
            }
        }
     }
} 

Führen Sie dies aus!

Shashidhar Reddy
quelle
1
Bitte bringen Sie den Jungen nicht bei, die lange veraltete und notorisch problematische SimpleDateFormatKlasse zu benutzen . Zumindest nicht als erste Option. Und das nicht ohne Vorbehalt. Heute haben wir so viel besser in java.timeder modernen Java-API für Datum und Uhrzeit und deren DateTimeFormatter.
Ole VV
Wenn wir wissen, wie wir das Problem lösen können, werden wir uns die neuesten Updates ansehen. Jetzt haben wir eine Lösung, wir werden versuchen, eine viel bessere zu bekommen. Trotzdem danke für dein Update!
Shashidhar Reddy
1
Es gibt einen Tippfehler für mm, der Minuten darstellt. Wir sollten MM verwenden, das Monate darstellt.
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
quelle
1
Bitte setzen Sie Ihre Antwort immer in einen Kontext, anstatt nur Code einzufügen. Sehen Sie hier für weitere Details.
gehbiszumeis