Регулярное выражение для сопоставления допустимых дат



Я пытаюсь написать регулярное выражение, которое проверяет дату. Регулярное выражение должно соответствовать следующему




  • М/Д/ГГГГ

  • ММ/ДД/ГГГГ

  • одноразрядные месяцы могут начинаться с нуля (например: 03/12/2008)

  • однозначные дни могут начинаться с нуля (например: 3/02/2008)

  • не может включать 30 февраля или 31 февраля (например: 2/31/2008)


пока у меня есть



^(([1-9]|1[012])[-/.]([1-9]|[12][0-9]|3[01])[-/.](19|20)dd)|((1[012]|0[1-9])(3[01]|2d|1d|0[1-9])(19|20)dd)|((1[012]|0[1-9])[-/.](3[01]|2d|1d|0[1-9])[-/.](19|20)dd)$


эти матчи правильно, за исключением того, что он по-прежнему включает 2/30/2008 и 2/31/2008.



у кого-нибудь есть лучшие предложения?



Edit: нашел ответ на RegExLib



^((((0[13578])|([13578])|(1[02]))[/](([1-9])|([0-2][0-9])|(3[01])))|(((0[469])|([469])|(11))[/](([1-9])|([0-2][0-9])|(30)))|((2|02)[/](([1-9])|([0-2][0-9]))))[/]d{4}$|^d{4}$


он соответствует всем действительным месяцам, которые следуют за форматом MM/DD/YYYY.



спасибо всем за помощь.

1255   15  

15 ответов:

это не подходящее использование регулярных выражений. Вам было бы лучше использовать

[0-9]{2}/[0-9]{2}/[0-9]{4}

и затем проверка диапазонов на языке более высокого уровня.

вот Reg ex, который соответствует всем действительным датам, включая високосные годы. Принятые форматы ММ/ДД / гггг или ММ-ДД-гггг или мм. ДД.формат гггг

^(?:(?:(?:0?[13578]|1[02])(\/|-|\.)31)|(?:(?:0?[1,3-9]|1[0-2])(\/|-|\.)(?:29|30)))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:0?2(\/|-|\.)29(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:(?:0?[1-9])|(?:1[0-2]))(\/|-|\.)(?:0?[1-9]|1\d|2[0-8])(?:(?:1[6-9]|[2-9]\d)?\d{2})$

вежливость Ашуга Ахамед

ремонтопригодная версия Perl 5.10

/
  (?:
      (?<month> (?&mon_29)) [\/] (?<day>(?&day_29))
    | (?<month> (?&mon_30)) [\/] (?<day>(?&day_30))
    | (?<month> (?&mon_31)) [\/] (?<day>(?&day_31))
  )
  [\/]
  (?<year> [0-9]{4})

  (?(DEFINE)
    (?<mon_29> 0?2 )
    (?<mon_30> 0?[469]   | (11) )
    (?<mon_31> 0?[13578] | 1[02] )

    (?<day_29> 0?[1-9] | [1-2]?[0-9] )
    (?<day_30> 0?[1-9] | [1-2]?[0-9] | 30 )
    (?<day_31> 0?[1-9] | [1-2]?[0-9] | 3[01] )
  )
/x

вы можете получить элементы по имени в этой версии.

say "Month=$+{month} Day=$+{day} Year=$+{year}";

(никаких попыток ограничить значения за год не предпринималось. )

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

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

это-

разделители

[^\w\d\r\n:] 

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

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

Примечание однако; он будет соответствовать строке со смешанными разделителями, как это 2/12-73, которые на самом деле не может быть действительной датой.

Год

(\d{4}|\d{2})

это соответствует группе из двух или 4 цифр, в большинстве случаев это приемлемо, но если вы имеете дело с данными из годы 0-999 или за 9999, вы должны решить, как справиться с этим, потому что в большинстве случаев 1, 3 или >4 года цифры-это фигня.

Месяц Значения

(0?[1-9]|1[0-2])

соответствует любому числу от 1 до 12 С или без ведущего нуля - Примечание: 0 и 00 не соответствует.

Дата

(0?[1-9]|[12]\d|30|31)

соответствует любому числу от 1 до 31 С или без ведущего нуля-Примечание: 0 и 00 не является соответствие.

это выражение соответствует дате, месяцу, году форматированные даты

(0?[1-9]|[12]\d|30|31)[^\w\d\r\n:](0?[1-9]|1[0-2])[^\w\d\r\n:](\d{4}|\d{2})

но он также будет соответствовать некоторым из года, месяца даты те. Он также должен быть записан с граничными операторами, чтобы гарантировать, что вся строка даты выбрана и предотвращает допустимые поддатки, извлекаемые из данных, которые не являются хорошо сформированными, т. е. без граничных тегов 20/12/194 соответствует 20/12/19 и 101/12/1974 соответствует 01/12/1974

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

