Here I wrote one for dd/mm/yyyy where separator can be one of -.,/ year range 0000-9999.
It deals with leap years and is designed for regex flavors, that support lookaheads, capturing groups and backreferences. NOT valid for such as d/m/yyyy. If needed add further separators to [-.,/]
^(?=\d{2}([-.,\/])\d{2}\1\d{4}$)(?:0[1-9]|1\d|[2][0-8]|29(?!.02.(?!(?!(?:[02468][1-35-79]|[13579][0-13-57-9])00)\d{2}(?:[02468][048]|[13579][26])))|30(?!.02)|31(?=.(?:0[13578]|10|12))).(?:0[1-9]|1[012]).\d{4}$
Test at regex101; as a Java string:
"^(?=\\d{2}([-.,\\/])\\d{2}\\1\\d{4}$)(?:0[1-9]|1\\d|[2][0-8]|29(?!.02.(?!(?!(?:[02468][1-35-79]|[13579][0-13-57-9])00)\\d{2}(?:[02468][048]|[13579][26])))|30(?!.02)|31(?=.(?:0[13578]|10|12))).(?:0[1-9]|1[012]).\\d{4}$"
explained:
(?x) # modifier x: free spacing mode (for comments)
     # verify date dd/mm/yyyy; possible separators: -.,/
     # valid year range: 0000-9999
^    # start anchor
# precheck xx-xx-xxxx,... add new separators here
(?=\d{2}([-.,\/])\d{2}\1\d{4}$)
(?:  # day-check: non caturing group
  # days 01-28
  0[1-9]|1\d|[2][0-8]| 
  # february 29d check for leap year: all 4y / 00 years: only each 400
  # 0400,0800,1200,1600,2000,...
  29
  (?!.02. # not if feb: if not ...
    (?!
      # 00 years: exclude !0 %400 years
      (?!(?:[02468][1-35-79]|[13579][0-13-57-9])00)
      # 00,04,08,12,... 
      \d{2}(?:[02468][048]|[13579][26])
    )
  )|
  # d30 negative lookahead: february cannot have 30 days
  30(?!.02)|
  # d31 positive lookahead: month up to 31 days
  31(?=.(?:0[13578]|10|12))
) # eof day-check
# month 01-12
.(?:0[1-9]|1[012])
# year 0000-9999
.\d{4}
$ # end anchor
Also see SO Regex FAQ; Please let me know, if it fails.