将字符串转换为ISO 8601持续时间格式

问题描述

我需要允许用户键入这样的字符串:

1m 3w 4d 22h 6m 3s

…,然后将其转换为ISO 8601持续时间格式,如下所示:

P1M3W4DT22H6M3S

甚至应该允许他们写出更短的符号,例如:

10d 12h

我什至不知道从哪里开始,而且似乎找不到任何有助于这些转换的库。我可以使用Moment将 ISO 8601持续时间格式转换,但不能将转换为

建议?

我试图删除空格,但是当然我想念之前的T

`P${duration.toupperCase().replace(/\s/g,'')}`

解决方法

删除空格并大写字母后,可以使用正则表达式\d+H?\d*M?\d*S?$捕获输入的时间部分(如果有的话)。这确定了“ T”定界符的插入点:

const toDuration = s => "P" + s.toUpperCase(s).replace(/\s/g,"")
                               .replace(/\d+H?\d*M?\d*S?$/,"T$&");

console.log(toDuration("1m 3w 4d 22h 6m 3s"));
console.log(toDuration("10d 12h"));
console.log(toDuration("10m"));
console.log(toDuration("1y 8w 3d"));
console.log(toDuration("2m 12h"));

做出一些假设:

  • 如果输入对“ m”(月或分钟)的含义有歧义,则将其解释为分钟。
  • 不需要更改输入中各部分的顺序即可得出有效的期间符号。
  • 输入遵循示例中给出的格式。此处没有输入验证。

如果要包括验证,则可以这样扩展:

const toDuration = s => /^(\d+y\s*)?(\d+m\s*)?(\d+w\s*)?(\d+d\s*)?(\d+h\s*)?(\d+m\s*)?(\d+s\s*)?$/i.test(s)
                ? "P" + s.toUpperCase(s).replace(/\s/g,"")
                         .replace(/\d+H?\d*M?\d*S?$/,"T$&")
                : ""; // indicates bad format

console.log(toDuration("1m 3w 4d 22h 6m 3s"));
console.log(toDuration("10d 12h"));
console.log(toDuration("10m"));
console.log(toDuration("1y 8w 3d"));
console.log(toDuration("2m 12h"));
console.log(toDuration("bad12 format"));

,

如果您只想念T后的D分隔符,只需添加.replace('D','DT')}

let duration = '1m 3w 4d 22h 6m 3s'
`P${duration.toUpperCase().replace(/\s/g,'').replace('D','DT')}`
// gives 'P1M3W4D22H6M3S'

但这仅适用于静态输入格式。


要同时解析完整和不完整的输入,您需要按类型对其进行解析。

const parseDuration = duration => {
  const keys = ['Y','M','W','D','H','MIN','S']
  if (!duration || typeof duration !== 'string')
    throw "Empty input"
  let parsed = {}
  // split entries by space and process them
  duration.split(/\s+/).map(r => {
    // get length (number) and type for each entry
    let [_,len,type] = r.match(/^(\d+)(\w+)$/)
    type = type.toUpperCase()
    // assume M means minutes if either of W,D or H is set
    if (type === 'M' && (parsed['W'] || parsed['D'] || parsed['H']))
      type = 'MIN'
    // asign length by type
    if (len && keys.includes(type))
      parsed[type] = len
  })
  
  if (!Object.keys(parsed).length)
    throw new Error("Empty input")
  
  // set all undefined types to zero
  for (const k of keys)
    parsed[k] = parseInt(parsed[k]) || 0
  
  // format to ISO 8601 duration
  return `P${parsed.Y}Y${parsed.M}M${parsed.W}W${parsed.D}DT${parsed.H}H${parsed.MIN}M${parsed.S}S`
}

try {
  console.log(parseDuration('1m 3w 4d 22h 6m 3s'))
} catch {
  console.warn('failed to parse')
}
// P0Y1M3W4DT22H6M3S

try {
  console.log(parseDuration('3w 12h 10m'))
} catch {
  console.warn('failed to parse')
}
// P0Y0M3W0DT12H10M0S

try {
  console.log(parseDuration('1bullshit'))
} catch {
  console.warn('failed to parse')
}
// fails

try {
  console.log(parseDuration(''))
} catch {
  console.warn('failed to parse')
}
// fails

示例经过修改以包含基本语法验证