Как проверить соответствие даты в Java
Мне любопытно, что наиболее очевидный способ создания объектов Date
в Java был устаревшим и, похоже, был «заменен» не столь очевидным для использования lenient calendar.
Как вы проверяете, является ли дата, указанная как комбинация дня, месяца и года, действительной датой?
Например, 2008-02-31 (как в yyyy-mm-dd) будет недопустимой датой.
- Как получить количество дней разницы между двумя датами в mysql?
- Swift: как создать отметку времени и формат даты как ISO 8601, RFC 3339, часовой пояс UTC?
- DateTime.Now против DateTime.UtcNow
- Как рассчитать разницу во времени в сценарии bash?
- Преобразование даты времени в эпоху в Bash
- Как преобразовать миллисекунды в читаемую человеком форму?
- t-sql получить все даты между двумя датами
- Формат Время в формате AngularJS
- Oracle: как добавить минуты в метку времени?
- Преобразование даты в MySQL из поля строки
- Разница между двумя датами в Python
- Разница в месяцах между датами в MySQL
- Как получить текущее время и дату на C ++?
Текущий способ – использовать class календаря. Он имеет метод setLenient, который будет проверять дату и бросок и исключение, если он находится вне диапазона, как в вашем примере.
Забыл добавить: если вы получите экземпляр календаря и установите время, используя свою дату, вы получите подтверждение.
Calendar cal = Calendar.getInstance(); cal.setLenient(false); cal.setTime(yourDate); try { cal.getTime(); } catch (Exception e) { System.out.println("Invalid date"); }
Ключ – 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; } }
Как показано @Maglob, основным подходом является проверка преобразования из строки на сегодняшний день с помощью SimpleDateFormat.parse . Это приведет к сбою недействительных комбинаций день / месяц, например, 2008-02-31.
Однако на практике это редко бывает, так как SimpleDateFormat.parse чрезвычайно либеральна. Вы можете быть заинтересованы в двух типах поведения:
Недопустимые символы в строке даты. Удивительно, что 2008-02-2x «пройдет» как действительная дата с форматом языка = «yyyy-MM-dd», например. Даже когда isLenient == false.
Годы: 2, 3 или 4 цифры? Вы также можете использовать 4-значные годы, а не разрешать поведение SimpleDateFormat по умолчанию (которое будет интерпретировать «12-02-31» по-разному в зависимости от того, был ли ваш формат «yyyy-MM-dd» или «yy-MM-dd», )
Строгое решение со стандартной библиотекой
Таким образом, полная проверка на соответствие текущей версии может выглядеть так: комбинация соответствия регулярному выражению, а затем принудительное преобразование даты. Трюк с регулярным выражением – сделать его удобным для локали.
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);
Обратите внимание, что регулярное выражение предполагает, что строка формата содержит только символы дня, месяца, года и разделителя. Кроме того, формат может быть в любом формате локали: «d / MM / yy», «yyyy-MM-dd» и т. Д. Строка формата для текущей локали может быть получена следующим образом:
Locale locale = Locale.getDefault(); SimpleDateFormat sdf = (SimpleDateFormat)DateFormat.getDateInstance(DateFormat.SHORT, locale ); String format = sdf.toPattern();
Время Джоды – лучшая альтернатива?
Недавно я слышал о времени джедаи и думал, что сравню . Два момента:
- Кажется, лучше строго соблюдать недопустимые символы в строке даты, в отличие от SimpleDateFormat
- Невозможно увидеть способ использования четырехзначных лет с ним (но я думаю, вы могли бы создать свой собственный DateTimeFormatter для этой цели)
Это довольно просто:
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; }
Вы можете использовать SimpleDateFormat
Например, что-то вроде:
boolean isLegalDate(String s) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); sdf.setLenient(false); return sdf.parse(s, new ParsePosition(0)) != null; }
java.time
С API-интерфейсом Date and Time ( java.time ), встроенным в Java 8 и более поздние LocalDate
, вы можете использовать class 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; }
ТЛ; др
Используйте строгий режим для 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. )
После синтаксического анализа вы можете проверить разумную стоимость. Например, дата рождения в течение последних 100 лет.
birthDate.isAfter( LocalDate.now().minusYears( 100 ) )
Избегайте устаревших classов времени
Избегайте использования неприятных старых classов времени и времени, поставляемых с самыми ранними версиями Java. Теперь вытесняется classами 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
. Мы вставляем строку в вышеуказанный код, чтобы попробовать каждый из режимов.
f = f.withResolverStyle ( ResolverStyle.LENIENT );
Результаты:
-
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 и более поздние версии . Эти classы вытесняют неприятные старые устаревшие classы времени, такие как java.util.Date
, Calendar
и SimpleDateFormat
.
Проект Joda-Time , теперь в режиме обслуживания , советует перейти на classы java.time .
Чтобы узнать больше, ознакомьтесь с учебным пособием Oracle . И поиск Stack Overflow для многих примеров и объяснений. Спецификация – JSR 310 .
Вы можете обменивать объекты java.time непосредственно с вашей базой данных. Используйте JDBC-драйвер, совместимый с JDBC 4.2 или новее. Нет необходимости в строках, нет необходимости в java.sql.*
.
Где можно получить classы java.time?
- Java SE 8 , Java SE 9 и более поздние версии
- Встроенный.
- Часть стандартного Java API с интегрированной реализацией.
- Java 9 добавляет некоторые незначительные функции и исправления.
- Java SE 6 и Java SE 7
- Большая часть функциональных возможностей java.time включена обратно в Java 6 и 7 в ThreeTen-Backport .
- Android
- Более поздние версии реализаций пакетов Android classов java.time.
- Для более ранних Android (<26) проект ThreeTenABP адаптирует ThreeTen-Backport (упомянутый выше). См. Раздел Как использовать ThreeTenABP ….
Проект ThreeTen-Extra расширяет java.time с дополнительными classами. Этот проект является доказательством возможных будущих дополнений к java.time. Здесь вы можете найти полезные classы, такие как Interval
, YearWeek
, YearQuarter
и другие .
Альтернативным строгим решением, использующим стандартную библиотеку, является выполнение следующих действий:
1) Создайте строгий SimpleDateFormat, используя свой шаблон
2) Попытка проанализировать введенное пользователем значение с помощью объекта формата
3) В случае успеха переформатируйте дату, полученную в результате (2), используя тот же формат даты (из (1))
4) Сравните переформатированную дату с исходным значением, введенным пользователем. Если они равны, то введенное значение строго соответствует вашему шаблону.
Таким образом, вам не нужно создавать сложные регулярные выражения – в моем случае мне нужно было поддерживать весь синтаксис шаблона SimpleDateFormat, а не ограничиваться определенными типами, например, днями, месяцами и годами.
Основываясь на ответе @Pangea, чтобы исправить проблему, отмеченную @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+"); }
Я предлагаю вам использовать class org.apache.commons.validator.GenericValidator
из apache.
GenericValidator.isDate(String value, String datePattern, boolean strict);
Примечание: строгое – должно ли быть точное совпадение с datePattern.
Я думаю, что проще всего просто преобразовать строку в объект даты и преобразовать ее обратно в строку. Данная строка даты является прекрасной, если обе строки все еще совпадают.
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; }
Предполагая, что обе эти строки являются строками (иначе они уже были действительными Датами), вот один из способов:
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.
он должен быть объявлен как статический экземпляр, если он объявлен как статический доступ, должен быть синхронизирован, поскольку он не является streamобезопасным
IME, что лучше, чем экземпляр экземпляра для каждого анализа даты.
Выше методов синтаксического анализа даты хороши, я просто добавил новую проверку существующих методов, которые дважды проверяют преобразованную дату с исходной датой с помощью formater, поэтому она работает почти для каждого случая, как я проверял. например, 02/29/2013 – неверная дата. Данная функция анализирует дату в соответствии с текущими допустимыми форматами даты. Он возвращает true, если дата не обрабатывается успешно.
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; }