Ciekawe, że najbardziej oczywisty sposób tworzenia Date
obiektów w Javie został uznany za przestarzały i wydaje się być „zastąpiony” przez nie tak oczywisty w użyciu łagodny kalendarz.
Jak sprawdzić, czy data podana jako kombinacja dnia, miesiąca i roku jest prawidłową datą?
Na przykład 2008-02-31 (jak w rrrr-mm-dd) byłby nieprawidłową datą.
java
validation
calendar
date
Bloodboiler
źródło
źródło
Odpowiedzi:
Obecnym sposobem jest użycie klasy kalendarza. Ma metodę setLenient , która zweryfikuje datę i zgłoszenie oraz wyjątek, jeśli jest poza zakresem, jak w przykładzie.
Zapomniałem dodać: Jeśli otrzymasz wystąpienie kalendarza i ustawisz czas na podstawie daty, w ten sposób uzyskasz walidację.
Calendar cal = Calendar.getInstance(); cal.setLenient(false); cal.setTime(yourDate); try { cal.getTime(); } catch (Exception e) { System.out.println("Invalid date"); }
źródło
java.util.Date
,java.util.Calendar
ijava.text.SimpleDateFormat
są teraz starsze , zastępowane przez klasy java.time wbudowane w Javę 8 i nowsze. Zobacz samouczek firmy Oracle .Klucz to df.setLenient (false); . To więcej niż wystarczające w prostych przypadkach. Jeśli szukasz bardziej niezawodnych (wątpię) i / lub alternatywnych bibliotek, takich jak czas joda, spójrz na odpowiedź użytkownika „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; } }
źródło
setLenient
czy nie:SimpleDateFormat
zawsze będzie analizować, dopóki wzorzec nie zostanie dopasowany i zignoruje resztę ciągu, w ten sposób otrzymasz201
rok.java.util.Date
,java.util.Calendar
ijava.text.SimpleDateFormat
są teraz starsze , zastąpione przez klasy java.time wbudowane w Javę 8 i nowsze. Zobacz samouczek firmy Oracle .Jak pokazuje @Maglob, podstawowym podejściem jest przetestowanie konwersji z łańcucha do daty przy użyciu SimpleDateFormat.parse . W ten sposób zostaną wykryte nieprawidłowe kombinacje dnia i miesiąca, takie jak 2008-02-31.
Jednak w praktyce rzadko to wystarcza, ponieważ SimpleDateFormat.parse jest niezwykle liberalny. Istnieją dwa zachowania, które mogą Cię niepokoić:
Nieprawidłowe znaki w ciągu daty Zaskakujące jest, że data 2008-02-2x „przejdzie” jako poprawna data, na przykład w formacie locale = „rrrr-MM-dd”. Nawet jeśli isLenient == false.
Lata: 2, 3 lub 4 cyfry? Możesz także wymusić czterocyfrowe lata zamiast zezwalania na domyślne zachowanie SimpleDateFormat (które będzie interpretować „12-02-31” inaczej w zależności od tego, czy Twój format to „rrrr-MM-dd” czy „rr-MM-dd” )
Ścisłe rozwiązanie z biblioteką standardową
Tak więc pełny test ciągu do daty mógłby wyglądać następująco: połączenie dopasowania wyrażenia regularnego, a następnie wymuszona konwersja daty. Sztuczka z wyrażeniem regularnym polega na tym, aby była przyjazna dla lokalizacji.
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);
Zwróć uwagę, że wyrażenie regularne zakłada, że ciąg formatu zawiera tylko znaki dnia, miesiąca, roku i separatora. Poza tym format może być w dowolnym formacie regionalnym: „d / MM / rr”, „rrrr-MM-dd” i tak dalej. Ciąg formatu dla bieżącej lokalizacji można uzyskać w następujący sposób:
Joda Time - lepsza alternatywa?
Słyszałem ostatnio o czasie joda i pomyślałem, że mógłbym porównać. Dwa punkty:
Jest dość prosty w użyciu:
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; }
źródło
Date
,SimpleDateFormat
etc.) są obecnie wypierane przez nowoczesne java.time klas. Podobnie projekt Joda-Time jest w trybie konserwacji i zaleca migrację do klas java.time .Możesz użyć SimpleDateFormat
Na przykład coś takiego:
boolean isLegalDate(String s) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); sdf.setLenient(false); return sdf.parse(s, new ParsePosition(0)) != null; }
źródło
java.util.Date
,java.util.Calendar
ijava.text.SimpleDateFormat
są teraz starsze , zastąpione przez klasy java.time wbudowane w Javę 8 i nowsze. Zobacz samouczek firmy Oracle .tl; dr
Użyj trybu ścisłego on
java.time.DateTimeFormatter
do analizowaniaLocalDate
. Pułapka naDateTimeParseException
.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. )
Po przeanalizowaniu możesz sprawdzić rozsądną wartość. Na przykład data urodzenia w ciągu ostatnich stu lat.
birthDate.isAfter( LocalDate.now().minusYears( 100 ) )
Unikaj starszych klas daty i godziny
Unikaj używania kłopotliwych starych klas daty i czasu dostarczanych z najwcześniejszymi wersjami Javy. Teraz zastąpione przez klasy java.time .
LocalDate
&DateTimeFormatter
&ResolverStyle
LocalDate
Klasa reprezentuje wartość data-tylko bez time-of-day i bez strefy czasowej.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 ); }
java.time.DateTimeFormatter
Klasa może być ustawiony do analizowania ciągów z jednym z trzech trybów łagodzenia kar określonych wResolverStyle
enum. Wstawiamy wiersz do powyższego kodu, aby wypróbować każdy z trybów.Wyniki:
ResolverStyle.LENIENT
ld: 2000-03-02
ResolverStyle.SMART
ld: 2000-02-29
ResolverStyle.STRICT
BŁĄD: java.time.format.DateTimeParseException: nie można przeanalizować tekstu „31/02/2000”: nieprawidłowa data „31 lutego”
Widzimy, że w
ResolverStyle.LENIENT
trybie nieprawidłowa data jest przesuwana do przodu o równoważną liczbę dni. WResolverStyle.SMART
trybie (domyślnym) podejmowana jest logiczna decyzja, aby zachować datę w ciągu miesiąca i iść z ostatnim możliwym dniem miesiąca, 29 lutego w roku przestępnym, ponieważ nie ma 31 dnia w tym miesiącu.ResolverStyle.STRICT
Tryb zgłasza wyjątek twierdząc, że nie ma takiej daty.Wszystkie trzy są uzasadnione w zależności od problemu biznesowego i zasad. Wygląda na to, że w twoim przypadku chcesz, aby tryb ścisły odrzucał nieprawidłową datę, zamiast ją dostosowywać.
Informacje o java.time
Struktura java.time jest wbudowana w Javę 8 i nowsze. Klasy te kłopotliwe zastąpić stary starszych klas Date-Time, takich jak
java.util.Date
,Calendar
, iSimpleDateFormat
.Aby dowiedzieć się więcej, zobacz samouczek Oracle . I przeszukaj Stack Overflow, aby znaleźć wiele przykładów i wyjaśnień. Specyfikacja to JSR 310 .
Projekt Joda-Time , obecnie w trybie konserwacji , zaleca migrację do klas java.time .
Możesz wymieniać obiekty java.time bezpośrednio ze swoją bazą danych. Użyj sterownika JDBC zgodnego z JDBC 4.2 lub nowszym. Nie ma potrzeby stosowania ciągów ani
java.sql.*
klas.Skąd wziąć klasy java.time?
Projekt ThreeTen-Extra rozszerza java.time o dodatkowe klasy. Ten projekt jest poligonem doświadczalnym dla ewentualnych przyszłych dodatków do java.time. Można znaleźć kilka przydatnych klas tutaj takie jak
Interval
,YearWeek
,YearQuarter
, i więcej .źródło
java.time
Dzięki interfejsowi API daty i godziny ( klasy java.time ) wbudowanemu w Javę 8 i nowszych wersjach można używać tej
LocalDate
klasy.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; }
źródło
ResolverStyle.SMART
które dostosowuje wynikową wartość do prawidłowej daty, zamiast zgłaszać wyjątek. Więc ten kod nie osiągnie celu pytania. Zobacz moją odpowiedź na przykład i rozwiązanie przy użyciuResolverStyle.STRICT
.Alternatywnym rozwiązaniem ścisłym wykorzystującym bibliotekę standardową jest wykonanie następujących czynności:
1) Utwórz ścisły SimpleDateFormat używając swojego wzorca
2) Spróbuj przeanalizować wartość wprowadzoną przez użytkownika przy użyciu obiektu formatu
3) Jeśli się powiedzie, sformatuj ponownie datę wynikającą z (2), używając tego samego formatu daty (z (1))
4) Porównaj ponownie sformatowaną datę z oryginalną wartością wprowadzoną przez użytkownika. Jeśli są równe, to wprowadzona wartość ściśle pasuje do twojego wzorca.
W ten sposób nie musisz tworzyć skomplikowanych wyrażeń regularnych - w moim przypadku musiałem obsługiwać całą składnię wzorców SimpleDateFormat, zamiast ograniczać się do niektórych typów, takich jak dni, miesiące i lata.
źródło
Opierając się na odpowiedzi Aravinda, aby naprawić problem wskazany przez ceklocka w jego komentarzu , dodałem metodę sprawdzania,
dateString
czy nie zawiera żadnych nieprawidłowych znaków.Oto jak to robię:
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+"); }
źródło
Myślę, że najprościej jest po prostu przekonwertować ciąg na obiekt daty i przekonwertować go z powrotem na ciąg. Podany ciąg daty jest prawidłowy, jeśli oba ciągi nadal są zgodne.
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; }
źródło
Proponuję użyć
org.apache.commons.validator.GenericValidator
klasy z Apache.GenericValidator.isDate(String value, String datePattern, boolean strict);
Uwaga: ścisłe - określa, czy ma być dokładne dopasowanie do wzoru daty.
źródło
Zakładając, że oba są ciągami znaków (w przeciwnym razie byłyby już prawidłowymi datami), oto jeden sposób:
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); } }
Oto wynik, który otrzymuję:
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
Jak widać, dobrze radzi sobie w obu przypadkach.
źródło
To działa świetnie dla mnie. Podejście zasugerowane powyżej przez Bena.
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; } }
źródło
wygląda na to, że SimpleDateFormat nie sprawdza ściśle wzorca nawet po ustawieniu setLenient (false); jest na nim stosowana metoda, więc użyłem poniższej metody do sprawdzenia, czy wprowadzona data jest poprawną datą, czy nie zgodnie z podanym wzorcem.
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; }
źródło
Dwa komentarze na temat użycia SimpleDateFormat.
IME, który jest lepszy niż tworzenie instancji dla każdej analizy daty.
źródło
Powyższe metody parsowania daty są fajne, właśnie dodałem nowe sprawdzanie w istniejących metodach, które dwukrotnie sprawdzają przekonwertowaną datę z oryginalną datą za pomocą formatera, więc działa to dla prawie każdego przypadku, jak zweryfikowałem. np. 02/29/2013 to nieprawidłowa data. Podana funkcja analizuje datę zgodnie z aktualnymi akceptowalnymi formatami daty. Zwraca prawdę, jeśli data nie została pomyślnie przeanalizowana.
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; }
źródło
Oto, co zrobiłem dla środowiska Node bez zewnętrznych bibliotek:
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); } }
A oto jak z niego korzystać:
isDateCorrect('2014-02-23') true
źródło
// 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; }
źródło
Tutaj chciałbym sprawdzić format daty:
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"); }
źródło
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 ""; } }
źródło
Dzięki „starszemu” formatowi daty możemy sformatować wynik i porównać go ze źródłem.
public boolean isValidFormat(String source, String pattern) { SimpleDateFormat sd = new SimpleDateFormat(pattern); sd.setLenient(false); try { Date date = sd.parse(source); return date != null && sd.format(date).equals(source); } catch (Exception e) { return false; } }
Ten plik wykonywalny mówi „false” do źródła = 01.01.04 ze wzorcem „01 .01.2004”
źródło
setLenient to false, jeśli lubisz ścisłą weryfikację
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; }
źródło