【问题标题】:Node.js - What is the fastest way to check if a string represents a valid datetime for a big number of elements?Node.js - 检查字符串是否代表大量元素的有效日期时间的最快方法是什么?
【发布时间】:2019-01-15 14:40:30
【问题描述】:

检查有效日期时间的最快方法是什么? 我需要考虑的不仅是字符串包含年、月、日、小时和分钟,而且日期时间是有效的,例如:2017-02-29 10:00 应该被认为是无效的,因为它在非闰年是第 29 位。

我有一个字符串元素数组(300k 元素),格式为:YYYYMMDDHHmm,我需要以最快的方式检查每一行。

使用 moment.js 检查每个元素的有效性需要在常规 for 循环中使用 5s

for (let i = 0; i < length; i++) {
    let el = datetimes[i];
    let d = moment.utc(el, "YYYYMMDDHHmm");
    d.isValid();
}

有更快的替代品吗?

【问题讨论】:

    标签: javascript node.js performance


    【解决方案1】:

    在调用任何类型的字符串匹配器或正则表达式之前,尽可能多地排除。

    moment 每次都会评估该表达式。 (除非已经开始进行创意缓存)

    您可以先检查虚假值(undefined、null、0、'' 等)。

    if (!el) {
        // not valid
    }
    

    你可以在检查 null 和 undefined 后检查长度(感谢 Luca)

    if (el.length !== 12) {
       // not valid
    }
    

    您也可以预编译一个正则表达式并使用它。

    // define this outside of your loop
    let rx2 = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})$/
    
    function leapYear(year) {
      return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
    }
    
    function checkValidDate(strDate) {
      if (!strDate || strDate.length !== 12) throw new Error('invalid date')
      let m = rx2.exec(strDate)
      if (!m) throw new Error('invalid date')
    
      let year = parseInt(m[1])
      if (year < 2000 || year >= 2100) throw new Error('bad year')
    
      let month = parseInt(m[2])
      if (month > 11) throw new Error('bad month')
    
      let day = parseInt(m[3])
      // base 0 days and months
      switch (month) {
        case 0, 2, 4, 5, 6, 7, 9, 11:
          if (day > 30) throw new Error('bad day')
          break;
        case 3, 5, 8, 10:
          if (day > 29) throw new Error('bad day')
          break;
        case 1:
          if (day > 28) throw new Error('bad day')
          if (day === 28 && !isLeapYear(year)) throw new Error('bad day')
          break;
      }
    
      let hour = parseInt(m[4])
      if (hour > 23) throw new Error('bad hour')
    
      let minute = parseInt([5])
      if (hour > 59) throw new Error('bad minute')
    }
    
    try {
      checkValidDate('asdf')
      console.log('valid')
    } catch (e) {
      console.error(e)
    }
    
    try {
      checkValidDate('200011241230')
      console.log('valid')
    } catch (e) {
      console.error(e)
    }
    
    try {
      checkValidDate('200001300000')
      console.log('valid')
    } catch (e) {
      console.error(e)
    }

    【讨论】:

    • 或者,检查长度是否小于12
    • 感谢您的回答,但您的解决方案只是检查字符串是否为可能的有效日期。我想我没有解释清楚。我还需要检查它应该代表的 DateTime 是否有效。例如:您的解决方案将标记为有效的 201899999999。我需要发现这些情况(例如,非闰年的 2 月 29 日)。同样的事情也适用于 DateTime 的时间部分
    • @Firephp 再次解析它会减慢速度。正则表达式完成后,所有元素都已转换为整数。
    • @Firephp 我更新了从零开始的月份和日期,以及闰年检查。使用从零开始的月份和日期很奇怪......
    • 下一步将是一次读取 4 或 2 个字符并解析它们,并在继续之前检查整数,以免接收整个输入...。@MarcoGalassi 你是满意了吗?如果速度不够快,我们可以创建一个包含该范围内每个日期/时间的巨型哈希表!好在你没有使用秒数:)
    【解决方案2】:

    你可以写一个正则表达式来检查日期字符串

    这是用于检查 YYYYMMDDHHmm 的示例 rexexp

    ([12][0-9]{3})(1[12]|0[1-9])(0[1-9]|[12][0-9]|3[01])([01][0-9]|[2][0-4])([0-5][0-9])
    

    请检查此垃圾箱https://jsbin.com/zawojov/edit?js,console

    【讨论】:

    • 感谢您的回答,但您的解决方案没有考虑闰年甚至 DST
    • 是的,它没有。但这可以让您了解如何使用正则表达式进行检查
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多