Frage

Ich finde es merkwürdig, dass der naheliegendste Weg Date Objekte in Java ist veraltet zu erstellen und erscheint „substituierte“ zu haben mit einem nicht so offensichtlich nachsichtig Kalender zu verwenden.

Wie überprüfen Sie, dass ein Datum, als eine Kombination von Tag gegeben, Monat und Jahr, ein gültiges Datum ist?

Zum Beispiel 2008.02.31 (wie in yyyy-mm-dd) würde ein ungültiges Datum sein.

War es hilfreich?

Lösung

Die aktuelle Art und Weise ist die Kalender-Klasse zu verwenden. Es hat die setLenient Methode, die das Datum und die wirft und Ausnahme wird überprüfen, ob es außerhalb des Bereichs, wie in Ihrem Beispiel.

vergessen hinzuzufügen: Wenn Sie einen Kalender Instanz erhalten und die Zeit mit Ihrem Datum festgelegt, das ist, wie Sie die Validierung erhalten.

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

Andere Tipps

Key ist df.setLenient (false); . Das ist mehr als genug für einfache Fälle. Wenn Sie für eine robustere suchen (ich bezweifle) und / oder alternative Bibliotheken wie joda-Zeit schaut dann auf der Antwort durch den Benutzer " tardate "

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

Wie @Maglob gezeigt, der grundlegende Ansatz ist die Umwandlung von Zeichenfolge auf dem Laufenden zu testen, mit SimpleDateFormat.parse . Das wird ungültig Tag / Monat Kombinationen wie 2008.02.31 fängt.

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

Ungültige Zeichen in der Zeichenkette für das Datum Überraschenderweise 2008-02-2x wird "pass" als gültiges Datum mit locale format = "yyyy-MM-dd" zum Beispiel. Selbst wenn isLenient == false.

Jahre: 2, 3 oder 4 Ziffern Sie können auch eher 4-stellige Jahre erzwingen, als das Standard-Simple Verhalten ermöglicht (die „12-02-31“ unterschiedlich in Abhängigkeit davon interpretiert, ob Ihr Format „yyyy-MM-dd“ oder „JJ-MM-dd“ war )

Eine strikte Lösung mit der Standardbibliothek

So eine vollständige Zeichenfolge bisher Test könnte wie folgt aussehen: eine Kombination von Regex, und dann einer Zwangsdatumsumstellung. Der Trick mit dem regex ist es locale freundlich 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 Regex des Format-String geht davon enthält nur Tag, Monat, Jahr und Trennzeichen. Abgesehen davon, kann in jedem Format locale Format sein: "d / MM / JJ", "yyyy-MM-DD", und so weiter. Das Format-String für die aktuelle locale könnte wie folgt erhalten werden:

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

Joda Time - bessere Alternative

?

Ich habe Anhörung über joda Zeit vor kurzem und dachte, ich würde vergleichen. Zwei Punkte:

  1. Es scheint besser zu streng über ungültige Zeichen in der Datumszeichenfolge sein, im Gegensatz zu Simple
  2. kann nicht sehen, wie man 4-stellige Jahre erzwingen damit noch (aber ich denke, Sie Ihre eigenen DateTimeFormatter für diesen Zweck)

Es ist ganz einfach zu bedienen:

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

Sie können mit Simple

Zum Beispiel so etwas wie:

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

tl; dr

Mit dem Strict-Modus java.time.DateTimeFormatter analysieren ein LocalDate . Falle für den 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, können Sie für einen vernünftigen Wert überprüfen. Zum Beispiel kann ein Geburtsdatum innerhalb der letzten 100 Jahre.

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

Legacy-Datum-Zeit-Klassen vermeiden

Vermeiden Sie die lästigen alten Datum Zeitklassen mit den ersten Versionen von Java ausgeliefert werden. Nun verdrängte durch die java.time Klassen.

LocalDate & DateTimeFormatter & ResolverStyle

Die LocalDate Klasse repräsentiert ein Datum -nur Wert ohne Zeit-of-Tag 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 java.time.DateTimeFormatter Klasse können eingestellt werden a href definiert analysieren Strings mit einem der drei Modi Milde im <= „http://docs.oracle.com/javase/8/docs/api/java/time/format/ResolverStyle.html“ rel =“ noreferrer "> ResolverStyle enum. Wir fügen eine Zeile in den obigen Code jeden der Modi zu versuchen.

f = f.withResolverStyle ( ResolverStyle.LENIENT );

Die Ergebnisse:

  • ResolverStyle.LENIENT
    ld: 2000-03-02
  • ResolverStyle.SMART
    ld: 2000-02-29
  • ResolverStyle.STRICT
    ERROR: java.time.format.DateTimeParseException: Text '31 / 02 / 2000' konnte nicht analysiert werden: Datum ungültig '31. Februar'

