Регулярное выражение |Високосные годы и многое другое - PullRequest
11 голосов
/ 27 декабря 2011

Недавно я искал регулярное выражение для некоторой проверки даты на стороне клиента, и мне не удалось найти такое, которое удовлетворяло бы следующим критериям:

  • Имеет диапазон от 1800 - сейчас
  • Выполняет надлежащую проверку даты с високосными годами
  • MM / DD / YYYY Форма
  • Проверка неверной даты

(Эти ограничения выходили за рамки моей компетенции и являются требованием клиента, несмотря на мои попытки убедить их, что это не так)t лучший маршрут)

Текущий код:

$('input').keyup(function()
{
       var regex = /^(?:(0[1-9]|1[012])[\/.](0[1-9]|[12][0-9]|3[01])[\/.](18|19|20)[0-9]{2})$/;
       $(this).toggleClass('invalid',!regex.test($(this).val()));    
});

Обновление:

Следует отметить, чтопрежде всего, чтобы увидеть, возможно ли такое регулярное выражение (, поскольку использование Regex не является моим выбором в этом вопросе ).Мне известны другие ( и лучше ) варианты проверки даты, однако, как уже упоминалось ранее, - это посмотреть, возможно ли это с помощью регулярного выражения.

Ответы [ 12 ]

26 голосов
/ 27 декабря 2011

Как уже упоминалось, регулярные выражения почти наверняка не то, что вы хотите.Но, сказав, что, если вы действительно хотите регулярное выражение, вот как оно построено:

31 день месяцев

(0[13578]|1[02])[\/.](0[1-9]|[12][0-9]|3[01])[\/.](18|19|20)[0-9]{2}

30 дней месяцев

(0[469]|11)[\/.](0[1-9]|[12][0-9]|30)[\/.](18|19|20)[0-9]{2}

Февраль 1-28 всегда действителен

(02)[\/.](0[1-9]|1[0-9]|2[0-8])[\/.](18|19|20)[0-9]{2}

29 февраля также действителен в високосные годы

(02)[\/.]29[\/.](((18|19|20)(04|08|[2468][048]|[13579][26]))|2000)

, что означает, что это будет так, если сложить все вместе:

((0[13578]|1[02])[\/.](0[1-9]|[12][0-9]|3[01])[\/.](18|19|20)[0-9]{2})|((0[469]|11)[\/.](0[1-9]|[12][0-9]|30)[\/.](18|19|20)[0-9]{2})|((02)[\/.](0[1-9]|1[0-9]|2[0-8])[\/.](18|19|20)[0-9]{2})|((02)[\/.]29[\/.](((18|19|20)(04|08|[2468][048]|[13579][26]))|2000))

Эта версия немного короче, но немного сложнее для понимания.

((0[13578]|1[02])[\/.]31[\/.](18|19|20)[0-9]{2})|((01|0[3-9]|1[1-2])[\/.](29|30)[\/.](18|19|20)[0-9]{2})|((0[1-9]|1[0-2])[\/.](0[1-9]|1[0-9]|2[0-8])[\/.](18|19|20)[0-9]{2})|((02)[\/.]29[\/.](((18|19|20)(04|08|[2468][048]|[13579][26]))|2000))

Эти сценарии длинные и не поддерживаются.Должно быть ясно, что это не очень хорошая идея, но она возможна.

Предостережения:

  • диапазон 1800-2099 (еще можно добавить без особых затруднений, нотребует изменений в 4-6 разрозненных местах)
  • требует двухзначных месяцев и дней (строгость может быть удалена из выражения в ~ 8 местах)
  • [\/.] в качестве разделителей (8 мест)
  • Не был протестирован (мы могли бы проверить его по всем комбинациям цифр и сравнить с функцией даты javascript? [Доказательство того, что мы заново изобретаем колесо])
13 голосов
/ 27 декабря 2011

Я бы посоветовал вам отказаться от попытки использовать регулярные выражения для этого. Вам намного лучше разбирать дату на составные части (месяц, день, год), а затем использовать числовые сравнения, чтобы убедиться, что она находится в нужном диапазоне.

Еще лучше, посмотрите, будет ли функция Javascript Date.parse делать то, что вы хотите.

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

