Мне кажется любопытным, что наиболее очевидный способ создания Date
объектов в Java устарел и, похоже, был «заменен» не столь очевидным для использования мягким календарем.
Как вы проверяете, что дата, заданная как комбинация дня, месяца и года, является действительной датой?
Например, 31 февраля 2008 г. (как в формате гггг-мм-дд) будет недопустимой датой.
java
validation
calendar
date
Bloodboiler
источник
источник
Ответы:
Текущий способ - использовать класс календаря. У него есть метод setLenient , который проверяет дату и генерирует исключение, если оно выходит за пределы допустимого диапазона, как в вашем примере.
Забыл добавить: если вы получаете экземпляр календаря и устанавливаете время, используя дату, вы получаете подтверждение именно так.
Calendar cal = Calendar.getInstance(); cal.setLenient(false); cal.setTime(yourDate); try { cal.getTime(); } catch (Exception e) { System.out.println("Invalid date"); }
источник
java.util.Date
,java.util.Calendar
иjava.text.SimpleDateFormat
, теперь являются устаревшими , вытесненными классами java.time, встроенными в Java 8 и новее. См. Учебник Oracle .Ключ - df.setLenient (false); . Для простых случаев этого более чем достаточно. Если вы ищете более надежные (я сомневаюсь) и / или альтернативные библиотеки, такие как joda-time, посмотрите ответ пользователя 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; } }
источник
setLenient
или нет:SimpleDateFormat
всегда будет анализировать, пока шаблон не будет сопоставлен, и игнорирует остальную часть строки, таким образом, вы получите201
год.java.util.Date
,java.util.Calendar
иjava.text.SimpleDateFormat
, теперь являются устаревшими , вытесненными классами java.time, встроенными в Java 8 и более поздних версий . См. Учебник Oracle .Как показывает @Maglob, основной подход состоит в том, чтобы протестировать преобразование строки в дату с помощью SimpleDateFormat.parse . Это приведет к обнаружению недопустимых комбинаций дня / месяца, например 31.02.2008.
Однако на практике этого бывает достаточно редко, поскольку SimpleDateFormat.parse чрезвычайно либерален. Есть два варианта поведения, которые могут вас заинтересовать:
Недопустимые символы в строке даты. Удивительно, но 2008-02-2x будет "проходить" как допустимую дату, например, с locale format = "yyyy-MM-dd". Даже когда isLenient == false.
Годы: 2, 3 или 4 цифры? Вы также можете использовать 4-значные годы вместо того, чтобы разрешать поведение SimpleDateFormat по умолчанию (которое будет интерпретировать «12-02-31» по-разному в зависимости от того, был ли ваш формат «гггг-ММ-дд» или «гг-ММ-дд» )
Строгое решение со стандартной библиотекой
Таким образом, полная проверка строки на дату может выглядеть так: комбинация совпадения регулярных выражений и последующего принудительного преобразования даты. Хитрость с регулярным выражением заключается в том, чтобы сделать его адаптированным к локали.
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);
Обратите внимание, что регулярное выражение предполагает, что строка формата содержит только день, месяц, год и символы-разделители. Кроме того, формат может быть в любом формате локали: «д / ММ / гг», «гггг-ММ-дд» и так далее. Строку формата для текущей локали можно получить следующим образом:
Джода Тайм - лучшая альтернатива?
Я недавно слышал о joda time и подумал, что сравню . Два момента:
Использовать довольно просто:
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; }
источник
Date
,SimpleDateFormat
и т.д.) теперь заменены современными классами java.time . Точно так же проект Joda-Time находится в режиме обслуживания и рекомендует перейти на классы java.time .Вы можете использовать SimpleDateFormat
Например что-то вроде:
boolean isLegalDate(String s) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); sdf.setLenient(false); return sdf.parse(s, new ParsePosition(0)) != null; }
источник
java.util.Date
,java.util.Calendar
иjava.text.SimpleDateFormat
, теперь являются устаревшими , вытесненными классами java.time, встроенными в Java 8 и более поздних версий . См. Учебник Oracle .tl; dr
Используйте строгий режим на
java.time.DateTimeFormatter
разобратьLocalDate
. Ловушка для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. )
После синтаксического анализа вы можете проверить разумное значение. Например, дата рождения за последние сто лет.
birthDate.isAfter( LocalDate.now().minusYears( 100 ) )
Избегайте устаревших классов даты и времени
Избегайте использования проблемных старых классов даты и времени, поставляемых с самыми ранними версиями Java. Теперь на смену классам java.time .
LocalDate
&DateTimeFormatter
&ResolverStyle
LocalDate
Класс представляет собой дату только значение без времени суток и без временной зоны.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
Класс может быть установлен для разбора строк с любым из трех режимов смягчения ответственности , определенных вResolverStyle
перечислении. Мы вставляем строку в приведенный выше код, чтобы попробовать каждый из режимов.Результаты:
ResolverStyle.LENIENT
ld: 2000-03-02
ResolverStyle.SMART
ld: 2000-02-29
ResolverStyle.STRICT
ОШИБКА: java.time.format.DateTimeParseException: текст '31/02/2000' не может быть проанализирован: недопустимая дата '31 февраля'
Мы видим, что в
ResolverStyle.LENIENT
режиме недопустимая дата перемещается на эквивалентное количество дней вперед. ВResolverStyle.SMART
режиме (по умолчанию) принимается логическое решение сохранить дату в пределах месяца и использовать последний возможный день месяца, 29 февраля високосного года, поскольку в этом месяце нет 31-го дня.ResolverStyle.STRICT
Режим бросает исключение жалуется , что нет такой даты.Все три варианта приемлемы в зависимости от вашей бизнес-задачи и политики. Похоже, в вашем случае вы хотите, чтобы строгий режим отклонял недопустимую дату, а не настраивал ее.
О java.time
Java.time каркас встроен в Java 8 и более поздних версий. Эти классы вытеснять неприятные старые устаревшие классы даты и времени , такие как
java.util.Date
,Calendar
, иSimpleDateFormat
.Чтобы узнать больше, см. Oracle Tutorial . И поищите в Stack Overflow множество примеров и объяснений. Спецификация - JSR 310 .
Проект Joda-Time , находящийся сейчас в режиме обслуживания , рекомендует перейти на классы java.time .
Вы можете обмениваться объектами java.time напрямую с вашей базой данных. Используйте драйвер JDBC, совместимый с JDBC 4.2 или новее. Нет необходимости в строках, нет необходимости в
java.sql.*
занятиях.Где взять классы java.time?
Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является испытательной площадкой для возможных будущих дополнений к java.time. Вы можете найти некоторые полезные классы здесь , такие как
Interval
,YearWeek
,YearQuarter
, и более .источник
java.time
С помощью API даты и времени ( классы java.time ), встроенного в Java 8 и более поздних версий , вы можете использовать этот
LocalDate
класс.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; }
источник
ResolverStyle.SMART
которое настраивает полученное значение на допустимую дату, а не генерирует исключение. Таким образом, этот код не достигнет цели Вопроса. См. Мой ответ для примера и решения с использованиемResolverStyle.STRICT
.Альтернативное строгое решение с использованием стандартной библиотеки - выполнить следующее:
1) Создайте строгий SimpleDateFormat, используя свой шаблон
2) Попытка проанализировать введенное пользователем значение с помощью объекта формата
3) В случае успеха переформатируйте дату, полученную из (2), используя тот же формат даты (из (1)).
4) Сравните переформатированную дату с исходным значением, введенным пользователем. Если они равны, то введенное значение строго соответствует вашему шаблону.
Таким образом, вам не нужно создавать сложные регулярные выражения - в моем случае мне нужно было поддерживать весь синтаксис шаблона SimpleDateFormat, а не ограничиваться определенными типами, такими как дни, месяцы и годы.
источник
Основываясь на ответе Аравинда по устранению проблемы, указанной ceklock в его комментарии , я добавил метод, позволяющий убедиться, что
dateString
он не содержит недопустимого символа.Вот как я это делаю:
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+"); }
источник
Я думаю, что проще всего просто преобразовать строку в объект даты и преобразовать ее обратно в строку. Данная строка даты подходит, если обе строки все еще совпадают.
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; }
источник
Предлагаю вам использовать
org.apache.commons.validator.GenericValidator
класс от apache.GenericValidator.isDate(String value, String datePattern, boolean strict);
Примечание: strict - должно ли быть точное соответствие datePattern.
источник
Предполагая, что оба они являются строками (иначе они уже были бы действительными датами), вот один способ:
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); } }
Вот результат, который я получаю:
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
Как видите, он отлично справляется с обоими вашими случаями.
источник
Это отлично работает для меня. Подход, предложенный выше Беном.
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; } }
источник
похоже, что SimpleDateFormat не проверяет шаблон строго даже после setLenient (false); к нему применяется метод, поэтому я использовал метод ниже, чтобы проверить, является ли введенная дата действительной датой или нет в соответствии с предоставленным шаблоном.
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; }
источник
Два комментария по использованию SimpleDateFormat.
IME, который лучше, чем создание экземпляра для каждого синтаксического анализа даты.
источник
Вышеупомянутые методы синтаксического анализа даты хороши, я просто добавил новую проверку в существующие методы, которые дважды проверяют преобразованную дату с исходной датой с помощью средства форматирования, поэтому он работает почти для каждого случая, как я проверял. например, 29.02.2013 неверная дата. Данная функция анализирует дату в соответствии с текущими допустимыми форматами даты. Он возвращает истину, если дата не была успешно проанализирована.
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; }
источник
Вот что я сделал для среды Node без внешних библиотек:
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); } }
И вот как им пользоваться:
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; }
источник
Вот я бы проверил формат даты:
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"); }
источник
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 ""; } }
источник
С «устаревшим» форматом даты мы можем отформатировать результат и сравнить его с исходным.
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; } }
Этот execerpt сообщает "false" для source = 01.01.04 с шаблоном '01 .01.2004 '
источник
setLenient в false, если вам нравится строгая проверка
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; }
источник