Wir können sehen, dass in ResolverStyle.LENIENT Modus wird das ungültige Datum vorwärts bewegt eine äquivalente Anzahl von Tagen. In ResolverStyle.SMART Modus (der Standard), wird eine logische Entscheidung, das Datum innerhalb des Monats und geht mit dem letzten möglichen Tag des Monats, 29. Februar in einem Schaltjahr zu halten gemacht, da es in diesem Monat keinen 31. Tag ist. Die ResolverStyle.STRICT Modus wirft ein Ausnahme beschwert, dass es kein solches Datum.

Alle drei sind angemessen abhängig von Ihrem Business-Problem und die Politik. Klingt wie in Ihrem Fall, dass Sie der Strict-Modus wollen lieber das ungültige Datum abzulehnen, als es anzupassen.


über java.time

Die java.time Rahmen in Java 8 gebaut und später. Diese Klassen verdrängen die lästigen alten Vermächtnis Datum Zeitklassen wie java.util.Date , Calendar & SimpleDateFormat .

Die Joda-Time Projekt jetzt in Wartungsmodus , rät Migration auf die java.time Klassen.

Um mehr zu erfahren, finden Sie in der Oracle Tutorial . Und Stack-Überlauf suchen viele Beispiele und Erklärungen. Spezifikation JSR 310 .

können Sie tauschen java.time Objekte direkt mit Ihrer Datenbank. Verwenden Sie einen JDBC-Treiber kompatibel mit JDBC 4.2 oder höher. Keine Notwendigkeit für Streicher, keine Notwendigkeit für java.sql.* Klassen.

Wo die java.time Klassen erhalten?

Das ThreeTen-Extra Projekt erstreckt java.time mit zusätzliche Klassen. Dieses Projekt ist eine Bewährungsprobe für eine mögliche zukünftige Ergänzungen java.time. Sie können einige nützliche Klassen hier wie Interval YearWeek , YearQuarter und mehr .

java.time

Mit der Datum und Uhrzeit API ( java.time Klassen) 8 in Java gebaut und später, können Sie die LocalDate Klasse.

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

Eine alternative strenge Lösung der Standard-Bibliothek ist folgendes auszuführen:

1) Erstellen Sie eine strenge Simple mit Ihrem Muster

2) versuchen, die Benutzer eingegebenen Wert mit dem Formatobjekt parsen

3) Wenn dies gelingt, formatieren Sie das Datum aus (2) das gleiche Datumsformat (von (1))

4) Vergleichen Sie das neu formatierte Datum gegenüber dem ursprünglichen, vom Benutzer eingegebene Wert. Wenn sie gleich sind, dann die eingegebene Wert streng entspricht Ihr Muster.

Auf diese Weise brauchen Sie nicht komplexe reguläre Ausdrücke erstellen - in meinem Fall musste ich alle Simple der Muster-Syntax unterstützen, anstatt auf bestimmte Arten wie nur wenige Tage, Monate und Jahre begrenzt

.

Aufbauend auf Antwort von @Pangea das Problem zu beheben wies darauf hin, durch @ ceklock , habe ich eine Methode, um zu überprüfen, dass die dateString keine ungültigen Zeichen enthält.

Hier ist, wie ich tun:

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+");
}

Ich schlage vor, Sie org.apache.commons.validator.GenericValidator Klasse von Apache zu verwenden.

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

. Hinweis: strict - Unabhängig davon, ob eine genaue Übereinstimmung der datePattern haben

Ich denke, die simpliest gerade ist eine Zeichenkette in ein Date-Objekt zu konvertieren und es zurück in eine Zeichenfolge zu konvertieren. Das angegebene Datum Zeichenfolge ist in Ordnung, wenn beide Strings noch entsprechen.

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

Unter der Annahme, dass beide von denen sind Strings (sonst würden sie bereits gültige Daten sein), hier ist eine Art und Weise:

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 erhalte ich:

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 können, ist es Ihre beiden Fällen gut umgehen kann.

Das funktioniert gut für mich. Ansatz vorgeschlagen oben von Ben.

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

Zwei Kommentare über die Verwendung von Simple.

  

es sollte als eine statische Instanz deklariert werden   wenn als statischer Zugriff deklariert soll synchronisiert werden, da es nicht sicher ist, fädelt

IME, die besser ist, dass eine Instanz für jede Parse eines Datums instanziieren.

über Methoden des Datum Parsing schön sind, habe ich nur noch neuen Check in bestehenden Methoden, die die doppelten konvertierte Datum überprüfen mit Originaldatum formater verwenden, so dass es für fast jeden Fall funktioniert, wie ich überprüfte. z.B. 2013.02.29 ist ungültig Datum. Gegebene Funktion parse das Datum nach aktuellen akzeptablen Datumsformate. Es gibt true zurück, wenn das Datum nicht erfolgreich analysiert wird.

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

Hier ist, was ich für Node-Umgebung hat keine externen Bibliotheken:

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 es zu benutzen:

isDateCorrect('2014-02-23')
true
// 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;
}

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");
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top