it-swarm.com.de

So überprüfen Sie ein Datum in Java

Ich finde es merkwürdig, dass der offensichtlichste Weg, Date-Objekte in Java zu erstellen, verworfen wurde und anscheinend durch einen nicht so offensichtlichen, durch den Nachgiebigkeitskalender verwendeten "ersetzt" wurde.

Wie prüfen Sie, ob ein Datum, das als Kombination aus Tag, Monat und Jahr angegeben ist, ein gültiges Datum ist?

Zum Beispiel wäre 2008-02-31 (wie in JJJJ-MM-TT) ein ungültiges Datum.

61
Bloodboiler

Die aktuelle Methode ist die Verwendung der Kalenderklasse. Es verfügt über die setLenient - Methode, die das Datum und den Wurf sowie die Ausnahme überprüft, wenn sie sich wie in Ihrem Beispiel außerhalb des gültigen Bereichs befindet. 

Vergessen, hinzuzufügen: Wenn Sie eine Kalenderinstanz erhalten und die Uhrzeit anhand Ihres Datums einstellen, erhalten Sie die Validierung.

Calendar cal = Calendar.getInstance();
cal.setLenient(false);
cal.setTime(yourDate);
try {
    cal.getTime();
}
catch (Exception e) {
  System.out.println("Invalid date");
}
27
AdamC

Schlüssel ist df.setLenient (false);. Für einfache Fälle ist dies mehr als ausreichend. Wenn Sie nach stabileren (ich bezweifeln) und/oder alternativen Bibliotheken wie joda-time suchen, dann schauen Sie sich die Antwort des Benutzers "tardate" an

final static String DATE_FORMAT = "dd-MM-yyyy";

public static boolean isDateValid(String date) 
{
        try {
            DateFormat df = new SimpleDateFormat(DATE_FORMAT);
            df.setLenient(false);
            df.parse(date);
            return true;
        } catch (ParseException e) {
            return false;
        }
}
64

Wie @Maglob zeigt, besteht der grundlegende Ansatz darin, die Konvertierung von String zu Datum mithilfe von SimpleDateFormat.parse zu testen. Das wird ungültige Tag/Monat-Kombinationen wie 2008-02-31 fangen.

In der Praxis ist dies jedoch selten genug, da SimpleDateFormat.parse überaus liberal ist. Es gibt zwei Verhaltensweisen, die Sie betreffen könnten:

Ungültige Zeichen in der Datumszeichenfolge Überraschenderweise wird 2008-02-2x als gültiges Datum mit dem Gebietsschema format = "yyyy-MM-dd" "übergeben". Auch wenn isLenient == false ist.

Jahre: 2, 3 oder 4 Ziffern? Möglicherweise möchten Sie auch vierstellige Jahreszahlen durchsetzen, anstatt das standardmäßige SimpleDateFormat-Verhalten zuzulassen (das "12-02-31" unterschiedlich interpretiert, abhängig davon, ob Ihr Format "JJJJ-MM-TT" oder "JJ" war -MM-dd ")

Eine strikte Lösung mit der Standardbibliothek

So könnte ein vollständiger String zum Datumstest so aussehen: eine Kombination aus Regex-Übereinstimmung und dann eine erzwungene Datumsumwandlung. Der Trick mit dem Regex besteht darin, ihn für das Gebietsschema geeignet zu machen.

  Date parseDate(String maybeDate, String format, boolean lenient) {
    Date date = null;

    // test date string matches format structure using regex
    // - weed out illegal characters and enforce 4-digit year
    // - create the regex based on the local format string
    String reFormat = Pattern.compile("d+|M+").matcher(Matcher.quoteReplacement(format)).replaceAll("\\\\d{1,2}");
    reFormat = Pattern.compile("y+").matcher(reFormat).replaceAll("\\\\d{4}");
    if ( Pattern.compile(reFormat).matcher(maybeDate).matches() ) {

      // date string matches format structure, 
      // - now test it can be converted to a valid date
      SimpleDateFormat sdf = (SimpleDateFormat)DateFormat.getDateInstance();
      sdf.applyPattern(format);
      sdf.setLenient(lenient);
      try { date = sdf.parse(maybeDate); } catch (ParseException e) { }
    } 
    return date;
  } 

  // used like this:
  Date date = parseDate( "21/5/2009", "d/M/yyyy", false);

Beachten Sie, dass der reguläre Ausdruck davon ausgeht, dass die Formatzeichenfolge nur Tag, Monat, Jahr und Trennzeichen enthält. Abgesehen davon kann das Format in jedem Gebietsschemaformat vorliegen: "d/MM/yy", "yyyy-MM-dd" und so weiter. Die Formatzeichenfolge für das aktuelle Gebietsschema kann folgendermaßen abgerufen werden:

Locale locale = Locale.getDefault();
SimpleDateFormat sdf = (SimpleDateFormat)DateFormat.getDateInstance(DateFormat.SHORT, locale );
String format = sdf.toPattern();

Joda Time - bessere Alternative?

Ich habe vor kurzem von joda time gehört und dachte, ich würde vergleichen. Zwei Punkte:

  1. Anders als SimpleDateFormat scheint es besser zu sein, ungültige Zeichen in der Datumszeichenfolge strikt einzuhalten
  2. Es kann noch kein Weg gefunden werden, um 4-stellige Jahre damit zu erzwingen (aber ich denke, Sie könnten Ihren eigenen DateTimeFormatter für diesen Zweck erstellen)

Es ist ganz einfach zu benutzen:

import org.joda.time.format.*;
import org.joda.time.DateTime;

org.joda.time.DateTime parseDate(String maybeDate, String format) {
  org.joda.time.DateTime date = null;
  try {
    DateTimeFormatter fmt = DateTimeFormat.forPattern(format);
    date =  fmt.parseDateTime(maybeDate);
  } catch (Exception e) { }
  return date;
}
45
tardate

Sie können SimpleDateFormat verwenden.

Zum Beispiel etwas wie:

boolean isLegalDate(String s) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    sdf.setLenient(false);
    return sdf.parse(s, new ParsePosition(0)) != null;
}
36
Maglob

Java.time

Mit der Date and Time-API ( Java.time - Klassen), die in Java 8 und höher integriert ist, können Sie die LocalDate -Klasse verwenden.

public static boolean isDateValid(int year, int month, int day) {
    boolean dateIsValid = true;
    try {
        LocalDate.of(year, month, day);
    } catch (DateTimeException e) {
        dateIsValid = false;
    }
    return dateIsValid;
}
12
Matthias Braun

tl; dr

Verwenden Sie den strengen Modus auf Java.time.DateTimeFormatter , um einen LocalDate zu parsen. Falle für die DateTimeParseException .

LocalDate.parse(                   // Represent a date-only value, without time-of-day and without time zone.
    "31/02/2000" ,                 // Input string.
    DateTimeFormatter              // Define a formatting pattern to match your input string.
    .ofPattern ( "dd/MM/uuuu" )
    .withResolverStyle ( ResolverStyle.STRICT )  // Specify leniency in tolerating questionable inputs.
)

Nach dem Parsen prüfen Sie möglicherweise nach einem angemessenen Wert. Zum Beispiel ein Geburtsdatum innerhalb der letzten einhundert Jahre.

birthDate.isAfter( LocalDate.now().minusYears( 100 ) )

Vermeiden Sie alte Datumszeitklassen

Vermeiden Sie die Verwendung der problematischen alten Datums-/Uhrzeit-Klassen, die mit den ersten Java-Versionen ausgeliefert wurden. Jetzt durch die Klassen Java.time ersetzt.

LocalDate & DateTimeFormatter & ResolverStyle

Die Klasse LocalDate steht für einen Datumswert ohne Uhrzeit und ohne Zeitzone.

String input = "31/02/2000";
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "dd/MM/uuuu" );
try {
    LocalDate ld = LocalDate.parse ( input , f );
    System.out.println ( "ld: " + ld );
} catch ( DateTimeParseException e ) {
    System.out.println ( "ERROR: " + e );
}

Die Klasse Java.time.DateTimeFormatter kann so eingestellt werden, dass Zeichenfolgen mit einem der drei in der Aufzählung ResolverStyle definierten Kronzeugenmodi analysiert werden. Wir fügen eine Zeile in den obigen Code ein, um jeden der Modi auszuprobieren.

f = f.withResolverStyle ( ResolverStyle.LENIENT );

Die Ergebnisse:

  • ResolverStyle.LENIENT
    ld: 2000-03-02
  • ResolverStyle.SMART
    ld: 2000-02-29
  • ResolverStyle.STRICT
    FEHLER: Java.time.format.DateTimeParseException: Text '31/02/2000 'konnte nicht analysiert werden: Ungültiges Datum' FEBRUARY 31 '

