使用JavaScript转换ISO 8601持续时间

guicsvcw  于 2022-12-10  发布在  Java
关注(0)|答案(9)|浏览(238)

如何使用JavaScript转换持续时间,例如:
PT16H30M型

63lcw9qa

63lcw9qa1#

理论上,您可以得到如下所示的ISO8601持续时间:
P1Y4M3W2DT10H31M3.452S
我编写了以下正则表达式来将其解析为组:

(-)?P(?:([.,\d]+)Y)?(?:([.,\d]+)M)?(?:([.,\d]+)W)?(?:([.,\d]+)D)?T(?:([.,\d]+)H)?(?:([.,\d]+)M)?(?:([.,\d]+)S)?

它并不漂亮,更精通正则表达式的人可能会写一个更好的。
这些组可归结为以下几类:
1.标志
1.年数
1.月数
1.周数
1.天数
1.小时数
1.会议记录
1.秒数
我编写了以下函数将其转换为一个nice对象:

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持续时间解析功能,你需要一个plugin to format,它似乎不能处理在写这篇笔记时指定的周数。

von4xj4u

von4xj4u2#

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

更多信息请访问https://momentjs.com/docs/#/durations/。

o4tp2gmn

o4tp2gmn3#

"PT16H30M".replace(/PT(\d+)H(\d+)M/, "$1:$2");
r8uurelv

r8uurelv4#

包了一个小包裹,以方便这一点:

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 tinydurationyarn add tinyduration安装
请参阅:https://www.npmjs.com/package/tinyduration

qyzbxkaa

qyzbxkaa5#

我刚刚这样做的持续时间,甚至超过一年之久。
这是一个fiddle

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);
}
vnjpjtjt

vnjpjtjt6#

特别是解决可在HTML5 <time/>标签中使用的DateTime字符串,因为它们被限制为Days、Minutes和Seconds(因为只有这些字符串可以转换为精确的秒数,因为Months和Years可以有不同的持续时间)

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
mo49yndu

mo49yndu7#

ISO 8601时期支持的基本解决方案。
由于JavaScript中缺少'duration'类型和奇怪的日期语义,它使用日期算法将'period'应用于'锚'日期(默认为当前日期和时间)。默认为添加period。
指定之前:false提供过去的日期。

// 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;
    }

没有保修。这可能有怪癖-测试您的用例。

a14dhokn

a14dhokn8#

这是James Caradoc-Davies提出的解决方案的更新

  • 它在句点开头添加了对+/-符号的支持
  • 正确处理浮点秒数
  • 使用多种输入格式输入要应用持续时间的日期
  • 强制T按规范要求存在
const durationExp = /^(?<sign>\+|-)?P((?<Y>\d+)Y)?((?<M>\d+)M)?((?<D>\d+)D)?T((?<H>\d+)H)?((?<m>\d+)M)?((?<S>\d+(\.\d+))?S)?$/
/**
 * # applyISODuration
 * Apply ISO 8601 duration string to given date
 * - **duration** must be a strng compliant with ISO 8601 duration
 *   /!\ it doesn't support the Week parameter but it can easily be replaced by adding a number of days
 * - **date** can be omit and will then default to Date.now()
 *   It can be any valid value for Date constructor so you can pass date
 *   as strings or number as well as Date object
 * returns a new Date with the duration applied
 */
const  applyISODuration = (duration/*:string*/, date/*?: Date|string|number*/) => {
  date = date ? new Date(date) : new Date()
  const parts = duration.match(durationExp)?.groups
  if (!parts) {
    throw new Error(`Invalid duration(${duration})`)
  }
  const addOrSubstract = parts.sign === '-' ? (value) => -value : (value) => +value;
  parts.Y && date.setFullYear(date.getFullYear() + addOrSubstract(parts.Y))
  parts.M && date.setMonth(date.getMonth() + addOrSubstract(parts.M))
  parts.D && date.setDate(date.getDate() + addOrSubstract(parts.D))
  parts.H && date.setHours(date.getHours() + addOrSubstract(parts.H))
  parts.m && date.setMinutes(date.getMinutes() + addOrSubstract(parts.m))
  parts.s && date.setSeconds(date.getSeconds() + addOrSubstract(parseFloat(parts.S)))
  return date
}

使用方法:

applyISODuration('P2DT12H5.555S')
applyISODuration('P2DT12H5.555S', new Date("2022-06-23T09:52:38.298Z") )
gdrx4gfi

gdrx4gfi9#

或者,您可以使用luxon程式库中的Duration.fromISO方法。
以下是如何使用它的示例:

const { Duration } = require('luxon');

// Parse an ISO 8601 duration string
const duration = Duration.fromISO('P3Y6M4DT12H30M5S');

// Print the total number of seconds in the duration
console.log(duration.as('seconds'));

这将记录持续时间的总秒数,在本例中为“117536305”。
请注意,luxon库是Moment project btw的一部分。

相关问题