<commit_before>/* eslint no-fallthrough: 0 */

import { isDate } from './is.js'
import { pad, capitalize } from './format.js'
import lang from '../lang.js'

const
  MILLISECONDS_IN_DAY = 86400000,
  MILLISECONDS_IN_HOUR = 3600000,
  MILLISECONDS_IN_MINUTE = 60000,
  token = /\[((?:[^\]\\]|\\]|\\)*)\]|d{1,4}|M{1,4}|m{1,2}|w{1,2}|Qo|Do|D{1,4}|YY(?:YY)?|H{1,2}|h{1,2}|s{1,2}|S{1,3}|Z{1,2}|a{1,2}|[AQExX]/g,
  parseConvertFns = {}

function formatTimezone (offset, delimeter = '') {
  const
    sign = offset > 0 ? '-' : '+',
    absOffset = Math.abs(offset),
    hours = Math.floor(absOffset / 60),
    minutes = absOffset % 60

  return sign + pad(hours) + delimeter + pad(minutes)
}

function setMonth (date, newMonth /* 1-based */) {
  const
    test = new Date(date.getFullYear(), newMonth, 0, 0, 0, 0, 0),
    days = test.getDate()

  date.setMonth(newMonth - 1, Math.min(days, date.getDate()))
}

function getChange (date, mod, add) {
  const
    t = new Date(date),
    sign = (add ? 1 : -1)

  Object.keys(mod).forEach(key => {
    if (key === 'month') {
      setMonth(t, t.getMonth() + 1 + sign * mod.month)
      return
    }

    const op = key === 'year'
      ? 'FullYear'
      : capitalize(key === 'days' ? 'date' : key)
    t[`set${op}`](t[`get${op}`]() + sign * mod[key])
  })
  return t
}

function maskToRegex (mask, opts = {}) {
  let index = 0

  const
    maskMap = {
      year: null,
      month: null,
      day: null
    },
    maskText = mask.replace(token, function (match, text) {
      switch (match) {
        case 'YYYY':
        case 'YY':
          maskMap.year = (++index)
          return '(-?\\d*)'
        case 'MM':
        case 'M':
          maskMap.month = (++index)
          return '(\\d{0,2})'
        case 'MMMM':
          maskMap.month = (++index)
          return `(${(opts.months || lang.props.date.months).join('|')})`
        case 'MMM':
          maskMap.month = (++index)
          return `(${(opts.monthsShort || lang.props.date.monthsShort).join('|')})`
        case 'DD':
        case 'D':
          maskMap.day = (++index)
          return '(\\d{0,2})'
        case 'Do':
          maskMap.day = (++index)
          return '(\\d{0,2}[^\\d]*?)'
        default:
          return (text === void 0 ? match : text.split('\\]').join(']'))
            .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
            .replace(/\s+/g, '\\$&\\s*')
      }
    })

  return {
    maskRegex: new RegExp(maskText),
    maskMap
  }
}

function standardDateFromMask (date, mask, opts = {}) {
  if (typeof mask !== 'string' || mask.length === 0 || mask === 'YYYY/MM/DD') {
    return date
  }

  const
    shortMonths = (opts.monthsShort || lang.props.date.monthsShort),
    months = (opts.months || lang.props.date.months),
    key = `${mask.toLowerCase()}|${shortMonths.join('|')}|${months.join('|')}`

  if (parseConvertFns[key] === void 0) {
    const
      { maskRegex, maskMap } = maskToRegex(mask, opts),
      parseFormatTo = (...args) => {
        let month = args[maskMap.month]

        if (/\d+/.test(month) === false) {
          let found = shortMonths.indexOf(month)
          if (found === -1) {
            found = months.indexOf(month)
          }
          if (found > -1) {
            month = found + 1
          }
        }

        return `${args[maskMap.year]}/${month}/${args[maskMap.day]}`
      }

    parseConvertFns[key] = text => {
      return text.replace(maskRegex, parseFormatTo)
    }
  }

  return parseConvertFns[key](date)
}

export function isValid (date) {
  if (typeof date === 'number') {
    return true
  }
  const t = Date.parse(date)
  return isNaN(t) === false
}

export function splitDate (date, mask, opts = {}) {
  let
    value = date,
    year, month, day

  [year, month, day] = standardDateFromMask(value, mask, opts).split('/')
    .concat([null, null, null])
    .slice(0, 3)
    .map(d => parseInt(d, 10))
    .map(d => isNaN(d) === true ? null : d)

  if (month > 12 || month === 0) {
    month = month % 12 || 12
  }
  else if (year !== null && month === null) {
    month = 1<commit_msg>feat(QDate): small tweaks to date.js<commit_after>/* eslint no-fallthrough: 0 */

import { isDate } from './is.js'
import { pad, capitalize } from './format.js'
import lang from '../lang.js'

const
  MILLISECONDS_IN_DAY = 86400000,
  MILLISECONDS_IN_HOUR = 3600000,
  MILLISECONDS_IN_MINUTE = 60000,
  token = /\[((?:[^\]\\]|\\]|\\)*)\]|d{1,4}|M{1,4}|m{1,2}|w{1,2}|Qo|Do|D{1,4}|YY(?:YY)?|H{1,2}|h{1,2}|s{1,2}|S{1,3}|Z{1,2}|a{1,2}|[AQExX]/g,
  parseConvertFns = {}

