Working with dates using Regex Named Capture Groups

#javascript

A feature added in ES2018 makes working with dates in different formats a bit easier

I just want to write a quick post on how to work with dates in different formats more easily with a relatively new Regex featured added in ES2018 called Regex Named Capture Groups.

Many formats of dates#

The date and time representation varies between countries. In China, pretty much everyone writes date in the format of YYYY-MM-DD (e.g. 2022/08/04) while a lot of Americans write dates in the DD-MM-YYYY format. And lastly there is the MM-DD-YYYY format. I live in Canada and I have seen all three formats used.

Conversion between different date formats#

Sometimes you need to convert one format to another format. The most obvious way to do it is via Regex. For example, if I have a date in the MDY (MM-DD-YYYY) format and I want it to be in the DMY (DD-MM-YYYY) format, I might write the following function for that:

function toLocalDateFormat(date) {
  return date.replace(/(\d{2})-(\d{2})-(\d{4})/, "$2-$1-$3")
}

const dateInMDY = '08-03-2022'
const dateInDMY = toMyLocalDateFormat(dateInMDY) // '03-08-2022'

This solution works. However, by merely looking at the Regex used for the conversion, it's hard to tell whether the conversion is from MDY to DMY or the other around. Of course we can add comments to document that. But it is better to have descriptive names than comments, as Clean Code has pointed out. Is there a way to attach some sort of identifier/names for each group of characters we want to capture?

Enter named capture groups#

Turns out there is a way to give descriptive names to certain portions of a string that a Regex matches: we can use Named Capture Groups. the syntax for assigning an identifier (think of variable names) to a certain group is ?<name> , where name can be anything, including reserved words such as if or var, as long as it is unique amongst other identifiers used in the Regex named capture groups. After that, we can backreference the named group via \k<name> . To replace it from String.prototype.replace, we can use $<name> .

Let's rewrite the toLocalDateFormat with named capture groups:

function toLocalDateFormat(date) {
  return date.replace(
    /(?<month>\d{2})-(?<day>\d{2})-(?<year>\d{4})/,
    '$<day>-$<month>-$<year>'
  )
}

Now, with the named groups in our Regex, it becomes pretty clear that we are converting the MDY format to the DMY format.

Let's write another function to sort dates in different formats with regex named capture groups:

const getSortDate = (dateFormat) => {
  const regexByDateFormat = {
    MDY: /(?<month>\d{2})-(?<day>\d{2})-(?<year>\d{4})/,
    DMY: /(?<day>\d{2})-(?<month>\d{2})-(?<year>\d{4})/,
    YMD: /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
  }

  return (dateA, dateB) => {
    const {
      day: dayA,
      month: monthA,
      year: yearA,
    } = dateA.match(regexByDateFormat[dateFormat]).groups

    const {
      day: dayB,
      month: monthB,
      year: yearB,
    } = dateB.match(regexByDateFormat[dateFormat]).groups

    return yearA - yearB || monthA - monthB || dayA - dayB
  }
}

const sortDateInDMY = getSortDate('DMY')
const sortDateInMDY = getSortDate('MDY')
const sortDateInYMD = getSortDate('YMD')

const datesInDMY = ['04-08-2022', '21-09-2007', '30-05-2019', '01-05-2017']
const datesInMDY = ['04-08-2022', '11-09-2007', '01-05-2019', '01-09-2017']
const datesInYMD = ['1993-10-17', '2000-09-22', '2022-05-01', '2011-01-01']

datesInDMY.sort(sortDateInDMY) 
// [ '21-09-2007', '01-05-2017', '30-05-2019', '04-08-2022' ]

datesInMDY.sort(sortDateInMDY) 
// [ '11-09-2007', '01-09-2017', '01-05-2019', '04-08-2022' ]

datesInYMD.sort(sortDateInYMD) 
// [ '1993-10-17', '2000-09-22', '2011-01-01', '2022-05-01' ]

Further reading#

ES2018: RegExp named capture groups

New JavaScript Features That Will Change How You Write Regex