73

日付を検証する正規表現を作成しようとしています。正規表現は次のものと一致する必要があります

  • 月/日/YYYY
  • MM/DD/YYYY
  • 1 桁の月は先行ゼロで開始できます (例: 03/12/2008)
  • 1 桁の日は先行ゼロで始めることができます (例: 3/02/2008)
  • 2 月 30 日または 2 月 31 日を含めることはできません (例: 2008 年 2 月 31 日)

これまでのところ、

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

これは、2008 年 2 月 30 日と 2008 年 2 月 31 日がまだ含まれていることを除いて、適切に一致します。

誰かがより良い提案をしていますか?

編集: 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 形式に従うすべての有効な月に一致します。

助けてくれてありがとう。

4

16 に答える 16

147

これは正規表現の適切な使用法ではありません。使ったほうがいい

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

次に、高水準言語で範囲をチェックします。

于 2008-09-09T04:37:15.310 に答える
60

うるう年を含むすべての有効な日付に一致する正規表現を次に示します。受け入れられる形式 mm/dd/yyyy または mm-dd-yyyy または mm.dd.yyyy 形式

^(?:(?:(?:0?[13578]|1[02])(\/|-|\.)31)\1|(?:(?:0?[1,3-9]|1[0-2])(\/|-|\.)(?:29|30)\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:0?2(\/|-|\.)29\3(?:(?:(?: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])\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$

礼儀アシク・アハメド

于 2012-01-07T07:58:11.307 に答える
38

I landed here because the title of this question is broad and I was looking for a regex that I could use to match on a specific date format (like the OP). But I then discovered, as many of the answers and comments have comprehensively highlighted, there are many pitfalls that make constructing an effective pattern very tricky when extracting dates that are mixed-in with poor quality or non-structured source data.

In my exploration of the issues, I have come up with a system that enables you to build a regular expression by arranging together four simpler sub-expressions that match on the delimiter, and valid ranges for the year, month and day fields in the order you require.

These are :-

Delimeters

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

This will match anything that is not a word character, digit character, carriage return, new line or colon. The colon has to be there to prevent matching on times that look like dates (see my test Data)

You can optimise this part of the pattern to speed up matching, but this is a good foundation that detects most valid delimiters.

Note however; It will match a string with mixed delimiters like this 2/12-73 that may not actually be a valid date.

Year Values

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

This matches a group of two or 4 digits, in most cases this is acceptable, but if you're dealing with data from the years 0-999 or beyond 9999 you need to decide how to handle that because in most cases a 1, 3 or >4 digit year is garbage.

Month Values

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

Matches any number between 1 and 12 with or without a leading zero - note: 0 and 00 is not matched.

Date Values

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

Matches any number between 1 and 31 with or without a leading zero - note: 0 and 00 is not matched.

This expression matches Date, Month, Year formatted dates

(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})

But it will also match some of the Year, Month Date ones. It should also be bookended with the boundary operators to ensure the whole date string is selected and prevent valid sub-dates being extracted from data that is not well-formed i.e. without boundary tags 20/12/194 matches as 20/12/19 and 101/12/1974 matches as 01/12/1974

Compare the results of the next expression to the one above with the test data in the nonsense section (below)

\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

There's no validation in this regex so a well-formed but invalid date such as 31/02/2001 would be matched. That is a data quality issue, and as others have said, your regex shouldn't need to validate the data.

Because you (as a developer) can't guarantee the quality of the source data you do need to perform and handle additional validation in your code, if you try to match and validate the data in the RegEx it gets very messy and becomes difficult to support without very concise documentation.

Garbage in, garbage out.

Having said that, if you do have mixed formats where the date values vary, and you have to extract as much as you can; You can combine a couple of expressions together like so;

This (disastrous) expression matches DMY and YMD dates

(\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)

BUT you won't be able to tell if dates like 6/9/1973 are the 6th of September or the 9th of June. I'm struggling to think of a scenario where that is not going to cause a problem somewhere down the line, it's bad practice and you shouldn't have to deal with it like that - find the data owner and hit them with the governance hammer.

Finally, if you want to match a YYYYMMDD string with no delimiters you can take some of the uncertainty out and the expression looks like this

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

But note again, it will match on well-formed but invalid values like 20010231 (31th Feb!) :)

Test data

In experimenting with the solutions in this thread I ended up with a test data set that includes a variety of valid and non-valid dates and some tricky situations where you may or may not want to match i.e. Times that could match as dates and dates on multiple lines.

I hope this is useful to someone.

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
12\24\1974
12\30\1974
1\31\1974
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
于 2016-10-28T16:45:10.893 に答える
13

保守可能な 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}";

