用于时间跟踪的正则表达式验证

Regex validation for time tracking

我正在尝试按照 Javascript 中 Jira 中的方式验证字符串。我正在尝试复制它在 Jira 中的验证方式。我猜我可以用 Regex 做到这一点,但我不确定怎么做。

用户可以输入格式为“1d 6h 30m”的字符串,表示 1 天 6 小时 30 分钟。我的用例不需要几周的时间。如果用户使用无效字符(除 'd'、'h'、'm' 或 ' ' 之外的任何字符,我想显示错误。此外,字符串必须用空格分隔持续时间,理想情况下,我想强制用户按降序输入持续时间,这意味着“6h 1d”将无效,因为天数应该排在第一位。此外,用户不必输入所有信息,因此“30m”有效。

这是我的代码,用于获取似乎有效的日期、小时和分钟。我只需要验证部分的帮助。

let time = '12h 21d 30m'; //example
let times = time.split(' ');
let days = 0;
let hours = 0;
let min = 0;
for(let i = 0; i < times.length; i++) {
    if (times[i].includes('d')){
        days = times[i].split('d')[0];
    }
    if (times[i].includes('h')){
        hours = times[i].split('h')[0];
    }
    if (times[i].includes('m')){
        min = times[i].split('m')[0];
    }
}
console.log(days);
console.log(hours);
console.log(min);

根据您的意见,我在 运行 匹配正则表达式之前添加了一个验证正则表达式 运行。

为了验证,您需要

/^(\d+[d]\s+)?(\d+[h]\s+)?(\d+[m]\s+)?(\d+[s]\s+|$)?/

要提取值,您需要

/([\d]+[dhms]\s+|$)/g

然后您可以将 String.match 与此正则表达式一起使用,遍历所有匹配项并根据末尾的时间字母添加添加时间

const INPUT = "12h 21d  30s";

checkTimespanFormat(INPUT);

if (checkTimespanKeysOrder(INPUT, true))
  console.log(`${INPUT} keys order is valid`);
else console.log(`${INPUT} keys order is NOT valid`);
//******************************************************************

/**
 * Ensures that time keys are:
 * - Preceeded by one or two digits
 * - Separated by one or many spaces
 */
function checkTimespanFormat(timespanStr, maltiSpacesSeparation = false) {
  // timespan items must be separated by 1 space
  if (maltiSpacesSeparation) timespanStr = timespanStr.toLowerCase().split(" ");
  // timespan items must be separated by one or many space
  else timespanStr = timespanStr.toLowerCase().split(/ +/);

  // timespan items must be formatted correctly
  timespanStr.forEach((item) => {
    if (!/^\d{1,2}[dhms]$/.test(item)) console.log("invalid", item);
    else console.log("valid", item);
  });
}

/**
 * Validates:
 * - Keys order
 * - Duplicate keys
 */
function checkTimespanKeysOrder(timespanStr) {
  const ORDER = ["d", "h", "m", "s"];

  let timeKeysOrder = timespanStr
    .replace(/[^dhms]/g, "") // Removing non time keys characters
    .split("") // toArray
    .map((char) => {
      return ORDER.indexOf(char); // Getting the order of time keys
    });

  for (i = 0; i < timeKeysOrder.length - 1; i++)
    if (timeKeysOrder.at(i) >= timeKeysOrder.at(i + 1)) return false;
  return true;
}

这是我对这个问题的看法:

  1. 比赛分钟数 ([1-5]?[\d])m,符合条件的值在 [0,59]
  2. 范围内
  3. 比赛时间 ([1]?[\d]|2[0-3])h ,符合条件的值在 [0,23]
  4. 范围内
  5. 比赛日 ([1-9]|[1-9][\d])d,符合条件的值在 [1,99]
  6. 范围内

然后我们可以封装以小时为单位的天的正则表达式和以分钟为单位的小时的正则表达式,以确保我们有像 {dd}d {hh}h {mm}m, {hh}h {mm}m, {mm}m:[=27= 这样的格式]

"(((([1-9]|[1-9][\d])d )?([1]?[\d]|2[0-3])h )*([1-5]?[\d])m)"

极端情况包括带零的输入,例如 00m00d 00m01h 00d 00m。为了拒绝前两个而接受最后一个,我们可以在没有找到前面的模式时取反值00m00d

要使用的最终正则表达式:

"(?!0m)(((?!0h)(([1-9]|[1-9][\d])d )?([1]?[\d]|2[0-3])h )*([1-5]?[\d])m)"

检查:

  • days 在组 4
  • hours 在组 5
  • minutes 第 6 组

https://regex101.com 测试。

它能解决您的问题吗?