7 голосов
/ 27 декабря 2011

Вот как я бы это сделал:

function validate( input ) {
    var date = new Date( input );
    input = input.split( '/' );   
    return date.getMonth() + 1 === +input[0] && 
           date.getDate() === +input[1] && 
           date.getFullYear() === +input[2];
}

Использование:

validate( '2/1/1983' ) // true
validate( '2/29/1983' ) // false
validate( '2/29/1984' ) // true (1984 is a leap year)

Демонстрационная версия: http://jsfiddle.net/9QNRx/

2 голосов
/ 03 августа 2017

Очевидно, что регулярные выражения не идеальный способ сделать это.Кроме того, гораздо безопаснее работать с форматом YYYY-MM-DD (ISO 8601), а не MM/DD/YYYY.

Тем не менее, здесь приведено самое короткое полностью работающее регулярное выражение для дат с 01.01.1800 по 31.12.2099:

^(((0[1-9]|1[012])\/(?!00|29)([012]\d)|(0[13-9]|1[012])\/(29|30)|(0[13578]|1[02])\/31)\/(18|19|20)\d{2}|02\/29\/((18|19|20)(0[48]|[2468][048]|[13579][26])|2000))$

Длина: 162 символа.

Разбивка:

^ # start
  (
    ( # non-leap months & days
      (0[1-9]|1[012])/(?!00|29)([012]\\d) # all months, days 01-28, uses negative lookahead
    |
      (0[13-9]|1[012])/(29|30) # all months except feb, days 29,30
    |
      (0[13578]|1[02])/31 # all 31 day months, day 31 only
    )
    /
    (18|19|20)\\d{2} # all years
  |
    02/29 # leap day
    /
    (
      (18|19|20)(0[48]|[2468][048]|[13579][26]) # leap years not divisible by 100
    |
      2000 # leap years divisible by 100
    )
  )
$ # end

Вот скрипка , которая проверяет все варианты использования с 00/00/1800 по 99/99/2099.

Также,для развлечения вот еще одна скрипка , которая генерирует самое отвратительное регулярное выражение, которое все еще работает, длиной 1205306 символов.Это выглядит примерно так:

^(01/01/1800|01/02/1800|01/03/1800|...|12/29/2099|12/30/2099|12/31/2099)$
1 голос
/ 27 июня 2016

это регулярное выражение для формата ГГГГ-ММ-ДД

((18|19|20)[0-9]{2}[\-.](0[13578]|1[02])[\-.](0[1-9]|[12][0-9]|3[01]))|(18|19|20)[0-9]{2}[\-.](0[469]|11)[\-.](0[1-9]|[12][0-9]|30)|(18|19|20)[0-9]{2}[\-.](02)[\-.](0[1-9]|1[0-9]|2[0-8])|(((18|19|20)(04|08|[2468][048]|[13579][26]))|2000)[\-.](02)[\-.]29
0 голосов
/ 27 мая 2019

Здравствуйте, найдите RegEx для вашего требования

  • Имеет диапазон от 1800
  • Теперь Выполняет правильную проверку даты с високосными годами
  • ДД / ММ / ГГГГ Формат
  • Проверка неверной даты

^ (:( ?: 31 (/) (?: 0 [13578] |? 1 [02])) \ 1 | (:( ?: 29 |? 30) (/) (0 ?: [13 -9] | 1 [0-2]) \ 2)) (:( ?: 18 |? 19 | 20) \ д {2}) $ |? ^ (?: 29 (/) 02 \ 3 (:( ? :( :( ?: 18 |? 19 | 20)) (0 ?: [48] | [2468] [048] | [13579] [26])))) $ |? ^ (?: 0 [1 -9] | 1 \ д | 2 [0-8]) (/) (:( ?: 0 [1-9]) | (:??? 1 [0-2])) \ 4 (:(? : 18 | 19 | 20) \ д {2}) $

enter image description here

Изображение и отладка RegEx на https://www.debuggex.com/

Тестирование:

  • ДД / ММ / ГГГГ
  • 01/12/190 Не соответствует
  • 29/02/1903 Не соответствует
  • 37/02/1903 Не соответствует
  • 09/03/1703 Не совпадает
  • 09/03/2103 Не соответствует
  • 09/31/2103 Не соответствует
  • 29/02/1904 - Матч
  • 01/12/1988 - Матч
0 голосов
/ 21 июля 2017
^(((?:(?:1[6-9]|[2-9]\d)?\d{2})(-)(?:(?:(?:0?[13578]|1[02])(-)31)|(?:(?:0?[1,3-9]|1[0-2])(-)(?:29|30))))|(((?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))(-)(?:0?2(-)29))|((?:(?:(?:1[6-9]|[2-9]\d)?\d{2})(-)(?:(?:0?[1-9])|(?:1[0-2]))(-)(?:0[1-9]|1\d|2[0-8]))))$

Пожалуйста, попробуйте приведенное выше выражение Reg.Я попробовал несколько комбинаций и обнаружил, что работает.

Пожалуйста, проверьте, работает ли это и для вас.

Принятый формат: ГГГГ-ММ-ДД

Год принят с 1600

0 голосов
/ 10 ноября 2016

Используя момент (не регулярное выражение), я сделал следующее:

Предполагается, что в качестве строкового значения указана дата ISO:

var isoDate = '2016-11-10';
var parsedIsoDate = moment(isoDate, ['YYYY-MM-DD'], true).format('YYYY-MM-DD');

if (parsedIsoDate !== isoDate) {
    // Invalid date.
}
0 голосов
/ 14 июля 2015

((0 [13578] | 1 [02]) [/.] 31 /. [0-9] {2}) | ((01 | 0 [3-9] | 1 [1-2]) /./ [0-9] {2}) |. ((0 [1-9] | 1 [0-2]) /./ [0-9] {2}) |. ((02) [/.] 29 /.тест

import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

public class RegxDateTest {

public static void main(String[] args) {
    // String to be scanned to find the pattern.
      String line = "This order was placed for QT3000! OK?";
        String pattern ="((0[13578]|1[02])[\\/.]31[\\/.](18|19|20)[0-9]{2})|((01|0[3-9]|1[1-2])[\\/.](29|30)[\\/.](18|19|20)[0-9]{2})|((0[1-9]|1[0-2])[\\/.](0[1-9]|1[0-9]|2[0-8])[\\/.](18|19|20)[0-9]{2})|((02)[\\/.]29[\\/.](((18|19|20)(04|08|[2468][048]|[13579][26]))|2000))";
      // Create a Pattern object
      Pattern r = Pattern.compile(pattern);
      LocalDate startDate = new LocalDate("1950-01-01");
      LocalDate endDate = new LocalDate("2020-01-01");
      for (LocalDate date = startDate; date.isBefore(endDate); date = date.plusDays(1))
      {
          if (date.toString("MM/dd/yyyy").matches(pattern)) {
             // System.out.println("This date does  match:  " + date.toString("MM/dd/yyyy") );
            }else{
                  System.out.println("This date does not match:  " + date.toString("MM/dd/yyyy") );
            }

      }
      String baddate1="02/29/2016";
      if (baddate1.matches(pattern)) {
          System.out.println("This date does  match:  " + baddate1 );
      }else{
          System.out.println("This date does not match:  " + baddate1 );
      }
      System.out.println("alldone:  "  );

}

}

0 голосов
/ 19 января 2015

Добавление моего ответа только для спорта - в противном случае я полностью согласен с @ Jim.

Это будет соответствовать високосным годам, в том числе с цифрами меньше или больше 4.

^\d*((((^|0|[2468])[048])|[13579][26])00$)|((0[48]|(^0*|[2468])[048]|[13579][26]))$

Мини-тестовый пример в Ruby (^ заменен на \A и $ на \Z, потому что Ruby):

r = /\A\d*((((\A|0|[2468])[048])|[13579][26])00\Z)|((0[48]|(\A0*|[2468])[048]|[13579][26]))\Z/
100000.times do |year|
  leap = year % 4 == 0 && ((year % 100 != 0) || (year % 400 == 0))
  leap_regex = !year.to_s[r].nil?
  if leap != leap_regex
    print 'Assertion broken:', year, leap, leap_regex, "\n"
  end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...