HEX
Server: Apache/2.4.52 (Ubuntu)
System: Linux spn-python 5.15.0-89-generic #99-Ubuntu SMP Mon Oct 30 20:42:41 UTC 2023 x86_64
User: arjun (1000)
PHP: 8.1.2-1ubuntu2.20
Disabled: NONE
Upload Files
File: //home/arjun/projects/buyercall/node_modules/date-format-parse/src/parse.ts
import { Unionize, PickByValue } from 'utility-types';
import { Locale } from './locale';
import defaultLocale from './locale/en';

import { startOfWeekYear } from './util';

const formattingTokens = /(\[[^\[]*\])|(MM?M?M?|Do|DD?|ddd?d?|w[o|w]?|YYYY|YY|a|A|hh?|HH?|mm?|ss?|S{1,3}|x|X|ZZ?|.)/g;

const match1 = /\d/; // 0 - 9
const match2 = /\d\d/; // 00 - 99
const match3 = /\d{3}/; // 000 - 999
const match4 = /\d{4}/; // 0000 - 9999
const match1to2 = /\d\d?/; // 0 - 99
const matchShortOffset = /[+-]\d\d:?\d\d/; // +00:00 -00:00 +0000 or -0000
const matchSigned = /[+-]?\d+/; // -inf - inf
const matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123

// const matchWord = /[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i; // Word

const YEAR = 'year';
const MONTH = 'month';
const DAY = 'day';
const HOUR = 'hour';
const MINUTE = 'minute';
const SECOND = 'second';
const MILLISECOND = 'millisecond';

export interface ParseFlagMark {
  year: number;
  month: number;
  day: number;
  hour: number;
  minute: number;
  second: number;
  millisecond: number;
  offset: number;
  weekday: number; // 0,1...6
  week: number; //  0 1... 52
  date: Date;
  isPM: boolean;
}

export type ParseFlagCallBackReturn = Unionize<ParseFlagMark>;

export type ParseFlagRegExp = RegExp | ((locale: Locale) => RegExp);
export type ParseFlagCallBack = (input: string, locale: Locale) => ParseFlagCallBackReturn;

export interface ParseFlag {
  [key: string]: [ParseFlagRegExp, ParseFlagCallBack];
}

const parseFlags: ParseFlag = {};

const addParseFlag = (
  token: string | string[],
  regex: ParseFlagRegExp,
  callback: ParseFlagCallBack | keyof PickByValue<ParseFlagMark, number>
) => {
  const tokens = Array.isArray(token) ? token : [token];
  let func: ParseFlagCallBack;
  if (typeof callback === 'string') {
    func = input => {
      const value = parseInt(input, 10);
      return { [callback]: value } as Unionize<PickByValue<ParseFlagMark, number>>;
    };
  } else {
    func = callback;
  }
  tokens.forEach(key => {
    parseFlags[key] = [regex, func];
  });
};

const escapeStringRegExp = (str: string) => {
  return str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
};

const matchWordRegExp = (localeKey: string) => {
  return (locale: Locale) => {
    const array = locale[localeKey];
    if (!Array.isArray(array)) {
      throw new Error(`Locale[${localeKey}] need an array`);
    }
    return new RegExp(array.map(escapeStringRegExp).join('|'));
  };
};

const matchWordCallback = (localeKey: string, key: 'month' | 'weekday') => {
  return (input: string, locale: Locale) => {
    const array = locale[localeKey];
    if (!Array.isArray(array)) {
      throw new Error(`Locale[${localeKey}] need an array`);
    }
    const index = array.indexOf(input);
    if (index < 0) {
      throw new Error('Invalid Word');
    }
    return { [key]: index } as Unionize<Pick<ParseFlagMark, 'month' | 'weekday'>>;
  };
};

addParseFlag('Y', matchSigned, YEAR);
addParseFlag('YY', match2, input => {
  const year = new Date().getFullYear();
  const cent = Math.floor(year / 100);
  let value = parseInt(input, 10);
  value = (value > 68 ? cent - 1 : cent) * 100 + value;
  return { [YEAR]: value };
});
addParseFlag('YYYY', match4, YEAR);
addParseFlag('M', match1to2, input => ({ [MONTH]: parseInt(input, 10) - 1 }));
addParseFlag('MM', match2, input => ({ [MONTH]: parseInt(input, 10) - 1 }));
addParseFlag('MMM', matchWordRegExp('monthsShort'), matchWordCallback('monthsShort', MONTH));
addParseFlag('MMMM', matchWordRegExp('months'), matchWordCallback('months', MONTH));
addParseFlag('D', match1to2, DAY);
addParseFlag('DD', match2, DAY);
addParseFlag(['H', 'h'], match1to2, HOUR);
addParseFlag(['HH', 'hh'], match2, HOUR);
addParseFlag('m', match1to2, MINUTE);
addParseFlag('mm', match2, MINUTE);
addParseFlag('s', match1to2, SECOND);
addParseFlag('ss', match2, SECOND);
addParseFlag('S', match1, input => {
  return {
    [MILLISECOND]: parseInt(input, 10) * 100,
  };
});
addParseFlag('SS', match2, input => {
  return {
    [MILLISECOND]: parseInt(input, 10) * 10,
  };
});
addParseFlag('SSS', match3, MILLISECOND);

function matchMeridiem(locale: Locale) {
  return locale.meridiemParse || /[ap]\.?m?\.?/i;
}

function defaultIsPM(input: string) {
  return `${input}`.toLowerCase().charAt(0) === 'p';
}

addParseFlag(['A', 'a'], matchMeridiem, (input, locale) => {
  const isPM = typeof locale.isPM === 'function' ? locale.isPM(input) : defaultIsPM(input);
  return { isPM };
});

function offsetFromString(str: string) {
  const [symbol, hour, minute] = str.match(/([+-]|\d\d)/g) || ['-', '0', '0'];
  const minutes = parseInt(hour, 10) * 60 + parseInt(minute, 10);
  if (minutes === 0) {
    return 0;
  }
  return symbol === '+' ? -minutes : +minutes;
}

addParseFlag(['Z', 'ZZ'], matchShortOffset, input => {
  return { offset: offsetFromString(input) };
});

addParseFlag('x', matchSigned, input => {
  return { date: new Date(parseInt(input, 10)) };
});

addParseFlag('X', matchTimestamp, input => {
  return { date: new Date(parseFloat(input) * 1000) };
});

addParseFlag('d', match1, 'weekday');
addParseFlag('dd', matchWordRegExp('weekdaysMin'), matchWordCallback('weekdaysMin', 'weekday'));
addParseFlag(
  'ddd',
  matchWordRegExp('weekdaysShort'),
  matchWordCallback('weekdaysShort', 'weekday')
);
addParseFlag('dddd', matchWordRegExp('weekdays'), matchWordCallback('weekdays', 'weekday'));

addParseFlag('w', match1to2, 'week');
addParseFlag('ww', match2, 'week');

function to24hour(hour?: number, isPM?: boolean) {
  if (hour !== undefined && isPM !== undefined) {
    if (isPM) {
      if (hour < 12) {
        return hour + 12;
      }
    } else if (hour === 12) {
      return 0;
    }
  }
  return hour;
}

type DateArgs = [number, number, number, number, number, number, number];

function getFullInputArray(input: Array<number | undefined>, backupDate = new Date()) {
  const result: DateArgs = [0, 0, 1, 0, 0, 0, 0];
  const backupArr = [
    backupDate.getFullYear(),
    backupDate.getMonth(),
    backupDate.getDate(),
    backupDate.getHours(),
    backupDate.getMinutes(),
    backupDate.getSeconds(),
    backupDate.getMilliseconds(),
  ];
  let useBackup = true;
  for (let i = 0; i < 7; i++) {
    if (input[i] === undefined) {
      result[i] = useBackup ? backupArr[i] : result[i];
    } else {
      result[i] = input[i]!;
      useBackup = false;
    }
  }
  return result;
}

function createDate(y: number, m: number, d: number, h: number, M: number, s: number, ms: number) {
  let date;
  if (y < 100 && y >= 0) {
    date = new Date(y + 400, m, d, h, M, s, ms);
    if (isFinite(date.getFullYear())) {
      date.setFullYear(y);
    }
  } else {
    date = new Date(y, m, d, h, M, s, ms);
  }

  return date;
}

function createUTCDate(...args: DateArgs) {
  let date: Date;
  const y = args[0];
  if (y < 100 && y >= 0) {
    args[0] += 400;
    date = new Date(Date.UTC(...args));
    // eslint-disable-next-line no-restricted-globals
    if (isFinite(date.getUTCFullYear())) {
      date.setUTCFullYear(y);
    }
  } else {
    date = new Date(Date.UTC(...args));
  }

  return date;
}

function makeParser(dateString: string, format: string, locale: Locale) {
  const tokens = format.match(formattingTokens);
  if (!tokens) {
    throw new Error();
  }
  const { length } = tokens;
  let mark: Partial<ParseFlagMark> = {};
  for (let i = 0; i < length; i += 1) {
    const token = tokens[i];
    const parseTo = parseFlags[token];
    if (!parseTo) {
      const word = token.replace(/^\[|\]$/g, '');
      if (dateString.indexOf(word) === 0) {
        dateString = dateString.substr(word.length);
      } else {
        throw new Error('not match');
      }
    } else {
      const regex = typeof parseTo[0] === 'function' ? parseTo[0](locale) : parseTo[0];
      const parser = parseTo[1];
      const value = (regex.exec(dateString) || [])[0];
      const obj = parser(value, locale);
      mark = { ...mark, ...obj };
      dateString = dateString.replace(value, '');
    }
  }
  return mark;
}

export function parse(
  str: string,
  format: string,
  options: { locale?: Locale; backupDate?: Date } = {}
) {
  try {
    const { locale = defaultLocale, backupDate = new Date() } = options;
    const parseResult = makeParser(str, format, locale);
    const {
      year,
      month,
      day,
      hour,
      minute,
      second,
      millisecond,
      isPM,
      date,
      offset,
      weekday,
      week,
    } = parseResult;
    if (date) {
      return date;
    }
    const inputArray = [year, month, day, hour, minute, second, millisecond];
    inputArray[3] = to24hour(inputArray[3], isPM);
    // check week
    if (week !== undefined && month === undefined && day === undefined) {
      // new Date(year, 3) make sure in current year
      const firstDate = startOfWeekYear(year === undefined ? backupDate : new Date(year, 3), {
        firstDayOfWeek: locale.firstDayOfWeek,
        firstWeekContainsDate: locale.firstWeekContainsDate,
      });
      return new Date(firstDate.getTime() + (week - 1) * 7 * 24 * 3600 * 1000);
    }

    let parsedDate: Date;

    const result = getFullInputArray(inputArray, backupDate);

    if (offset !== undefined) {
      result[6] += offset * 60 * 1000;
      parsedDate = createUTCDate(...result);
    } else {
      parsedDate = createDate(...result);
    }
    // check weekday
    if (weekday !== undefined && parsedDate.getDay() !== weekday) {
      return new Date(NaN);
    }
    return parsedDate;
  } catch (e) {
    return new Date(NaN);
  }
}