将字符串转换为 ISO 8601 持续时间格式
Converting a string to ISO 8601 duration format
我需要允许用户输入这样的字符串:
1m 3w 4d 22h 6m 3s
...然后将其转换为 ISO 8601 时长格式,如下所示:
P1M3W4DT22H6M3S
他们甚至应该被允许写更短的符号,比如:
10d 12h
我什至不知道从哪里开始,而且似乎找不到任何有助于这些转换的库。我可以使用 Moment 将 从 ISO 8601 持续时间格式转换为 到 .
建议?
我试着只删除空格,但当然我错过了时间之前的 T
:
`P${duration.toUpperCase().replace(/\s/g, '')}`
如果您只缺少 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+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"));
我需要允许用户输入这样的字符串:
1m 3w 4d 22h 6m 3s
...然后将其转换为 ISO 8601 时长格式,如下所示:
P1M3W4DT22H6M3S
他们甚至应该被允许写更短的符号,比如:
10d 12h
我什至不知道从哪里开始,而且似乎找不到任何有助于这些转换的库。我可以使用 Moment 将 从 ISO 8601 持续时间格式转换为 到 .
建议?
我试着只删除空格,但当然我错过了时间之前的 T
:
`P${duration.toUpperCase().replace(/\s/g, '')}`
如果您只缺少 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+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"));