Преобразование длительности ISO 8601 с помощью JavaScript

Как я могу преобразовать продолжительность с помощью JavaScript, например:

PT16H30M


person cvelinho    schedule 18.02.2013    source источник
comment
Что вы пробовали? Во что вы хотите его преобразовать? Это может быть обман stackoverflow .com/questions/4829569/   -  person Dutts    schedule 18.02.2013
comment
Как из этого формата получить время 16:30;   -  person cvelinho    schedule 18.02.2013
comment
Для этого есть пакет: npmjs.com/package/iso8601-duration   -  person str    schedule 27.08.2018


Ответы (6)


Теоретически вы можете получить продолжительность ISO8601, которая выглядит следующим образом:

P1Y4M3W2DT10H31M3.452S

Я написал следующее регулярное выражение, чтобы разбить это на группы:

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

Это не очень красиво, и кто-то, кто лучше разбирается в регулярных выражениях, может написать лучше.

Группы сводятся к следующему:

  1. Подписать
  2. Годы
  3. Месяцы
  4. Недели
  5. Дней
  6. Часы
  7. Минуты
  8. Секунды

Я написал следующую функцию, чтобы преобразовать его в красивый объект:

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. Вам понадобится плагин для его форматирования, и, похоже, он не обрабатывает продолжительность недели, указанные в периоде на момент написания этой заметки.

person crush    schedule 19.03.2015
comment
P1D является допустимой продолжительностью, поэтому не следует ожидать T внутри регулярного выражения. Вот более правильное регулярное выражение: (-)?P(?:([.,\d]+)Y)?(?:([.,\d]+)M)?(?:([.,\d]+)W)?(?:([.,\d]+)D)?(?:T(?:([.,\d]+)H)?(?:([.,\d]+)M)?(?:([.,\d]+)S)?)? - person Ser; 25.05.2018
comment
Я специально залогинился, чтобы проголосовать. Отлично и экономит время, спасибо! - person Artyom Pranovich; 02.05.2019
comment
Ваш код можно значительно упростить, используя оператор объединения Nullish '??'. - person Илья Зеленько; 15.08.2020

Я только что сделал это для длительности даже более года.
Вот скрипка.

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);
}
person Miko Lukasik    schedule 18.06.2014
comment
это круто, только баг в том, что когда минуты не указаны, выводит три нуля вместо двух. - person wiherek; 03.07.2014
comment
Спасибо, я этого не заметил. Мне удалось исправить это по ошибке при переписывании сценария. Обновление сейчас. - person Miko Lukasik; 11.07.2014

В частности, решение строки DateTime которые можно использовать в тегах HTML5 <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
person Luke Stevenson    schedule 08.01.2019

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/ .

person Sir hennihau    schedule 28.06.2020

Завернул небольшой пакет, чтобы облегчить это:

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

См.: https://www.npmjs.com/package/tinyduration.

person Melle    schedule 15.10.2020
comment
Спасибо большое, отлично работает :) - person Kfir Eichenblat; 05.01.2021

Базовое решение для поддержки периода ISO8601.

Из-за отсутствия типа «длительность» в JavaScript и странной семантики дат, здесь используется арифметика дат для применения «периода» к дате «привязки» (по умолчанию используются текущие дата и время). По умолчанию добавляется период.

Укажите назад: 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;
    }

Нет гарантии. Это может иметь особенности - проверьте свой вариант использования.

person James Caradoc-Davies    schedule 18.08.2019