阻止 moment.js 接受整数作为有效的 ISO8601 日期

Prevent moment.js accepting an integer as a valid ISO8601 date

我有一些 API 用于创建仪表板小部件。传递给 Google 图表的 API 的 return 基本 name/value 数据对。 Moment.js 检查该值是否为 ISO8601 日期,如果是,则作为日期实例传递给 Google 图表。

但是,ISO_8601 isValid 检查目前 return 如果日期是一个简单的整数,例如1234:

var myInt = 1234;
if (moment(myInt, moment.ISO_8601, true).isValid()) {
    console.log("Valid!");
}

我无法在 moment.js 代码中找到强制日期格式的必要功能,所以这个残酷的 hack 现在有效:

var myInt = 1234;
if (JSON.stringify(myInt).includes("T") && moment(myInt, moment.ISO_8601, true).isValid()) {
    console.log("Valid!");
}

是否有正确的方法来使用moment.js配置isValid()检查?

我的 API 中的日期格式是 yyyy-mm-ddThh:mm:ss(末尾没有 Z)。

你可以在把它传递给当下之前证明它的存在。?我从这个 post

中举了一个例子
/**
 * RegExp to test a string for a full ISO 8601 Date
 * Does not do any sort of date validation, only checks if the string is according to the ISO 8601 spec.
 *  YYYY-MM-DDThh:mm:ss
 *  YYYY-MM-DDThh:mm:ssTZD
 *  YYYY-MM-DDThh:mm:ss.sTZD
 * @see: https://www.w3.org/TR/NOTE-datetime
 * @type {RegExp}
 */
var ISO_8601_FULL = /^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(\.\d+)?(([+-]\d\d:\d\d)|Z)?$/i


// Usage:

ISO_8601_FULL.test( "2016-05-24T15:54:14.876Z" )  // true
ISO_8601_FULL.test( "2002-12-31T23:00:00+01:00" ) // true
ISO_8601_FULL.test( "2016-02-01" )                // false
ISO_8601_FULL.test( "2016" )                      // false

if (ISO_8601_FULL.test(myDate) && moment(myDate, moment.ISO_8601, true).isValid()) {
    console.log("Valid!");
}

我想日期应该不是整数。

根据THIS回答,当使用严格解析(最后一个参数设置为true)时,您还应该指定解析格式,以避免出现您描述的情况。正如许多用户注意到的那样,指定字符串格式而不是使用 moment.ISO_8601 可以按预期工作。

alert(isISODateValid(123)); //false
alert(isISODateValid("2011-10-10T14:48:00")); //true
alert(isISODateValid("2011-10-10T14:48:00Z")); //true

function isISODateValid(date) {

  return moment(date.toString().replaceAll("Z",""), "YYYY-MM-DDTHH:mm:ss", true).isValid();

}
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>

编辑:更新片段 - 如果日期包含“Z”后缀,在解析验证源日期格式之前将其删除

由于您声明:“我的 API 的日期格式是 yyyy-mm-ddThh:mm:ss(末尾没有 Z)”,解析它的最佳方法是使用正确的时刻格式 tokens instead of using moment.ISO_8601.

明确传递您期望时刻的格式

因此,在您的情况下,只需使用 moment(myInt, "YYYY-MM-DDTHH:mm:ss", true),如代码片段所示:

function checkValid(input) {
  if (moment(input, "YYYY-MM-DDTHH:mm:ss", true).isValid()) {
    console.log(input + " is valid!");
  }
}

checkValid(1234);
checkValid("2021-04-27T20:40:15");
checkValid("2021-04-27T20:40:15Z");
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>


请注意末尾的Z代表时区偏移UTC+0,如果你有它,moment会考虑到它,如果没有它,moment会将输入解析为本地时间(见Local vs UTC vs Offset指南)

作为旁注,moment.ISO_86012.25.0 之前的 moment 版本中的工作方式与您预期的一样:

function checkValid(input) {
  if (moment(input, moment.ISO_8601, true).isValid()) {
    console.log(input + " is valid!");
  }
}

checkValid(1234);
checkValid("2021-04-27T20:40:15");
checkValid("2021-04-27T20:40:15Z");
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>

有趣的是字符串和数字之间的区别。如果它是一个数字,它被解释为自 epoc 以来的毫秒数,这在计算机语言中非常有用,但显然并不总是所要求的,而且对每个开发人员来说也不明显。这可以通过类型检查轻松避免 (typeof input != 'string').

另一个变体更令人困惑:"12345" 无效。好的。但是 "1234" 被解释为年份,同时“34”似乎被解释为以分钟为单位的时间偏移量 (Sun Jan 01 1234 00:00:00 GMT+0034)。对我来说,这显然像是库中的一个错误,因为为了不同的目的多次解析相同的数字是毫无用处的。但在修复此问题后,"1234" 仍然是标准 ISO 8601

中定义的有效日期(仅限年份)

https://en.wikipedia.org/wiki/ISO_8601

For reduced precision,[17] any number of values may be dropped from any of the date and time representations, but in the order from the least to the most significant. For example, "2004-05" is a valid ISO 8601 date, which indicates May (the fifth month) 2004. This format will never represent the 5th day of an unspecified month in 2004, nor will it represent a time-span extending from 2004 into 2005.

顺便说一句:"543210" 也是有效的,表示 "5432-10",或 5432 年的十月

function checkValid(input) {
  m = moment(input, true);
  console.log(input + "  type: " + typeof input + "  valid: " + m.isValid() + "  result:" + m.toString());
}

checkValid(1234);
checkValid("1234");
checkValid(12345);
checkValid("12345");
checkValid("2021-04-27T20:40:15");
checkValid("2021-04-27T20:40:15Z");
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>