import parseDate from 'date-fns/parse'; const TIME_REGEX = /^((\d{1,2})((:|\.)?(\d{1,2}))?)(a|p|(am|a\.m\.|midnight|mi|pm|p\.m\.|noon|n))?$/; const ALTERNATIVE_AM_MERIDIEMS_SET = new Set(['am', 'a.m.', 'midnight', 'mi']); const ALTERNATIVE_PM_MERIDIEMS_SET = new Set(['pm', 'p.m.', 'noon', 'n']); const TimeFormats = { TWENTY_FOUR_HOUR: 'twentyFourHour', TWELVE_HOUR: 'twelveHour', }; const PATTERNS_GROUPS_BY_TIME_FORMAT = { [TimeFormats.TWENTY_FOUR_HOUR]: { byNumbersTotal: { 1: ['H'], 2: ['HH'], 3: ['Hmm'], 4: ['HHmm'], }, withDelimiter: ['H:m', 'H:mm', 'HH:m', 'HH:mm'], }, [TimeFormats.TWELVE_HOUR]: { byNumbersTotal: { 1: ['haaaaa'], 2: ['hhaaaaa'], 3: ['hmmaaaaa'], 4: ['hhmmaaaaa'], }, withDelimiter: ['h:maaaaa', 'h:mmaaaaa', 'hh:maaaaa', 'hh:mmaaaaa'], }, }; const INVALID_DATE = new Date('invalid-date'); const normalizeDelimeter = (delimeter) => (delimeter === '.' ? ':' : delimeter); const normalizeMeridiem = (meridiem, alternativeMeridiem) => { if (meridiem && alternativeMeridiem) { if (ALTERNATIVE_AM_MERIDIEMS_SET.has(alternativeMeridiem)) { return 'a'; } if (ALTERNATIVE_PM_MERIDIEMS_SET.has(alternativeMeridiem)) { return 'p'; } } return meridiem; }; const makeTimeString = (hours, minutes, delimeter, meridiem) => { let timeString = hours; if (delimeter) { timeString += delimeter; } if (minutes) { timeString += minutes; } if (meridiem) { timeString += meridiem; } return timeString; }; export default (string, referenceDate) => { const match = string.replace(/\s/g, '').toLowerCase().match(TIME_REGEX); if (!match) { return INVALID_DATE; } const [, hoursAndMinutes, hours, , delimeter, minutes, meridiem, alternativeMeridiem] = match; const normalizedDelimeter = normalizeDelimeter(delimeter); const normalizedMeridiem = normalizeMeridiem(meridiem, alternativeMeridiem); const timeString = makeTimeString(hours, minutes, normalizedDelimeter, normalizedMeridiem); const timeFormat = meridiem ? TimeFormats.TWELVE_HOUR : TimeFormats.TWENTY_FOUR_HOUR; const patternsGroups = PATTERNS_GROUPS_BY_TIME_FORMAT[timeFormat]; const patterns = delimeter ? patternsGroups.withDelimiter : patternsGroups.byNumbersTotal[hoursAndMinutes.length]; if (!referenceDate) { referenceDate = new Date(); // eslint-disable-line no-param-reassign } for (let i = 0; i < patterns.length; i += 1) { const parsedDate = parseDate(timeString, patterns[i], referenceDate); if (!Number.isNaN(parsedDate.getTime())) { return parsedDate; } } return INVALID_DATE; };