\b(0?[1-9]|[12]\d|30|31)[^\w\d\r\n:](0?[1-9]|1[0-2])[^\w\d\r\n:](\d{4}|\d{2})\b

в этом регулярном выражении нет проверки, поэтому будет сопоставлена хорошо сформированная, но недействительная дата, такая как 31/02/2001. Это проблема качества данных, и, как говорили другие, ваше регулярное выражение не должно проверять данные.

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

мусор внутрь, мусор наружу.

сказав это, если у вас есть смешанные форматы, где значения дат различаются, и вы должны извлечь столько, сколько сможете; вы можете объединить несколько выражений вместе;

это (катастрофическое) выражение соответствует DMY и YMD даты

(\b(0?[1-9]|[12]\d|30|31)[^\w\d\r\n:](0?[1-9]|1[0-2])[^\w\d\r\n:](\d{4}|\d{2})\b)|(\b(0?[1-9]|1[0-2])[^\w\d\r\n:](0?[1-9]|[12]\d|30|31)[^\w\d\r\n:](\d{4}|\d{2})\b)

но вы не сможете сказать, если даты, как 6/9/1973 являются 6 сентября или 9 июня. Я изо всех сил пытаюсь придумать сценарий, в котором это не вызовет проблемы где - то внизу, это плохая практика, и вам не нужно иметь дело с этим так-найти владельца данных и ударить их молотком управления.

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

\b(\d{4})(0[1-9]|1[0-2])(0[1-9]|[12]\d|30|31)\b

но обратите внимание, что он будет соответствовать хорошо сформированным, но недопустимым значениям, таким как 20010231 (31 февраля!):)

тестовых данных

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

Я надеюсь, что это кому-то пригодится.

Valid Dates in various formats

Day, month, year
2/11/73
02/11/1973
2/1/73
02/01/73
31/1/1973
02/1/1973
31.1.2011
31-1-2001
29/2/1973
29/02/1976 
03/06/2010
12/6/90

month, day, year
02/24/1975 
06/19/66 
03.31.1991
2.29.2003
02-29-55
03-13-55
03-13-1955
1274
1274
174
03/31/2001
01/21/2001
12/13/2001

Match both DMY and MDY
12/12/1978
6/6/78
06/6/1978
6/06/1978

using whitespace as a delimiter

13 11 2001
11 13 2001
11 13 01 
13 11 01
1 1 01
1 1 2001

Year Month Day order
76/02/02
1976/02/29
1976/2/13
76/09/31

YYYYMMDD sortable format
19741213
19750101

Valid dates before Epoch
12/1/10
12/01/660
12/01/00
12/01/0000

Valid date after 2038

01/01/2039
01/01/39

Valid date beyond the year 9999

01/01/10000

Dates with leading or trailing characters

12/31/21/
31/12/1921AD
31/12/1921.10:55
12/10/2016  8:26:00.39
wfuwdf12/11/74iuhwf
fwefew13/11/1974
01/12/1974vdwdfwe
01/01/99werwer
12321301/01/99

Times that look like dates

12:13:56
13:12:01
1:12:01PM
1:12:01 AM

Dates that runs across two lines

1/12/19
74

01/12/19
74/13/1946

31/12/20
08:13

Invalid, corrupted or nonsense dates

0/1/2001
1/0/2001
00/01/2100
01/0/2001
0101/2001
01/131/2001
31/31/2001
101/12/1974
56/56/56
00/00/0000
0/0/1999
12/01/0
12/10/-100
74/2/29
12/32/45
20/12/194

2/12-73

для управления сроком действия даты в следующем формате:

гггг / ММ / ДД или гггг-ММ-ДД

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

(((19|20)([2468][048]|[13579][26]|0[48])|2000)[/-]02[/-]29|((19|20)[0-9]{2}[/-](0[4678]|1[02])[/-](0[1-9]|[12][0-9]|30)|(19|20)[0-9]{2}[/-](0[1359]|11)[/-](0[1-9]|[12][0-9]|3[01])|(19|20)[0-9]{2}[/-]02[/-](0[1-9]|1[0-9]|2[0-8])))

игр

2016-02-29 | 2012-04-30 | 2019/09/31

Не Соответствует

2016-02-30 | 2012-04-31 | 2019/09/35

вы можете настроить его, если вы хотите разрешить только '/' или '-' сепараторы. Это регулярное выражение строго контролирует срок действия даты и проверяет 28,30 и 31 дней месяцев, даже високосные годы с 29/02 месяца.

попробуйте, он работает очень хорошо и предотвратить ваш код от большого количества ошибок !