(年の値を制限する試みは行われていません。)

于 2008-09-13T21:28:43.710 に答える
4

この目的のために正規表現を拡張しすぎているようです。私がすることは、正規表現を使用していくつかの日付形式を照合し、別の関数を使用して抽出された日付フィールドの値を検証することです。

于 2008-09-09T04:34:14.243 に答える
3

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}$
于 2008-09-13T20:56:42.570 に答える
3

上記の提案が機能しない場合は、これを使用します。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])$ 
于 2012-01-21T00:03:21.327 に答える
1

これがあなたの質問に答えていないことはわかっていますが、日付処理ルーチンを使用して、有効な日付かどうかを確認してみませんか? (?!31/0?2) のような否定的な先読みアサーションで正規表現を変更しても (つまり、31/2 または 31/02 と一致しない)、うるう年以外で 29 02 を受け入れるという問題が残ります。および単一の区切り日付形式について。

日付を実際に検証したい場合、この問題は簡単ではありません。このフォーラムのスレッドを確認してください。

例またはより良い方法については、C# で、このリンクを確認してください

別のプラットフォーム/言語を使用している場合は、お知らせください

于 2008-09-09T04:43:17.607 に答える
1

正規表現は、数値範囲を検証するためのものではありませんでした (この数値は、その前の数値がたまたま 2 であり、その前の数値がたまたま 6 未満の場合、1 から 5 でなければなりません)。正規表現での数字の配置パターンを探してください。日付の品質を検証する必要がある場合は、それを日付オブジェクト js/c#/vb に入れ、そこで数値を調べます。

于 2008-09-09T04:36:22.380 に答える
1

Perl 6 バージョン

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>$/

年を確認しようとしたり、うるう年以外の 2 月 29 日と一致しないことを確認したりしていません。

于 2008-09-13T21:42:45.657 に答える
0

これを正規表現で行うことに固執する場合は、次のようなものをお勧めします。

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

これにより、読んで理解することが可能になるかもしれません。

于 2008-09-09T04:45:27.647 に答える
0

/(([1-9]{1}|0[1-9]|1[0-2])\/(0[1-9]|[1-9]{1}|[12]\d|3[01])\/[12]\d{3})/

これは、次の場合に検証されます-

  • 1 ~ 31 の範囲の 1 桁または 2 桁の日。例: 1、01、11、31。
  • 1 ~ 12 の範囲の 1 桁または 2 桁の月。1、01、12。
  • 4 桁の年。例えば。2021 年、1980 年。
于 2021-09-11T04:50:15.827 に答える
-1

あなたにとって役立つかもしれないし、役に立たないかもしれない、わずかに異なるアプローチ。

私はphpにいます。

これに関連するプロジェクトの日付が 2008 年 1 月 1 日より前になることはありません。答えが >= 1199167200 の場合、私にとって有益な日付があります。日付のように見えないものを入力すると、-1 が返されます。null が入力された場合、今日の日付番号が返されるため、最初に null 以外のエントリをチェックする必要があります。

私の状況、おそらくあなたの状況でも機能しますか?

于 2008-10-21T12:56:45.993 に答える