簡體   English   中英

正則表達式匹配有效日期

[英]Regular Expression to match valid dates

我正在嘗試編寫一個驗證日期的正則表達式。 正則表達式需要匹配以下內容

  • 月/日/年
  • 月/日/年
  • 個位數月份可以以前導零開頭(例如:03/12/2008)
  • 個位數的日期可以以前導零開頭(例如:3/02/2008)
  • 不能包括 2 月 30 日或 2 月 31 日(例如:2/31/2008)

到目前為止我有

^(([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)$

這正確匹配,除了它仍然包括 2/30/2008 和 2/31/2008。

有人有更好的建議嗎?

編輯:我在 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 格式的有效月份。

感謝大家的幫助。

這不是正則表達式的適當使用。 你最好使用

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

然后用高級語言檢查范圍。

這是匹配所有有效日期(包括閏年)的 Reg ex。 接受的格式為 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})$

禮貌阿西克·艾哈邁德

我來到這里是因為這個問題的標題很寬泛,我正在尋找一個正則表達式,我可以用它來匹配特定的日期格式(如 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。 這是一個數據質量問題,正如其他人所說,您的正則表達式不需要驗證數據。

因為您(作為開發人員)無法保證您需要在代碼中執行和處理額外驗證的源數據的質量,如果您嘗試匹配驗證 RegEx 中的數據,它會變得非常混亂並且變得難以支持沒有非常簡潔的文檔。

垃圾進垃圾出。

話雖如此,如果您確實有日期值不同的混合格式,則必須盡可能多地提取; 您可以像這樣將幾個表達式組合在一起;

這個(災難性的)表達式匹配 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 之類的日期是 9 月 6 日還是 6 月 9 日。 我正在努力想出一種不會在某處造成問題的場景,這是不好的做法,您不應該像那樣處理它 - 找到數據所有者並用治理錘擊打他們.

最后,如果你想匹配一個沒有分隔符的 YYYYMMDD 字符串,你可以消除一些不確定性,表達式看起來像這樣

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

但請再次注意,它將匹配格式正確但無效的值,例如 20010231(2 月 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
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

可維護的 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}";

(沒有嘗試限制年份的值。)

按照以下格式控制日期有效性:

YYYY/MM/DD 或 YYYY-MM-DD

我建議您使用以下正則表達式:

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

如果您只想允許使用 '/' 或 '-' 分隔符,您可以自定義它。 這個 RegEx 嚴格控制日期的有效性並驗證 28,30 和 31 天月份,甚至是閏年 29/02 月份。

試試吧,它工作得很好,可以防止你的代碼出現很多錯誤!

僅供參考:我為 SQL 日期時間制作了一個變體。 你會在那里找到它(尋找我的名字): 正則表達式來驗證時間戳

歡迎反饋:)

聽起來您為此目的過度擴展了正則表達式。 我會做的是使用正則表達式來匹配一些日期格式,然后使用單獨的函數來驗證如此提取的日期字段的值。

此正則表達式使用匹配的分隔符驗證 01-01-2000 和 12-31-2099 之間的日期。

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

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

正則表達式不是為了驗證數字范圍(當它前面的數字恰好是 2 並且它前面的數字恰好低於 6 時,這個數字必須是從 1 到 5)。 只需在正則表達式中查找數字的放置模式即可。 如果您需要驗證日期的質量,請將其放入日期對象 js/c#/vb 中,並在那里查詢數字。

我知道這不能回答您的問題,但是您為什么不使用日期處理程序來檢查它是否是有效日期? 即使您使用 (?!31/0?2) 之類的否定前瞻斷言修改正則表達式(即,不匹配 31/2 或 31/02),您仍然會遇到在非閏年接受 29 02 的問題以及關於單個分隔符日期格式。

如果您想真正驗證日期,這個問題並不容易,請查看此 論壇主題

有關示例或更好的方法,在 C# 中,請查看此鏈接

如果您使用其他平台/語言,請告訴我們

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 日不匹配。

如果您要堅持使用正則表達式執行此操作,我建議您使用以下方法:

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

可能使閱讀和理解成為可能。

/(([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、01、11、31。
  • 單數和 2 位數月份,范圍從 1 到 12。例如。 1、01、12。
  • 4 位數字年份。 例如。 2021 年,1980 年。

一種稍微不同的方法,可能對您有用,也可能沒有用。

我在 php 中。

與此相關的項目永遠不會有 2008 年 1 月 1 日之前的日期。因此,我采用輸入的“日期”並使用 strtotime()。 如果答案是 >= 1199167200 那么我就有一個對我有用的日期。 如果輸入了看起來不像日期的內容,則返回 -1。 如果輸入空值,它會返回今天的日期編號,因此您需要先檢查非空條目。

適用於我的情況,也許你的也適用?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM