Source: LDay.js

/**
 * 统计字符串中每个字符出现次数
 * @param {string} text 需要统计的字符串
 * @return {Object} 返回一个对象
*/
function Char_static(text) {
  let SArray = new Array()
  for (let i = 0; i < text.length; i++) {
    let obj = {}
    obj[text[i]] = 1
    let k = 1
    while (i < text.length && text[i + 1] === text[i]) {
      k++
      i++
    }
    obj[text[i]] = k
    SArray.push(obj)
  }
  return SArray
}

/**
* 创建一个Day对象
* @class Day
* 传入参数可为:<br>
* 1. 日期字符串<br>
* 2. 日期数字 年,月,日,时,分,秒<br>
*/
class Day {
  constructor() {
    this.Factory([...arguments])
    this.update()
    this.isLdayObj = true
    this.rules = {
      y: 'y',
      Y: 'y',
      year: 'y',
      M: 'M',
      Month: 'M',
      d: 'd',
      day: 'd',
      h: 'h',
      H: 'h',
      hour: 'h',
      m: 'm',
      minute: 'm',
      s: 's',
      second: 's',
      ms: 'ms',
      millisecond: 'ms'
    }
  }

  Factory(date) {
    let length = date.length

    if (length === 1) {
      if (date[0].isLdayObj) {
        this.date = date[0].date
        return
      }
      if (Array.isArray(date[0])) {
        date = date[0]
        length = date.length
      }
    }

    if (length >= 3) {
      const Y = date[0].toString().padStart(4, '0')
      const M = date[1].toString().padStart(2, '0')
      const D = date[2].toString().padStart(2, '0')

      if (length === 3) {
        this.date = new Date(Y, M - 1, D)
      } else {
        let temp = new Date(Y, M - 1, D)
        date[3] && temp.setHours(date[3])
        date[4] && temp.setMinutes(date[4])
        date[5] && temp.setSeconds(date[5])

        this.date = temp
      }
    }
    else if (length === 1) {
      this.date = new Date(date[0])
    }
    else {
      this.date = new Date()
    }
  }

  // 内部更新数据
  update() {
    this.$Y = this.date.getFullYear()
    this.$M = this.date.getMonth() + 1
    this.$D = this.date.getDate()
    this.$W = this.date.getDay()
    this.$h = this.date.getHours()
    this.$m = this.date.getMinutes()
    this.$s = this.date.getSeconds()
    this.$t = this.date.getTime()
    this.$ms = this.date.getMilliseconds()
    this.$L = this.isLeap()
    this.Months = [31, this.$L ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
  }

  /**
  * 判断是否是闰年
  * @return {Boolean} 是否是闰年
  *
  */
  isLeap() {
    return (this.$Y % 4 === 0 && this.$Y !== 0) || this.$Y % 400 === 0
  }

  /**
  * 格式化
  * @param {String} pattern 输入格式化字符串
  * @return {String} 格式化后的字符串时间
  */
  format(pattern = "YYYY-MM-DD") {
    if (!pattern) {
      return `${this.$Y}-${this.$M}-${this.$D}`
    } else {
      let SArray = Char_static(pattern)
      let Tstring = ''
      for (let i of SArray) {
        let key = Object.keys(i)[0]
        let num = i[key]
        switch (key) {
          case 'Y': {
            Tstring += `${this.$Y.toString().padStart(num, "0")}`
            break
          }
          case "M": {
            Tstring += `${this.$M.toString().padStart(num, "0")}`
            break
          }
          case "D": {
            Tstring += `${this.$D.toString().padStart(num, "0")}`
            break
          }
          case "H": {
            Tstring += `${this.$h.toString().padStart(num, "0")}`
            break
          }
          case "m": {
            Tstring += `${this.$m.toString().padStart(num, "0")}`
            break
          }
          case "s": {
            Tstring += `${this.$s.toString().padStart(num, "0")}`
            break
          }
          default: {
            Tstring += `${key.repeat(num)}`
          }
        }
      }
      return Tstring
    }
  }

  /**
  * 是否相同时间
  * @param {Lday} 另一个Lday对象
  * @return {Boolean} 是否相同
  */
  isSame(otherDate) {
    return this.$t - otherDate.$t === 0
  }

  /**
  * 获取是一周第几天
  * @return {Number} 是一周中的第几天
  *
  */
  day() {
    return this.$W === 0 ? 7 : this.$W
  }

  /**
  * 一年内第几天
  * @return {Number} 是一年中的第几天
  *
  */
  dayOfYear() {
    let num = this.$D
    for (let i = 0; i < this.$M - 1; i++) {
      num += this.Months[i]
    }
    return num
  }

  /**
  * 一年内第几周
  * @return {Number} 一年内的第几周 (周一为一周开始)
  *
  */
  week() {
    const date1 = new Date(this.$Y, parseInt(this.$M) - 1, this.$D)
    const date2 = new Date(this.$Y, 0, 1)
    const d = Math.round((date1.valueOf() - date2.valueOf()) / 86400000)

    return Math.ceil((d + ((date2.getDay() + 1) - 1)) / 7)
  }

  /**
  * 获取unix时间戳
  * @return {Number} unix时间戳
  *
  */
  unix() {
    return this.date.valueOf() / 1000
  }

  /**
  * 加
  * @param {Number} num 数量
  * @param {String} key 关键字
  * @return {Lday} 新的Lday对象
  *
  */
  add(key, num) {
    let Y = this.$Y
    let M = this.$M
    let t = this.$t
    let nums
    const keys = ["Y", "y", "year", "M", "month"]
    let flag = keys.includes(key)
    function addT(numT) {
      let res = t + numT
      return res
    }
    switch (this.rules[key]) {
      case "y":
        Y = Y + num
        break
      case "M":
        M = M + num
        if (M > 12) {
          Y = Y + Math.floor(M / 12)
          M = (M % 12)
        } else if (M < 0) {
          let n = Math.floor(M / 12)
          Y = Y + n
          M = Math.abs(n) * 12 + M
          if (M === 0) {
            Y = Y - 1
            M = 12
          }
        } else if (M === 0) {
          Y = Y - 1
          M = 12
        }
        break
      case "d":
        nums = num * 1000 * 60 * 60 * 24
        break
      case "h":
        nums = num * 1000 * 60 * 60
        break
      case "m":
        nums = num * 1000 * 60
        break
      case "s":
        nums = num * 1000
        break
      case "ms":
        break
    }

    let para = flag
      ? [Y, M, this.$D, this.$h, this.$m, this.$s]
      : addT(nums)

    return new Day(para)
  }

  /**
  * 减
  * @param {Number} num 数量
  * @param {String} key 关键字
  * @return {Lday} 新的Lday对象
  *
  */
  subtract(key, num) {
    return this.add(key, num * -1)
  }

  /**
  * 获取本月天数
  * @return {Number} 本月的天数
  *
  */
  daysInMonth() {
    return this.Months[this.$M - 1]
  }

  /**
  * 获取数据
  * @param {String} id 关键字
  * @return {number} 新的Lday对象数据
  *
  */
  get(id) {
    let key = id || ""
    const date = {
      y: this.$Y,
      year: this.$Y,
      M: this.$M - 1,
      month: this.$M - 1,
      d: this.$D,
      date: this.$D,
      h: this.$h,
      hour: this.$h,
      m: this.$m,
      minute: this.$m,
      s: this.$s,
      second: this.$s,
      ms: this.$t,
      millisecond: this.$t,
      W: this.$W,
      day: this.$W
    }

    return date[key]
  }

  /**
  * 设置数据
  * @param {String} key 关键字
  * @param {Number| String} num 设置的值
  *
  */
  set(key, num) {
    switch (this.rules[key]) {
      case 'y': {
        this.$Y = num
        this.date.setFullYear(num)
        this.$L = this.isLeap()
        break
      }
      case 'M': {
        this.$M = num
        this.date.setMonth(num - 1)
        break
      }
      case 'd': {
        this.$D = num
        this.date.setDate(num)
        break
      }
      case 'h': {
        this.$h = num
        this.date.setHours(num)
        break
      }
      case 'm': {
        this.$m = num
        this.date.setMinutes(num)
        break
      }
      case 's': {
        this.$s = num
        this.date.setSeconds(num)
        break
      }
    }

    this.update()
  }

  /**
  * 格式转换
  * @return {Array} 年 月 日 时 分 秒 星期几(从0开始)组成的数组
  *
  */
  toArray() {
    // 年 月 日 时 分 秒 毫秒 星期几(从0开始)
    return [
      this.$Y,
      this.$M - 1,
      this.$D,
      this.$h,
      this.$m,
      this.$s,
      this.$ms,
      this.$W,
    ]
  }

  /**
  * 是否为前面的时间
  * @param {String|Date} date 比较时间点
  * @return {Boolean} 比较结果
  */
  isBefore(date) {
    return this.$t < (new Day(date)).$t
  }

  /**
  * 是否为后面的时间
  * @param {String|Date} date 比较时间点
  * @return {Boolean} 比较结果
  */
  isAfter(date) {
    return this.$t > (new Day(date)).$t
  }

  /**
  * 克隆新对象
  * @return {Day} 返回新的Day对象
  *
  */
  clone() {
    return new Day(this.$Y, this.$M, this.$D, this.$h, this.$m, this.$s)
  }

  /**
  * 获取当前时间时区
  * @return {number} 返回时区小时数
  **/
  getTimeZone() {
    return this.date.getTimezoneOffset() / 60
  }

  /**
  * 获取时间差
  * @param {string | date | Lday} 比较日期
  * @param {string} 转换成的数据 支持 天,分,秒,毫秒
  * @return {number} 返回时间差值
  **/
  diff(date, key) {
    const diffMs = this.$t - (new Day(date)).$t
    const transform = {
      d: 1000 * 60 * 60 * 24,
      h: 1000 * 60 * 60,
      m: 1000 * 60,
      s: 1000,
      ms: 1
    }
    const format = transform[this.rules[key]] || transform['s']

    return diffMs / format
  }
}


const oldPro = Day.prototype

function Lday() {
  return new Day(...arguments)
}

Lday.extend = (plugin, option) => {
  plugin(Lday, oldPro, option)
}

// 原型方法
/**
* @param {Number} num 时间戳
* @return {Day} LDay对象
*/
Lday.unix = (num) => {
  return new Day(num)
}

/**
* @param {Any} obj 判断参数
* @return {Boolean} 是否是LDay对象
*/
Lday.isLDay = (obj) => {
  return !!obj.isLdayObj
}


/* 构造函数 */
export default Lday