function generateRegex(formatSpec) {
        var matcher = {};
        if (formatSpec.type === 'datetime') {
            matcher.type = 'datetime';
            matcher.parts = formatSpec.parts.map(function (part) {
                var res = {};
                if (part.type === 'literal') {
                    res.regex = part.value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
                } else if (part.component === 'Z' || part.component === 'z') {
                    // timezone
                    let separator;
                    if (!Array.isArray(part.integerFormat.groupingSeparators)) {
                        separator = part.integerFormat.groupingSeparators;
                    }
                    res.regex = '';
                    if (part.component === 'z') {
                        res.regex = 'GMT';
                    }
                    res.regex += '[-+][0-9]+';
                    if (separator) {
                        res.regex += separator.character + '[0-9]+';
                    }
                    res.parse = function(value) {
                        if (part.component === 'z') {
                            value = value.substring(3); // remove the leading GMT
                        }
                        let offsetHours = 0, offsetMinutes = 0;
                        if (separator) {
                            offsetHours = Number.parseInt(value.substring(0, value.indexOf(separator.character)));
                            offsetMinutes = Number.parseInt(value.substring(value.indexOf(separator.character) + 1));
                        } else {
                            // depends on number of digits
                            const numdigits = value.length - 1;
                            if (numdigits <= 2) {
                                // just hour offset
                                offsetHours = Number.parseInt(value);
                            } else {
                                offsetHours = Number.parseInt(value.substring(0, 3));
                                offsetMinutes = Number.parseInt(value.substring(3));
                            }
                        }
                        return offsetHours * 60 + offsetMinutes;
                    };
                } else if (part.integerFormat) {
                    res = generateRegex(part.integerFormat);
                } else {
                    // must be a month or day name
                    res.regex = '[a-zA-Z]+';
                    var lookup = {};
                    if (part.component === 'M' || part.component === 'x') {
                        // months
                        months.forEach(function (name, index) {
                            if (part.width && part.width.max) {
                                lookup[name.substring(0, part.width.max)] = index + 1;
                            } else {
                                lookup[name] = index + 1;
                            }
                        });
                    } else if (part.component === 'F') {
                        // days
                        days.forEach(function (name, index) {
                            if (index > 0) {
                                if (part.width && part.width.max) {
                                    lookup[name.substring(0, part.width.max)] = index;
                                } else {
                                    lookup[name] = index;
                                }
                            }
                        });
                    } else if (part.component === 'P') {
                        lookup = {'am': 0, 'AM': 0, 'pm': 1, 'PM': 1};
                    } else {
                        // unsupported 'name' option for this component
                        throw {
                            code: 'D3133',
                            value: part.component
                        };
                    }
                    res.parse = function (value) {
                        return lookup[value];
                    };
                }
                res.component = part.component;
                return res;
            });
        } else { // type === 'integer'
            matcher.type = 'integer';
            const isUpper = formatSpec.case === tcase.UPPER;

            switch (formatSpec.primary) {
                case formats.LETTERS:
                    matcher.regex = isUpper ? '[A-Z]+' : '[a-z]+';
                    matcher.parse = function (value) {
                        return lettersToDecimal(value, isUpper ? 'A' : 'a');
                    };
                    break;
                case formats.ROMAN:
                    matcher.regex = isUpper ? '[MDCLXVI]+' : '[mdclxvi]+';
                    matcher.parse = function (value) {
                        return romanToDecimal(isUpper ? value : value.toUpperCase());
                    };
                    break;
                case formats.WORDS:
                    matcher.regex = '(?:' + Object.keys(wordValues).concat('and', '[\\-, ]').join('|') + ')+';
                    matcher.parse = function (value) {
                        return wordsToNumber(value.toLowerCase());
                    };
                    break;
                case formats.DECIMAL:
                    matcher.regex = '[0-9]';
                    if (formatSpec.parseWidth) {
                        matcher.regex += `{${formatSpec.parseWidth}}`;
                    } else {
                        matcher.regex += '+';
                    }
                    if (formatSpec.ordinal) {
                        // ordinals
                        matcher.regex += '(?:th|st|nd|rd)';
                    }
                    matcher.parse = function (value) {
                        let digits = value;
                        if (formatSpec.ordinal) {
                            // strip off the suffix
                            digits = value.substring(0, value.length - 2);
                        }
                        // strip out the separators
                        if (formatSpec.regular) {
                            digits = digits.split(',').join('');
                        } else {
                            formatSpec.groupingSeparators.forEach(sep => {
                                digits = digits.split(sep.character).join('');
                            });
                        }
                        if (formatSpec.zeroCode !== 0x30) {
                            // apply offset
                            digits = digits.split('').map(char => String.fromCodePoint(char.codePointAt(0) - formatSpec.zeroCode + 0x30)).join('');
                        }
                        return parseInt(digits);
                    };
                    break;
                case formats.SEQUENCE:
                    throw {
                        code: 'D3130',
                        value: formatSpec.token
                    };
            }

        }
        return matcher;
    }
Generate a regex to parse integers or timestamps
@param {object} formatSpec - object representing the format
@returns {object} - regex