I had a similar usecase and I used and changed @lpg's answer to fit my needs.
I needed to parse invoice dates that will have all sorts of date formats.
I included an option to parse month names.
As I live in Belgium, most dates are formatted dd-mm-yyyy or dd-mmmm-yyyy , so I assumed dates will most likely have this format. It's easy to swap parts of the regex to swap months and days.
Some explanation is included in the comments of the code.
It's also easy enough to translate month names.
Note: In dutch we don't use the format "may 3th 2023" so I didn't include this.
I ended up using this:
function parseStringedDate(d) {
/*
Valid separators => . - / *space* (dot dash slash and space)
Detexted formats =>
dd mm yyyy
d mm yyyy
d m yyyy
yyyy mm dd
yyyy m d
d mmmm yyyy
mmmm d yyyy
*/
// Object to map month names to numbers
const monthsConfig = {
"januari": 0,
"februari": 1,
"maart": 2,
"april": 3,
"mei": 4,
"juni": 5,
"juli": 6,
"augustus": 7,
"september": 8,
"oktober": 9,
"november": 10,
"december": 11
};
var day, month, year, result, dateSplitted;
// dd-mm-yyyy || d-mm-yyyy || mm-dd-yyyy || m-d-yyyy || mm-d-yyyy || m-dd-yyyy
result = d.match("[0-9]{1,2}([\-/ \.])[0-9]{1,2}[\-/ \.][0-9]{4}");
if (null != result) {
dateSplitted = result[0].split(result[1]);
day = parseInt(dateSplitted[0]);
month = parseInt(dateSplitted[1]) - 1;
year = parseInt(dateSplitted[2]);
}
// yyyy-mm-dd || yyyy-mm-d || yyyy-m-dd || yyyy-m-d || yyyy-mm-d || yyyy-dd-m || yyyy-d-mm
result = d.match("[0-9]{4}([\-/ \.])[0-9]{1,2}[\-/ \.][0-9]{1,2}");
if (null != result) {
dateSplitted = result[0].split(result[1]);
day = dateSplitted[2];
month = parseInt(dateSplitted[1]) - 1;
year = dateSplitted[0];
}
// dd-mmmm-yyyy || d-mmmm-yyyy
result = d.match("[0-9]{1,2}([\-/ \.])[a-zA-Z]{3,}[\-/ \.][0-9]{4}");
if (null != result) {
dateSplitted = result[0].split(result[1]);
day = dateSplitted[0];
month = monthsConfig[dateSplitted[1]]
year = dateSplitted[2];
}
// mmmm-dd-yyyy
result = d.match("[a-zA-Z]{3,}[\-/ \.][0-9]{1,2}([\-/ \.])[0-9]{4}");
if (null != result) {
dateSplitted = result[0].split(result[1]);
month = monthsConfig[dateSplitted[0]]
day = dateSplitted[1];
year = dateSplitted[2];
}
day = parseInt(day)
month = parseInt(month)
year = parseInt(year)
if (!day || month === undefined || !year) {
throw new Error("Invalid date format...")
}
// If the month is larger then 11, it is not a month, but a day for sure. Swap variables...
if (month > 11) {
let aux
aux = day;
day = month;
month = aux;
month-- // what was tought to be a day, was actually a month, so we need to substract 1, as months are zero based.
day++ // restore the day, we accidently changed the day -1 in the previous code because we tought it was a month.
}
const jsDate = new Date(year, month, day)
if (!jsDate instanceof Date) {
throw new Error("Invalid date format...")
}
return jsDate.toISOString().split('T')[0]
}