function formatTimezone (offset, delimeter = '') {
  const
    sign = offset > 0 ? '-' : '+',
    absOffset = Math.abs(offset),
    hours = Math.floor(absOffset / 60),
    minutes = absOffset % 60

  return sign + pad(hours) + delimeter + pad(minutes)
}

function setMonth (date, newMonth /* 1-based */) {
  const
    test = new Date(date.getFullYear(), newMonth, 0, 0, 0, 0, 0),
    days = test.getDate()

  date.setMonth(newMonth - 1, Math.min(days, date.getDate()))
}

function getChange (date, mod, add) {
  const
    t = new Date(date),
    sign = (add ? 1 : -1)

  Object.keys(mod).forEach(key => {
    if (key === 'month') {
      setMonth(t, t.getMonth() + 1 + sign * mod.month)
      return
    }

    const op = key === 'year'
      ? 'FullYear'
      : capitalize(key === 'days' ? 'date' : key)
    t[`set${op}`](t[`get${op}`]() + sign * mod[key])
  })
  return t
}

function maskToRegex (mask, opts = {}) {
  let index = 0

  const
    maskMap = {
      year: null,
      month: null,
      day: null
    },
    maskText = mask.replace(token, function (match, text) {
      switch (match) {
        case 'YYYY':
        case 'YY':
          maskMap.year = (++index)
          return '(-?\\d*)'
        case 'MM':
        case 'M':
          maskMap.month = (++index)
          return '(\\d{0,2})'
        case 'MMMM':
          maskMap.month = (++index)
          return `(${(opts.months || lang.props.date.months).join('|')})`
        case 'MMM':
          maskMap.month = (++index)
          return `(${(opts.monthsShort || lang.props.date.monthsShort).join('|')})`
        case 'DD':
        case 'D':
          maskMap.day = (++index)
          return '(\\d{0,2})'
        case 'Do':
          maskMap.day = (++index)
          return '(\\d{0,2}[^\\d]*?)'
        default:
          return (text === void 0 ? match : text.split('\\]').join(']'))
            .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
            .replace(/\s+/g, '\\$&\\s*')
      }
    })

  return {
    maskRegex: new RegExp(maskText),
    maskMap
  }
}

function standardDateFromMask (date, mask, opts) {
  if (typeof mask !== 'string' || mask.length === 0 || mask === 'YYYY/MM/DD') {
    return date
  }

  opts = opts || {}

  const
    shortMonths = (opts.monthsShort || lang.props.date.monthsShort),
    months = (opts.months || lang.props.date.months),
    key = `${mask.toLowerCase()}|${shortMonths.join('|')}|${months.join('|')}`

  if (parseConvertFns[key] === void 0) {
    const
      { maskRegex, maskMap } = maskToRegex(mask, opts),
      parseFormatTo = (...args) => {
        let month = args[maskMap.month]

        if (/\d+/.test(month) === false) {
          let found = shortMonths.indexOf(month)
          if (found === -1) {
            found = months.indexOf(month)
          }
          if (found > -1) {
            month = found + 1
          }
        }

        return `${args[maskMap.year]}/${month}/${args[maskMap.day]}`
      }

    parseConvertFns[key] = text => {
      return text.replace(maskRegex, parseFormatTo)
    }
  }

  return parseConvertFns[key](date)
}

export function isValid (date) {
  if (typeof date === 'number') {
    return true
  }
  const t = Date.parse(date)
  return isNaN(t) === false
}

export function splitDate (date, mask, opts = {}) {
  let
    value = date,
    year, month, day

  ;[year, month, day] = standardDateFromMask(value, mask, opts || {}).split('/')
    .concat([null, null, null])
    .slice(0, 3)
    .map(d => parseInt(d, 10))
    .map(d => isNaN(d) === true ? null : d)

  if (month > 12 || month === 0) {
    month = month % 12 || 12
  }
  else if (year !== null && month === null) {
    month = 1
{'fraction_non_alphanumeric': 0.20495001219214826, 'fraction_numerical': 0.020238966105827847, 'mean_word_length': 2.275958466453674, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '879505', 'n_tokens_mistral': 3112, 'n_tokens_neox': 2981, 'n_words': 636}