【问题标题】:Converting a string to ISO 8601 duration format将字符串转换为 ISO 8601 持续时间格式
【发布时间】:2021-01-11 03:38:34
【问题描述】:

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

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

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

P1M3W4DT22H6M3S

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

10 天 12 小时

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

建议?

我试图只删除空格,但我当然错过了T 之前的时间:

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

【问题讨论】:

  • 看起来太复杂了,但你尝试过什么?
  • 不能给用户提供日期时间选择器吗?
  • 我的简要说明是允许这种类型的字符串。我正在努力坚持下去,但不确定是否可行。
  • 如果输入是“10m”怎么办?那是10分钟还是10个月?其次,输入中各部分的顺序是否保证是从大单元到小单元?
  • @trincot — 我不得不说分钟优先。

标签: javascript momentjs iso


【解决方案1】:

删除空格并将字母大写后,您可以使用正则表达式\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"));

【讨论】:

  • 这是黄金,但除非您考虑进行一些输入验证。例如,toDuration('') 给你PtoDuration('bullshit') 给你PBULLSHIT :-)
  • 当然,这假设输入的格式与原始帖子中的一样。
  • 我可能可以将其验证为带有时刻的日期。
  • 添加了一个版本,当输入不具有预期格式时将返回一个空字符串。
【解决方案2】:

如果您在D 标记后缺少T 分隔符,只需添加.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

示例已编辑以包含基本语法验证

【讨论】:

  • 但 D 不是必需的。你可以有一个像3w 12h 10m 这样的字符串。
  • 我明白了,那你需要一个一个解析值……应该不复杂,等一下
猜你喜欢
  • 2015-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-05
  • 1970-01-01
相关资源
最近更新 更多