如何使用 JavaScript 转换持续时间,例如:
PT16H30M
理论上,您可以获得如下所示的 ISO8601 持续时间:
P1Y4M3W2DT10H31M3.452S
我编写了以下正则表达式将其解析为组:
(-)?P(?:([.,\d]+)Y)?(?:([.,\d]+)M)?(?:([.,\d]+)W)?(?:([.,\d]+)D)?T(?:([.,\d]+)H)?(?:([.,\d]+)M)?(?:([.,\d]+)S)?
它不漂亮,更精通正则表达式的人可能能够写出更好的。
这些组归结为以下几点:
我编写了以下函数将其转换为一个不错的对象:
var iso8601DurationRegex = /(-)?P(?:([.,\d]+)Y)?(?:([.,\d]+)M)?(?:([.,\d]+)W)?(?:([.,\d]+)D)?T(?:([.,\d]+)H)?(?:([.,\d]+)M)?(?:([.,\d]+)S)?/;
window.parseISO8601Duration = function (iso8601Duration) {
var matches = iso8601Duration.match(iso8601DurationRegex);
return {
sign: matches[1] === undefined ? '+' : '-',
years: matches[2] === undefined ? 0 : matches[2],
months: matches[3] === undefined ? 0 : matches[3],
weeks: matches[4] === undefined ? 0 : matches[4],
days: matches[5] === undefined ? 0 : matches[5],
hours: matches[6] === undefined ? 0 : matches[6],
minutes: matches[7] === undefined ? 0 : matches[7],
seconds: matches[8] === undefined ? 0 : matches[8]
};
};
像这样使用:
window.parseISO8601Duration('P1Y4M3W2DT10H31M3.452S');
希望这可以帮助那里的人。
如果您使用momentjs,它们具有可用的 ISO8601 持续时间解析功能。您需要一个插件来格式化它,并且它似乎无法处理在撰写本说明时指定的周数的持续时间。
"PT16H30M".replace(/PT(\d+)H(\d+)M/, "$1:$2");
包裹了一个小包以促进这一点:
import { parse, serialize } from 'tinyduration';
// Basic parsing
const durationObj = parse('P1Y2M3DT4H5M6S');
assert(durationObj, {
years: 1,
months: 2,
days: 3,
hours: 4,
minutes: 5,
seconds: 6
});
// Serialization
assert(serialize(durationObj), 'P1Y2M3DT4H5M6S');
使用npm install --save tinyduration
或安装yarn add tinyduration
Moment.js 与 2.3 版本一起发布了持续时间支持。
const iso8601Duration = "PT16H30M"
moment.duration(iso8601Duration)
// -> { _data: { days: 0, hours: 16, milliseconds: 0, minutes: 30, months: 0, seconds: 0, years: 0} ...
moment.duration(iso8601Duration).asSeconds()
// -> 59400
我刚刚这样做了甚至超过一年的时间。
这是一个小提琴。
function convertDuration(t){
//dividing period from time
var x = t.split('T'),
duration = '',
time = {},
period = {},
//just shortcuts
s = 'string',
v = 'variables',
l = 'letters',
// store the information about ISO8601 duration format and the divided strings
d = {
period: {
string: x[0].substring(1,x[0].length),
len: 4,
// years, months, weeks, days
letters: ['Y', 'M', 'W', 'D'],
variables: {}
},
time: {
string: x[1],
len: 3,
// hours, minutes, seconds
letters: ['H', 'M', 'S'],
variables: {}
}
};
//in case the duration is a multiple of one day
if (!d.time.string) {
d.time.string = '';
}
for (var i in d) {
var len = d[i].len;
for (var j = 0; j < len; j++) {
d[i][s] = d[i][s].split(d[i][l][j]);
if (d[i][s].length>1) {
d[i][v][d[i][l][j]] = parseInt(d[i][s][0], 10);
d[i][s] = d[i][s][1];
} else {
d[i][v][d[i][l][j]] = 0;
d[i][s] = d[i][s][0];
}
}
}
period = d.period.variables;
time = d.time.variables;
time.H += 24 * period.D +
24 * 7 * period.W +
24 * 7 * 4 * period.M +
24 * 7 * 4 * 12 * period.Y;
if (time.H) {
duration = time.H + ':';
if (time.M < 10) {
time.M = '0' + time.M;
}
}
if (time.S < 10) {
time.S = '0' + time.S;
}
duration += time.M + ':' + time.S;
alert(duration);
}
专门解决可在 HTML5标签中使用的DateTime 字符串<time/>
,因为它们仅限于天、分和秒(因为只有这些可以转换为精确的秒数,因为月和年可以有不同的持续时间)
function parseDurationString( durationString ){
var stringPattern = /^PT(?:(\d+)D)?(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d{1,3})?)S)?$/;
var stringParts = stringPattern.exec( durationString );
return (
(
(
( stringParts[1] === undefined ? 0 : stringParts[1]*1 ) /* Days */
* 24 + ( stringParts[2] === undefined ? 0 : stringParts[2]*1 ) /* Hours */
)
* 60 + ( stringParts[3] === undefined ? 0 : stringParts[3]*1 ) /* Minutes */
)
* 60 + ( stringParts[4] === undefined ? 0 : stringParts[4]*1 ) /* Seconds */
);
}
测试数据
"PT1D" returns 86400
"PT3H" returns 10800
"PT15M" returns 900
"PT1D12H30M" returns 131400
"PT1D3M15.23S" returns 86595.23
ISO8601期支持的基本解决方案。
由于 JavaScript 中缺少“持续时间”类型和奇怪的日期语义,它使用日期算法将“周期”应用于“锚”日期(默认为当前日期和时间)。默认是添加期间。
指定 ago: true 以提供过去的日期。
// Adds ISO8601 period: P<dateparts>(T<timeparts>)?
// E.g. period 1 year 3 months 2 days: P1Y3M2D
// E.g. period 1H: PT1H
// E.g. period 2 days 12 hours: P2DT12H
// @param period string: ISO8601 period string
// @param ago bool [optiona] true: Subtract the period, false: add (Default)
// @param anchor Date [optional] Anchor date for period, default is current date
function addIso8601Period(period /*:string */, ago /*: bool? */, anchor /*: Date? */) {
var re = /^P((?<y>\d+)Y)?((?<m>\d+)M)?((?<d>\d+)D)?(T((?<th>\d+)H)?((?<tm>\d+)M)?((?<ts>\d+(.\d+)?)S)?)?$/;
var match = re.exec(period);
var direction = ago || false ? -1 : 1;
anchor = new Date(anchor || new Date());
anchor.setFullYear(anchor.getFullYear() + (match.groups['y'] || 0) * direction);
anchor.setMonth(anchor.getMonth() + (match.groups['m'] || 0) * direction);
anchor.setDate(anchor.getDate() + (match.groups['d'] || 0) * direction);
anchor.setHours(anchor.getHours() + (match.groups['th'] || 0) * direction);
anchor.setMinutes(anchor.getMinutes() + (match.groups['tm'] || 0) * direction);
anchor.setSeconds(anchor.getSeconds() + (match.groups['ts'] || 0) * direction);
return anchor;
}
没有保修。这可能有怪癖 - 测试您的用例。