Подтвердить что ты не робот

Как проверить соответствие даты в Java

Мне любопытно, что наиболее очевидный способ создания объектов Date в Java был устаревшим и, похоже, был "заменен" не столь очевидным для использования lenient calendar.

Как вы проверяете, является ли дата, указанная как комбинация дня, месяца и года, действительной датой?

Например, 2008-02-31 (как в yyyy-mm-dd) будет недопустимой датой.

4b9b3361

Ответ 1

Текущий способ - использовать класс календаря. У него есть метод setLenient, который будет проверять дату, а также throw и исключение, если он находится вне диапазона, как в вашем примере.

Забыл добавить: Если вы получаете экземпляр календаря и задаете время с помощью своей даты, вы получите подтверждение.

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

Ответ 2

Ключ - 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;
        }
}

Ответ 3

Как показано @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();

Время в Джоде - лучшая альтернатива?

Недавно я слышал о joda time и думал, что сравню. Две точки:

  • Кажется, лучше строго соблюдать недопустимые символы в строке даты, в отличие от 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;
}

Ответ 4

Вы можете использовать SimpleDateFormat

Например, что-то вроде:

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

Ответ 5

ТЛ; др

Используйте строгий режим на 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. Мы вставляем строку в приведенный выше код, чтобы попробовать каждый из режимов.

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 вызывает исключение, сообщающее, что такой даты нет.

Все три из них являются разумными в зависимости от вашей бизнес-проблемы и политики. Похоже, в вашем случае вы хотите, чтобы строгий режим отклонял неверную дату, а не корректировал ее.


Table of all date-time types in Java, both modern and legacy.


О java.time

Среда java.time встроена в Java 8 и более поздние версии. Эти классы вытесняют проблемные старые устаревшие классы даты и времени, такие как java.util.Date, Calendar и & SimpleDateFormat.

Чтобы узнать больше, см. Учебное пособие по Oracle. И поищите в Кару множество примеров и объяснений. Спецификация: JSR 310.

Проект Joda-Time, который теперь находится в режиме обслуживания, рекомендует выполнить переход на классы java.time.

Вы можете обмениваться объектами java.time напрямую с вашей базой данных. Используйте драйвер JDBC, совместимый с JDBC 4.2 или более поздней версией. Нет необходимости в строках, нет необходимости в классах java.sql.*.

Где взять классы java.time?

  • Java SE 8, Java SE 9, Java SE 10, Java SE 11 и более поздние версии - часть стандартного Java API с встроенной реализацией ,
    • Java 9 добавляет некоторые мелкие функции и исправления.
  • Java SE 6 и Java SE 7
    • Большая часть функциональности java.time перенесена на Java 6 & 7 в ThreeTen-Backport.
  • Android

Table of which java.time library to use with which version of Java or Android

Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к java.time. Здесь вы можете найти несколько полезных классов, таких как Interval, YearWeek, YearQuarter и еще more.

Ответ 6

java.time

С API-интерфейсом Date and Time (java.time), встроенным в Java 8 и более поздние LocalDate, вы можете использовать класс 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;
}

Ответ 7

Основываясь на ответе Аравинда, чтобы исправить проблему, указанную 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+");
}

Ответ 8

Альтернативное строчное решение с использованием стандартной библиотеки состоит в следующем:

1) Создайте строгий SimpleDateFormat, используя ваш шаблон

2) Попытайтесь проанализировать введенное пользователем значение с помощью объекта формата

3) В случае успеха переформатируйте дату, полученную в результате (2), используя тот же формат даты (из (1))

4) Сравните переформатированную дату с исходным, введенным пользователем значением. Если они равны, то введенное значение строго соответствует вашему шаблону.

Таким образом, вам не нужно создавать сложные регулярные выражения - в моем случае мне нужно было поддерживать весь синтаксис шаблона SimpleDateFormat, а не ограничиваться определенными типами, такими как дни, месяцы и годы.

Ответ 9

Я предлагаю вам использовать класс org.apache.commons.validator.GenericValidator от apache.

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

Примечание: строгое - должно быть точное совпадение с datePattern.

Ответ 10

Я думаю, что проще всего просто преобразовать строку в объект даты и преобразовать ее обратно в строку. Данная строка даты прекрасна, если обе строки все еще совпадают.

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

Ответ 11

Предполагая, что обе эти строки являются строками (иначе они уже были бы действительными датами), здесь один из способов:

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

Как вы можете видеть, он отлично справляется с обоими случаями.

Ответ 12

Это отлично работает для меня. Подход, предложенный выше Беном.

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

Ответ 13

Два комментария к использованию SimpleDateFormat.

он должен быть объявлен как статический экземпляр если он объявлен как статический доступ, должен быть синхронизирован, так как он не является потокобезопасным

IME, что лучше, чем экземпляр экземпляра для каждого анализа даты.

Ответ 14

похоже, 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;
}

Ответ 15

Выше методы синтаксического анализа даты хороши, я просто добавил новую проверку существующих методов, которые дважды проверяют преобразованную дату с исходной датой с помощью 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;
    }

Ответ 16

Вот что я сделал для среды 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

Ответ 17

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

Ответ 18

Вот я бы проверил формат даты:

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

Ответ 19

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

    }

Ответ 20

Используя устаревший формат даты, мы можем отформатировать результат и сравнить его с источником.

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

Этот отрывок говорит "ложь" источнику = 01.01.04 с шаблоном "01. 01.2004"

Ответ 21

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