- How to sanity check a date in Java
- 25 Answers 25
- A Strict Solution with the Standard Library
- Joda Time — Better Alternative?
- Java — how to check if String is valid date?
- 1. Overview
- 2. With SimpleDateFormat
- 3. With SimpleDateFormat + different patterns
- 4. LocalDate — Java 8
- 5. LocalDate + DateTimeFormatter build-in patterns
- 6. LocalDate + DateTimeFormatter different patterns
- References
- Проверьте, является ли строка Допустимой датой в Java
- 2. Обзор Проверки данных
- 3. Проверьте С помощью DateFormat
- 4. Проверьте С Помощью LocalDate
- 5. Проверка с помощью DateTimeFormatter
- 6. Проверка С Помощью Валидатора Apache Commons
- 7. Заключение
- Читайте ещё по теме:
How to sanity check a date in Java
I find it curious that the most obvious way to create Date objects in Java has been deprecated and appears to have been «substituted» with a not so obvious to use lenient calendar. How do you check that a date, given as a combination of day, month, and year, is a valid date? For instance, 2008-02-31 (as in yyyy-mm-dd) would be an invalid date.
25 Answers 25
Key is df.setLenient(false); . This is more than enough for simple cases. If you are looking for a more robust (I doubt that) and/or alternate libraries like joda-time, then look at the answer by user «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; >>
@ceklock that’s the way it works, no matter if you use setLenient or not: SimpleDateFormat will always parse until the pattern is matched and ignore the rest of the string, thus you get 201 as year.
Incorporating exception handling entails a big performance hit, so this is probably a bad design IF you expect malformed input in normal operation (e.g. validating user input). But if the method is used as a double-check against inputs that are supposed to be valid all the time (except for bugs), it is fine.
FYI, the terribly troublesome old date-time classes such as java.util.Date , java.util.Calendar , and java.text.SimpleDateFormat are now legacy, supplanted by the java.time classes built into Java 8 and later. See Tutorial by Oracle.
As shown by @Maglob, the basic approach is to test the conversion from string to date using SimpleDateFormat.parse. That will catch invalid day/month combinations like 2008-02-31.
However, in practice that is rarely enough since SimpleDateFormat.parse is exceedingly liberal. There are two behaviours you might be concerned with:
Invalid characters in the date string Surprisingly, 2008-02-2x will «pass» as a valid date with locale format = «yyyy-MM-dd» for example. Even when isLenient==false.
Years: 2, 3 or 4 digits? You may also want to enforce 4-digit years rather than allowing the default SimpleDateFormat behaviour (which will interpret «12-02-31» differently depending on whether your format was «yyyy-MM-dd» or «yy-MM-dd»)
A Strict Solution with the Standard Library
So a complete string to date test could look like this: a combination of regex match, and then a forced date conversion. The trick with the regex is to make it locale-friendly.
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"); reFormat = Pattern.compile("y+").matcher(reFormat).replaceAll("\\\\d"); 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);
Note that the regex assumes the format string contains only day, month, year, and separator characters. Aside from that, format can be in any locale format: «d/MM/yy», «yyyy-MM-dd», and so on. The format string for the current locale could be obtained like this:
Locale locale = Locale.getDefault(); SimpleDateFormat sdf = (SimpleDateFormat)DateFormat.getDateInstance(DateFormat.SHORT, locale ); String format = sdf.toPattern();
Joda Time — Better Alternative?
I’ve been hearing about joda time recently and thought I’d compare. Two points:
- Seems better at being strict about invalid characters in the date string, unlike SimpleDateFormat
- Can’t see a way to enforce 4-digit years with it yet (but I guess you could create your own DateTimeFormatter for this purpose)
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; >
Java — how to check if String is valid date?
Geo-Baby
1. Overview
In this post we can find code which can validate if String contains valid Date based on defined pattern.
2. With SimpleDateFormat
import java.text.ParseException; import java.text.SimpleDateFormat; public class Example1 < public static void main(String[] args) < System.out.println(checkIfDateIsValid("2019-10-13")); // true System.out.println(checkIfDateIsValid("2019:10:13")); // false >private static boolean checkIfDateIsValid(String date) < SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); // With lenient parsing, the parser may use heuristics to interpret // inputs that do not precisely match this object's format. format.setLenient(false); try < format.parse(date); >catch (ParseException e) < return false; >return true; > >
3. With SimpleDateFormat + different patterns
import java.text.ParseException; import java.text.SimpleDateFormat; public class Example2 < public static void main(String[] args) < System.out.println("# Example 1"); System.out.println(checkIfDateIsValid("yyyy-MM-dd", "2019-10-13")); // true System.out.println(checkIfDateIsValid("yyyy-MM-dd", "2019:10:13")); // false System.out.println("# Example 2"); System.out.println(checkIfDateIsValid("yyyy/MM/dd", "2019/10/13")); // true System.out.println("# Example 3"); System.out.println(checkIfDateIsValid("yyyy:MM:dd", "2019:10:13")); // true System.out.println("# Example 4"); System.out.println(checkIfDateIsValid("MM/dd/yyyy", "10/13/2019")); // true >private static boolean checkIfDateIsValid(String pattern, String date) < SimpleDateFormat format = new SimpleDateFormat(pattern); // With lenient parsing, the parser may use heuristics to interpret // inputs that do not precisely match this object's format. format.setLenient(false); try < format.parse(date); >catch (ParseException e) < return false; >return true; > >
# Example 1 true false # Example 2 true # Example 3 true # Example 4 true
4. LocalDate — Java 8
import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; public class Example3 < public static void main(String[] args) < System.out.println(checkIfDateIsValid("2019-10-13")); // true System.out.println(checkIfDateIsValid("2019:10:13")); // false >private static boolean checkIfDateIsValid(String date) < try < LocalDate.parse(date, DateTimeFormatter.ISO_DATE); >catch (DateTimeParseException e) < return false; >return true; > >
5. LocalDate + DateTimeFormatter build-in patterns
import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; public class Example4 < public static void main(String[] args) < System.out.println("# Example 1"); System.out.println(checkIfDateIsValid( DateTimeFormatter.ISO_DATE, "2019-10-13")); // true System.out.println(checkIfDateIsValid( DateTimeFormatter.ISO_DATE, "2019:10:13")); // false System.out.println("# Example 2"); System.out.println(checkIfDateIsValid( DateTimeFormatter.BASIC_ISO_DATE, "20191013")); // true System.out.println("# Example 3"); System.out.println(checkIfDateIsValid( DateTimeFormatter.ISO_LOCAL_DATE, "2019-10-13")); // true >private static boolean checkIfDateIsValid(DateTimeFormatter formatter, String date) < try < LocalDate.parse(date, formatter); >catch (DateTimeParseException e) < return false; >return true; > >
# Example 1 true false # Example 2 true # Example 3 true
6. LocalDate + DateTimeFormatter different patterns
import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; public class Example5 < public static void main(String[] args) < System.out.println("# Example 1"); System.out.println(checkIfDateIsValid("yyyy-MM-dd", "2019-10-13")); // true System.out.println(checkIfDateIsValid("yyyy-MM-dd", "2019:10:13")); // false System.out.println("# Example 2"); System.out.println(checkIfDateIsValid("yyyy/MM/dd", "2019/10/13")); // true System.out.println("# Example 3"); System.out.println(checkIfDateIsValid("yyyy:MM:dd", "2019:10:13")); // true System.out.println("# Example 4"); System.out.println(checkIfDateIsValid("MM/dd/yyyy", "10/13/2019")); // true >private static boolean checkIfDateIsValid(String pattern, String date) < try < DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern); LocalDate.parse(date, formatter); >catch (DateTimeParseException e) < return false; >return true; > >
# Example 1 true false # Example 2 true # Example 3 true # Example 4 true
References
Проверьте, является ли строка Допустимой датой в Java
В этом уроке мы обсудим различные способы проверки того, содержит ли Строка допустимую дату в Java.
Мы обсудим решения до Java 8, после Java 8 и с использованием валидатора Apache Commons .
2. Обзор Проверки данных
Всякий раз, когда мы получаем данные в любом приложении, нам необходимо убедиться, что они действительны, прежде чем выполнять дальнейшую обработку.
В случае ввода данных нам может потребоваться проверить следующее:
- Входные данные содержат дату в допустимом формате, например ММ/ДД/ГГГГ
- Различные части входных данных находятся в допустимом диапазоне
- Входные данные преобразуются в действительную дату в календаре
Для этого мы можем использовать регулярные выражения. Однако регулярные выражения для обработки различных форматов и языков ввода сложны и подвержены ошибкам. Кроме того, они могут ухудшить производительность.
Мы обсудим различные способы реализации проверки данных гибким, надежным и эффективным способом.
Во-первых, давайте напишем интерфейс для проверки данных:
public interface DateValidator
В следующих разделах мы реализуем этот интерфейс, используя различные подходы.
3. Проверьте С помощью DateFormat
Java с самого начала предоставляла возможности для форматирования и анализа дат. Эта функциональность находится в DateFormat абстрактном классе и его реализации — SimpleDateFormat .
Давайте реализуем проверку данных с помощью метода parse класса DateFormat :
public class DateValidatorUsingDateFormat implements DateValidator < private String dateFormat; public DateValidatorUsingDateFormat(String dateFormat) < this.dateFormat = dateFormat; >@Override public boolean isValid(String dateStr) < DateFormat sdf = new SimpleDateFormat(this.dateFormat); sdf.setLenient(false); try < sdf.parse(dateStr); >catch (ParseException e) < return false; >return true; > >
Поскольку Формат Даты и связанные классы не являются потокобезопасными , мы создаем новый экземпляр для каждого вызова метода.
Далее давайте напишем модульный тест для этого класса:
DateValidator validator = new DateValidatorUsingDateFormat("MM/dd/yyyy"); assertTrue(validator.isValid("02/28/2019")); assertFalse(validator.isValid("02/30/2019"));
Это было наиболее распространенным решением до Java 8.
4. Проверьте С Помощью LocalDate
Java 8 представила улучшенный API даты и времени . Он добавил класс LocalDate , который представляет дату без времени. Этот класс является неизменяемым и потокобезопасным.
Локальная дата предоставляет два статических метода для анализа дат. Оба они используют DateTimeFormatter для фактического анализа:
public static LocalDate parse(CharSequence text) // parses dates using using DateTimeFormatter.ISO_LOCAL_DATE public static LocalDate parse(CharSequence text, DateTimeFormatter formatter) // parses dates using the provided formatter
Давайте используем метод parse для реализации проверки данных:
public class DateValidatorUsingLocalDate implements DateValidator < private DateTimeFormatter dateFormatter; public DateValidatorUsingLocalDate(DateTimeFormatter dateFormatter) < this.dateFormatter = dateFormatter; >@Override public boolean isValid(String dateStr) < try < LocalDate.parse(dateStr, this.dateFormatter); >catch (DateTimeParseException e) < return false; >return true; > >
Реализация использует объект DateTimeFormatter для форматирования. Поскольку этот класс потокобезопасен, мы используем один и тот же экземпляр для разных вызовов методов.
Давайте также добавим модульный тест для этой реализации:
DateTimeFormatter dateFormatter = DateTimeFormatter.BASIC_ISO_DATE; DateValidator validator = new DateValidatorUsingLocalDate(dateFormatter); assertTrue(validator.isValid("20190228")); assertFalse(validator.isValid("20190230"));
5. Проверка с помощью DateTimeFormatter
В предыдущем разделе мы видели, что LocalDate использует DateTimeFormatter объект для синтаксического анализа. Мы также можем использовать класс DateTimeFormatter непосредственно для форматирования и синтаксического анализа.
DateTimeFormatter анализирует текст в два этапа. На этапе 1 он анализирует текст в различные поля даты и времени в зависимости от конфигурации. На этапе 2 он преобразует проанализированные поля в объект даты и/или времени.
Атрибут ResolverStyle управляет этапом 2. Это перечисление , имеющее три возможных значения:
- СНИСХОДИТЕЛЬНО – разрешает даты и время снисходительно
- ИНТЕЛЛЕКТУАЛЬНОЕ – интеллектуальное разрешение дат и времени
- СТРОГО – строго определяет даты и время
Теперь давайте запишем проверку данных с помощью DateTimeFormatter напрямую:
public class DateValidatorUsingDateTimeFormatter implements DateValidator < private DateTimeFormatter dateFormatter; public DateValidatorUsingDateTimeFormatter(DateTimeFormatter dateFormatter) < this.dateFormatter = dateFormatter; >@Override public boolean isValid(String dateStr) < try < this.dateFormatter.parse(dateStr); >catch (DateTimeParseException e) < return false; >return true; > >
Далее давайте добавим модульный тест для этого класса:
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("uuuu-MM-dd", Locale.US) .withResolverStyle(ResolverStyle.STRICT); DateValidator validator = new DateValidatorUsingDateTimeFormatter(dateFormatter); assertTrue(validator.isValid("2019-02-28")); assertFalse(validator.isValid("2019-02-30"));
В приведенном выше тесте мы создаем DateTimeFormatter на основе шаблона и локали. Мы используем строгое разрешение для дат.
6. Проверка С Помощью Валидатора Apache Commons
Проект Apache Commons предоставляет платформу проверки. Это содержит процедуры проверки, такие как дата, время, номера, валюта, IP-адрес, адрес электронной почты и URL.
Для нашей цели в этой статье давайте рассмотрим класс GenericValidator , который предоставляет несколько методов для проверки того, содержит ли строка допустимую дату:
public static boolean isDate(String value, Locale locale) public static boolean isDate(String value,String datePattern, boolean strict)
Чтобы использовать библиотеку, давайте добавим в наш проект зависимость commons-validator Maven:
commons-validator commons-validator 1.6
Далее, давайте использовать класс GenericValidator для проверки дат:
assertTrue(GenericValidator.isDate("2019-02-28", "yyyy-MM-dd", true)); assertFalse(GenericValidator.isDate("2019-02-29", "yyyy-MM-dd", true));
7. Заключение
В этой статье мы рассмотрели различные способы проверки того, содержит ли строка допустимую дату.
Как обычно, полный исходный код можно найти на GitHub .