Im Modus ResolverStyle.LENIENT wird das ungültige Datum um eine entsprechende Anzahl von Tagen nach vorne verschoben. Im Modus ResolverStyle.SMART (Standardeinstellung) wird die logische Entscheidung getroffen, das Datum innerhalb des Monats zu halten und den letzten möglichen Tag des Monats, den 29. Februar in einem Schaltjahr, zu wählen, da es keinen 31. Tag gibt in diesem Monat Der Modus ResolverStyle.STRICT löst eine Ausnahme aus und beschwert sich, dass es kein Datum gibt.

Alle drei davon sind in Abhängigkeit von Ihrem geschäftlichen Problem und Ihren Richtlinien angemessen. Klingt wie in Ihrem Fall, dass der strikte Modus das ungültige Datum ablehnt, anstatt es anzupassen.


Über Java.time

Das Framework Java.time ist in Java 8 und höher integriert. Diese Klassen ersetzen die störenden alten Legacy Datum-Uhrzeit-Klassen, wie Java.util.Date , Calendar , & SimpleDateFormat .

Das Projekt Joda-Time , das sich jetzt im Wartungsmodus befindet, empfiehlt die Migration zu den Klassen Java.time .

Weitere Informationen finden Sie unter Oracle Tutorial . In Stack Overflow finden Sie viele Beispiele und Erklärungen. Die Spezifikation lautet JSR 310 .

Sie können Java.time - Objekte direkt mit Ihrer Datenbank austauschen. Verwenden Sie einen JDBC-Treiber , der mit JDBC 4.2 oder höher kompatibel ist. Keine Notwendigkeit für Zeichenfolgen, keine Notwendigkeit für Java.sql.*-Klassen.

Wo erhalten Sie die Java.time-Klassen? 

  • Java SE 8 , Java SE 9 und später
    • Eingebaut. 
    • Teil der Standard-Java-API mit einer mitgelieferten Implementierung.
    • Java 9 fügt einige kleinere Funktionen und Korrekturen hinzu.
  • Java SE 6 und Java SE 7
    • Ein Großteil der Java.time-Funktionalität wird in ThreeTen-Backport auf Java 6 & 7 zurückportiert.
  • Android
    • Spätere Versionen von Android enthalten Implementierungen der Java.time-Klassen.
    • Für frühere Android-Projekte (<26) passt das Projekt ThreeTenABPThreeTen-Backport (oben erwähnt) an. Siehe Verwendung von ThreeTenABP… .

Das Projekt ThreeTen-Extra erweitert Java.time um zusätzliche Klassen. Dieses Projekt ist ein Testfeld für mögliche zukünftige Erweiterungen von Java.time. Hier finden Sie nützliche Klassen wie Interval , YearWeek , YearQuarter und mehr .

11
Basil Bourque

Eine alternative Lösung, die die Standardbibliothek verwendet, besteht darin, Folgendes auszuführen:

1) Erstellen Sie ein striktes SimpleDateFormat anhand Ihres Musters

2) Versuchen Sie, den vom Benutzer eingegebenen Wert mit dem Formatobjekt zu analysieren

3) Formatieren Sie bei Erfolg das aus (2) resultierende Datum mit demselben Datumsformat (aus (1)).

4) Vergleichen Sie das neu formatierte Datum mit dem vom Benutzer eingegebenen ursprünglichen Wert. Wenn sie gleich sind, stimmt der eingegebene Wert genau mit Ihrem Muster überein.

Auf diese Weise müssen Sie keine komplexen regulären Ausdrücke erstellen. In meinem Fall musste ich die gesamte Mustersyntax von SimpleDateFormat unterstützen, anstatt auf bestimmte Typen wie Tage, Monate und Jahre beschränkt zu sein.

6
Ben

Aufbauend auf der Antwort von @Pangea , um das von @ceklock angegebene Problem zu beheben, habe ich eine Methode hinzugefügt, um zu überprüfen, ob die dateString kein ungültiges Zeichen enthält.

So mache ich:

private boolean isDateCorrect(String dateString) {
    try {
        Date date = mDateFormatter.parse(dateString);
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return matchesOurDatePattern(dateString);    //added my method
    }
    catch (ParseException e) {
        return false;
    }
}

/**
 * This will check if the provided string matches our date format
 * @param dateString
 * @return true if the passed string matches format 2014-1-15 (YYYY-MM-dd)
 */
private boolean matchesDatePattern(String dateString) {
    return dateString.matches("^\\d+\\-\\d+\\-\\d+");
}
6
Sufian

Ich schlage vor, dass Sie die Klasse org.Apache.commons.validator.GenericValidator von Apache verwenden.

