Before I answer this, I would like to say that there are better ways to validate this, other than using regex, as February isn't a simple month to get right, some years it will have 28 days, others it will have 29 days, and it also isn't as simple as just "every four years February will have 29 days" either. The year 2100, you would expect to be a leap year under this pretext, but it is not, because it is not divisible by 400.
You're better off using an actual date library for this, as there are a lot of variables when it comes to the dates, that the regex will simply be a bad tool for.
With that out of the way, here is my answer:
(?:(?:(?:[0-2]\d)\.(?:0\d|1[12]))|(?:30\.(?:0[469]|11))|(?:3[01]\.(?:0[13578]|1[02])))\.\d+(?:, (?:[01]\d|2[0-4])\:[0-5]\d)?$
This should match dates in the format that you wish it to.
See it working on Regex101
Edit
I just did a bit of messing around with the regex to better provide functionality for this. This function will do a double check for leap years, and return a boolean for whether the date is valid or not.
function isValidDate(date) {
let regex = /^(?:((?:[0-2]\d)\.(?:0\d|1[12]))|(30\.(?:0[469]|11))|(3[01]\.(?:0[13578]|1[02])))\.(\d+)(?:, (?:[01]\d|2[0-4])\:[0-5]\d)?$/,
matches = date.match(regex);
if (!matches) {
return false;
}
let dayMonth = matches[1] || matches[2] || matches[3],
year = matches[4],
valid = !dayMonth || !year ? false : true;
if (dayMonth == "29.02") {
let divisibleBy = {four: year % 4 === 0, oneHundred: year % 100 === 0, fourHundred: year % 400 === 0 };
if (year > 400) {
if (!divisibleBy.four) {
valid = false;
}
if (divisibleBy.four && divisibleBy.oneHundred && !divisibleBy.fourHundred) {
valid = false;
}
}
}
return valid;
}
console.log(
isValidDate("29.02.2100"),
isValidDate("29.02.2000"),
isValidDate("25.12.2017, 22:00"),
isValidDate("31.05.1678"),
isValidDate("32.05.1900"),
isValidDate("29.02.32000, 06:15")
)