FYI: я сделал вариант для SQL datetime. Вы найдете его там (ищите мое имя):регулярное выражение для проверки метку

обратная связь приветствуются :)

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

на Perl Расширенная версия

внимание /x модификатор.

/^(
      (
        ( # 31 day months
            (0[13578])
          | ([13578])
          | (1[02])
        )
        [\/]
        (
            ([1-9])
          | ([0-2][0-9])
          | (3[01])
        )
      )
    | (
        ( # 30 day months
            (0[469])
          | ([469])
          | (11)
        )
        [\/]
        (
            ([1-9])
          | ([0-2][0-9])
          | (30)
        )
      )
    | ( # 29 day month (Feb)
        (2|02)
        [\/]
        (
            ([1-9])
          | ([0-2][0-9])
        )
      )
    )
    [\/]
    # year
    \d{4}$

  | ^\d{4}$ # year only
/x

Оригинал

^((((0[13578])|([13578])|(1[02]))[\/](([1-9])|([0-2][0-9])|(3[01])))|(((0[469])|([469])|(11))[\/](([1-9])|([0-2][0-9])|(30)))|((2|02)[\/](([1-9])|([0-2][0-9]))))[\/]\d{4}$|^\d{4}$

Если вы не получили эти выше предложения работают, я использую это, так как он получает любую дату я запустил это выражение через 50 ссылок, и он получил все даты на каждой странице.

^20\d\d-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(0[1-9]|[1-2][0-9]|3[01])$ 
    var dtRegex = new RegExp(/[1-9\-]{4}[0-9\-]{2}[0-9\-]{2}/);
    if(dtRegex.test(date) == true){
        var evalDate = date.split('-');
        if(evalDate[0] != '0000' && evalDate[1] != '00' && evalDate[2] != '00'){
            return true;
        }
    }

это регулярное выражение проверяет даты между 01-01-2000 и 12-31-2099 с соответствующими разделителями.

^(0[1-9]|1[012])([- /.])(0[1-9]|[12][0-9]|3[01])(19|20)\d\d$

Perl 6 version

rx{
  ^

  $<month> = (\d ** 1..2)
  { $<month> <= 12 or fail }

  '/'

  $<day> = (\d ** 1..2)
  {
    given( +$<month> ){
      when 1|3|5|7|8|10|12 {
        $<day> <= 31 or fail
      }
      when 4|6|9|11 {
        $<day> <= 30 or fail
      }
      when 2 {
        $<day> <= 29 or fail
      }
      default { fail }
    }
  }

  '/'

  $<year> = (\d ** 4)

  $
}

после того, как вы используете это, чтобы проверить входные значения доступны в $/ или $<month>,$<day>,$<year>. ( это просто синтаксис для доступа к значениям в $/)

не было предпринято никаких попыток проверить год, или что он не соответствует 29 февраля на не високосные годы.

регулярное выражение не предназначалось для проверки диапазонов чисел (это число должно быть от 1 до 5, когда число, предшествующее ему, оказывается 2, а число, предшествующее, оказывается ниже 6). Просто посмотрите на шаблон размещения чисел в регулярном выражении. Если вам нужно проверить качества даты, поместите ее в объект даты js/c#/vb и interogate числа там.

Я знаю, что это не ответ на ваш вопрос, но почему бы вам не использовать процедуру обработки даты, чтобы проверить, если это действительная дата? Даже если вы измените регулярное выражение с отрицательным условное выражение как (?!31/0?2) (т. е. не совпадают 31/2 или 31/02) у вас все равно будет проблема принятия 29 02 на не високосных годах и о формате даты с одним разделителем.

проблема не проста если вы хотите действительно проверить дату, проверьте это ветке форума.

для пример или лучший способ в C#, проверить этой ссылке

Если вы используете другую платформу/язык, дайте нам знать

Если вы собираетесь настаивать на этом с регулярным выражением, я бы рекомендовал что-то вроде:

( (0?1|0?3| <...> |10|11|12) / (0?1| <...> |30|31) |
  0?2 / (0?1| <...> |28|29) ) 
/ (19|20)[0-9]{2}

этой может сделать возможным читать и понимать.

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

Я в php.

проект, к которому это относится, никогда не будет иметь даты до 1 января 2008 года. Итак, я беру "дата" ввода и использования функции strtotime(). Если ответ >= 1199167200, то у меня есть дата, которая мне полезна. Если что-то, что не похоже на дату вводится -1 возвращается. Если введен null, он возвращает номер сегодняшней даты, поэтому вам нужна проверка на ненулевое значение вход первый.

работает для моей ситуации, возможно, ваш тоже?

Comments

    Ничего не найдено.