GenericValidator.isDate(String value, String datePattern, boolean strict);

Hinweis: strict - Gibt an, ob eine genaue Übereinstimmung mit dem datePattern vorliegen soll.

4
Ziya

Ich denke, am einfachsten ist es, einen String in ein Date-Objekt zu konvertieren und ihn wieder in einen String zu konvertieren. Die angegebene Datumszeichenfolge ist in Ordnung, wenn beide Zeichenfolgen noch übereinstimmen.

public boolean isDateValid(String dateString, String pattern)
{   
    try
    {
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        if (sdf.format(sdf.parse(dateString)).equals(dateString))
            return true;
    }
    catch (ParseException pe) {}

    return false;
}
3
mawa

Angenommen, beide sind Strings (sonst wären sie bereits gültige Daten), hier ist eine Möglichkeit:

package cruft;

import Java.text.DateFormat;
import Java.text.ParseException;
import Java.text.SimpleDateFormat;
import Java.util.Date;

public class DateValidator
{
    private static final DateFormat DEFAULT_FORMATTER;

    static
    {
        DEFAULT_FORMATTER = new SimpleDateFormat("dd-MM-yyyy");
        DEFAULT_FORMATTER.setLenient(false);
    }

    public static void main(String[] args)
    {
        for (String dateString : args)
        {
            try
            {
                System.out.println("arg: " + dateString + " date: " + convertDateString(dateString));
            }
            catch (ParseException e)
            {
                System.out.println("could not parse " + dateString);
            }
        }
    }

    public static Date convertDateString(String dateString) throws ParseException
    {
        return DEFAULT_FORMATTER.parse(dateString);
    }
}

Hier ist die Ausgabe, die ich bekomme:

Java cruft.DateValidator 32-11-2010 31-02-2010 04-01-2011
could not parse 32-11-2010
could not parse 31-02-2010
arg: 04-01-2011 date: Tue Jan 04 00:00:00 EST 2011

Process finished with exit code 0

Wie Sie sehen, werden beide Fälle gut behandelt.

2
duffymo

Das funktioniert großartig für mich. Ansatz, der oben von Ben vorgeschlagen wurde. 

private static boolean isDateValid(String s) {
    SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
    try {
        Date d = asDate(s);
        if (sdf.format(d).equals(s)) {
            return true;
        } else {
            return false;
        }
    } catch (ParseException e) {
        return false;
    }
}
2
Dresden Sparrow

Zwei Kommentare zur Verwendung von SimpleDateFormat.

es sollte als statische Instanz deklariert werden Wenn als statischer Zugriff deklariert, sollte es synchronisiert werden, da es nicht threadsicher ist

IME ist besser als das Instanziieren einer Instanz für jede Analyse eines Datums.

1
Tom

Bei den oben genannten Methoden zum Datums-Parsing handelt es sich um Nizza. Ich habe nur neue Überprüfungen in vorhandenen Methoden hinzugefügt, die das konvertierte Datum mit dem ursprünglichen Datum unter Verwendung des Formatierers überprüfen, sodass es für fast jeden Fall funktioniert. z.B. 29.02.2013 ist ungültiges Datum . Die angegebene Funktion parst das Datum gemäß den aktuellen akzeptablen Datumsformaten. Es wird true zurückgegeben, wenn das Datum nicht erfolgreich analysiert wurde.

 public final boolean validateDateFormat(final String date) {
        String[] formatStrings = {"MM/dd/yyyy"};
        boolean isInvalidFormat = false;
        Date dateObj;
        for (String formatString : formatStrings) {
            try {
                SimpleDateFormat sdf = (SimpleDateFormat) DateFormat.getDateInstance();
                sdf.applyPattern(formatString);
                sdf.setLenient(false);
                dateObj = sdf.parse(date);
                System.out.println(dateObj);
                if (date.equals(sdf.format(dateObj))) {
                    isInvalidFormat = false;
                    break;
                }
            } catch (ParseException e) {
                isInvalidFormat = true;
            }
        }
        return isInvalidFormat;
    }
0
Imran

sieht aus wie SimpleDateFormat überprüft das Muster auch nach nicht streng Die Methode setLenient (false); wird darauf angewendet, daher habe ich die folgende Methode verwendet, um zu überprüfen, ob das eingegebene Datum ein gültiges Datum ist oder nicht, wie im angegebenen Muster angegeben.

import Java.time.format.DateTimeFormatter;
import Java.time.format.DateTimeParseException;
public boolean isValidFormat(String dateString, String pattern) {
    boolean valid = true;
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
    try {
        formatter.parse(dateString);
    } catch (DateTimeParseException e) {
        valid = false;
    }
    return valid;
}
0
vijay
        public static String detectDateFormat(String inputDate, String requiredFormat) {
        String tempDate = inputDate.replace("/", "").replace("-", "").replace(" ", "");
        String dateFormat;

        if (tempDate.matches("([0-12]{2})([0-31]{2})([0-9]{4})")) {
            dateFormat = "MMddyyyy";
        } else if (tempDate.matches("([0-31]{2})([0-12]{2})([0-9]{4})")) {
            dateFormat = "ddMMyyyy";
        } else if (tempDate.matches("([0-9]{4})([0-12]{2})([0-31]{2})")) {
            dateFormat = "yyyyMMdd";
        } else if (tempDate.matches("([0-9]{4})([0-31]{2})([0-12]{2})")) {
            dateFormat = "yyyyddMM";
        } else if (tempDate.matches("([0-31]{2})([a-z]{3})([0-9]{4})")) {
            dateFormat = "ddMMMyyyy";
        } else if (tempDate.matches("([a-z]{3})([0-31]{2})([0-9]{4})")) {
            dateFormat = "MMMddyyyy";
        } else if (tempDate.matches("([0-9]{4})([a-z]{3})([0-31]{2})")) {
            dateFormat = "yyyyMMMdd";
        } else if (tempDate.matches("([0-9]{4})([0-31]{2})([a-z]{3})")) {
            dateFormat = "yyyyddMMM";
        } else {
            return "Pattern Not Added";
//add your required regex
        }
        try {
            String formattedDate = new SimpleDateFormat(requiredFormat, Locale.ENGLISH).format(new SimpleDateFormat(dateFormat).parse(tempDate));

            return formattedDate;
        } catch (Exception e) {
            //
            return "";
        }

    }
0
Suheb Rafique

Hier würde ich das Datumsformat überprüfen:

 public static boolean checkFormat(String dateTimeString) {
    return dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}") || dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}")
            || dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}") || dateTimeString
            .matches("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z") ||
            dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}Z");
}
0
Dinesh Bhatia

Folgendes habe ich für die Knotenumgebung ohne externe Bibliotheken getan:

Date.prototype.yyyymmdd = function() {
   var yyyy = this.getFullYear().toString();
   var mm = (this.getMonth()+1).toString(); // getMonth() is zero-based
   var dd  = this.getDate().toString();
   return zeroPad([yyyy, mm, dd].join('-'));  
};

function zeroPad(date_string) {
   var dt = date_string.split('-');
   return dt[0] + '-' + (dt[1][1]?dt[1]:"0"+dt[1][0]) + '-' + (dt[2][1]?dt[2]:"0"+dt[2][0]);
}

function isDateCorrect(in_string) {
   if (!matchesDatePattern) return false;
   in_string = zeroPad(in_string);
   try {
      var idate = new Date(in_string);
      var out_string = idate.yyyymmdd();
      return in_string == out_string;
   } catch(err) {
      return false;
   }

   function matchesDatePattern(date_string) {
      var dateFormat = /[0-9]+-[0-9]+-[0-9]+/;
      return dateFormat.test(date_string); 
   }
}

Und hier ist, wie man es benutzt:

isDateCorrect('2014-02-23')
true
0
TennisVisuals
// to return valid days of month, according to month and year
int returnDaysofMonth(int month, int year) {
    int daysInMonth;
    boolean leapYear;
    leapYear = checkLeap(year);
    if (month == 4 || month == 6 || month == 9 || month == 11)
        daysInMonth = 30;
    else if (month == 2)
        daysInMonth = (leapYear) ? 29 : 28;
    else
        daysInMonth = 31;
    return daysInMonth;
}

// to check a year is leap or not
private boolean checkLeap(int year) {
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.YEAR, year);
    return cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365;
}
0

setLenient auf false, wenn Sie eine strikte Validierung wünschen

public boolean isThisDateValid(String dateToValidate, String dateFromat){

    if(dateToValidate == null){
        return false;
    }

    SimpleDateFormat sdf = new SimpleDateFormat(dateFromat);
    sdf.setLenient(false);

    try {

        //if not valid, it will throw ParseException
        Date date = sdf.parse(dateToValidate);
        System.out.println(date);

    } catch (ParseException e) {

        e.printStackTrace();
        return false;
    }

    return true;
}
0
Pravin