【问题标题】:Converting Youtube Data API V3 video duration format to seconds in JavaScript/Node.js在 JavaScript/Node.js 中将 Youtube Data API V3 视频持续时间格式转换为秒
【发布时间】:2014-04-04 15:02:01
【问题描述】:

我正在尝试将 ISO 8601 字符串转换为 JS/Node 中的秒数。我能想到的最好的方法是:

function convert_time(duration) {
    var a = duration.match(/\d+/g)
    var duration = 0

    if(a.length == 3) {
        duration = duration + parseInt(a[0]) * 3600;
        duration = duration + parseInt(a[1]) * 60;
        duration = duration + parseInt(a[2]);
    }

    if(a.length == 2) {
        duration = duration + parseInt(a[0]) * 60;
        duration = duration + parseInt(a[1]);
    }

    if(a.length == 1) {
        duration = duration + parseInt(a[0]);
    }
    return duration
}

当我输入诸如“PT48S”、“PT3M20S”或“PT3H2M31S”之类的字符串时,它可以工作,但如果字符串是“PT1H11S”,它就会失败。有人有更好的主意吗?

【问题讨论】:

  • 它对我有用 (jsfiddle.net/y6h4t)
  • 它可以工作,但同时它输出错误的时间。它应该是 3600 + 11,而不是 71。请注意缺少分钟标记。
  • 是的,对不起,我调查 :)

标签: javascript node.js time youtube timestamp


【解决方案1】:

如果您使用的是moment.js,您只需调用...

moment.duration('PT15M33S').asMilliseconds();

= 933000 毫秒

EDIT 2021:虽然这可行,并且仍然获得支持,但我不建议仅为此包含 moment.js。我建议使用像@redgetan's这样的正则表达式答案@

【讨论】:

  • 为了避免moment.js,你可以试试npmjs.com/package/duration-fns,它是这样工作的:import * as duration from 'duration-fns'; duration.toSeconds('PT10H4M10S'); // 36250
【解决方案2】:
function YTDurationToSeconds(duration) {
  var match = duration.match(/PT(\d+H)?(\d+M)?(\d+S)?/);

  match = match.slice(1).map(function(x) {
    if (x != null) {
        return x.replace(/\D/, '');
    }
  });

  var hours = (parseInt(match[0]) || 0);
  var minutes = (parseInt(match[1]) || 0);
  var seconds = (parseInt(match[2]) || 0);

  return hours * 3600 + minutes * 60 + seconds;
}

适用于这些情况:

PT1H
PT23M
PT45S
PT1H23M
PT1H45S
PT23M45S
PT1H23M45S

【讨论】:

  • 这将捕获包括 H、M 和 S 在内的字符串
  • 我将您的功能修复了数年、数周和数天。看我的评论。
  • 我添加了一个简单的修复来处理 api 中返回的直播项目
【解决方案3】:

我建议这个小技巧来防止你的问题案例:

function convert_time(duration) {
    var a = duration.match(/\d+/g);

    if (duration.indexOf('M') >= 0 && duration.indexOf('H') == -1 && duration.indexOf('S') == -1) {
        a = [0, a[0], 0];
    }

    if (duration.indexOf('H') >= 0 && duration.indexOf('M') == -1) {
        a = [a[0], 0, a[1]];
    }
    if (duration.indexOf('H') >= 0 && duration.indexOf('M') == -1 && duration.indexOf('S') == -1) {
        a = [a[0], 0, 0];
    }

    duration = 0;

    if (a.length == 3) {
        duration = duration + parseInt(a[0]) * 3600;
        duration = duration + parseInt(a[1]) * 60;
        duration = duration + parseInt(a[2]);
    }

    if (a.length == 2) {
        duration = duration + parseInt(a[0]) * 60;
        duration = duration + parseInt(a[1]);
    }

    if (a.length == 1) {
        duration = duration + parseInt(a[0]);
    }
    return duration
}

Fiddle

【讨论】:

  • 你有一个很棒的技巧。我建议添加code if (duration.indexOf('H') >= 0 && duration.indexOf('M') == -1 && duration.indexOf('S') == -1) { a = [a[0], 0, 0] } code 所以它也适用于“PT1H”
  • Np :) 更新后 - 如果您想验证此答案以获得支持;)
  • 我认为这包括所有可能性:jsfiddle.net/andrusasumets/W4Bru
  • @PaulRad 我如何在 Java 中做到这一点。我被正则表达式部分卡住了。
  • 此解决方案在“PT1H22M”等情况下失败。刚刚写了一个适用于所有情况的解决方案:gist.github.com/denniszhao/8972cd4ae637cf10fe01
【解决方案4】:

这是我的解决方案:

function parseDuration(duration) {
    var matches = duration.match(/[0-9]+[HMS]/g);

    var seconds = 0;

    matches.forEach(function (part) {
        var unit = part.charAt(part.length-1);
        var amount = parseInt(part.slice(0,-1));

        switch (unit) {
            case 'H':
                seconds += amount*60*60;
                break;
            case 'M':
                seconds += amount*60;
                break;
            case 'S':
                seconds += amount;
                break;
            default:
                // noop
        }
    });

    return seconds;
}

【讨论】:

【解决方案5】:

你可以找到一个非常简单的PHP解决方案here - How To Convert Youtube API Time (ISO 8601 String Video Duration) to Seconds In PHP - Code

此函数 convert_time() 将一个参数作为输入 - Youtube API 时间(视频持续时间),它采用 ISO 8601 字符串格式并以秒为单位返回其持续时间。

function convert_time($str) 
{
    $n = strlen($str);
    $ans = 0;
    $curr = 0;
    for($i=0; $i<$n; $i++)
    {
        if($str[$i] == 'P' || $str[$i] == 'T')
        {

        }
        else if($str[$i] == 'H')
        {
            $ans = $ans + 3600*$curr;
            $curr = 0;
        }
        else if($str[$i] == 'M')
        {
            $ans = $ans + 60*$curr;
            $curr = 0;
        }
        else if($str[$i] == 'S')
        {
            $ans = $ans + $curr;
            $curr = 0;
        }
        else
        {
            $curr = 10*$curr + $str[$i];
        }
    }
    return($ans);
}

测试一些输入:

"PT2M23S" => 143
"PT2M" => 120
"PT28S" => 28
"PT5H22M31S" => 19351
"PT3H" => 10800
"PT1H6M" => 3660
"PT1H6S" => 3606

【讨论】:

    【解决方案6】:

    我的解决方案:

    function convert_time(duration) {
      var total = 0;
      var hours = duration.match(/(\d+)H/);
      var minutes = duration.match(/(\d+)M/);
      var seconds = duration.match(/(\d+)S/);
      if (hours) total += parseInt(hours[1]) * 3600;
      if (minutes) total += parseInt(minutes[1]) * 60;
      if (seconds) total += parseInt(seconds[1]);
      return total;
    }
    

    Fiddle

    【讨论】:

      【解决方案7】:

      这是@redgetan 在 ES6 中的解决方案。

      我还修复了几年、几周和几天。

      https://www.digi.com/resources/documentation/digidocs/90001437-13/reference/r_iso_8601_duration_format.htm

      // Copied from:
      // https://stackoverflow.com/questions/22148885/converting-youtube-data-api-v3-video-duration-format-to-seconds-in-javascript-no
      function parseISO8601Duration(duration) {
          const match = duration.match(/P(\d+Y)?(\d+W)?(\d+D)?T(\d+H)?(\d+M)?(\d+S)?/)
          // An invalid case won't crash the app.
          if (!match) {
              console.error(`Invalid YouTube video duration: ${duration}`)
              return 0
          }
          const [
              years,
              weeks,
              days,
              hours,
              minutes,
              seconds
          ] = match.slice(1).map(_ => _ ? parseInt(_.replace(/\D/, '')) : 0)
        return (((years * 365 + weeks * 7 + days) * 24 + hours) * 60 + minutes) * 60 + seconds
      }
      
      if (parseISO8601Duration('PT1H') !== 3600) {
          throw new Error()
      }
      
      if (parseISO8601Duration('PT23M') !== 1380) {
          throw new Error()
      }
      
      if (parseISO8601Duration('PT45S') !== 45) {
          throw new Error()
      }
      
      if (parseISO8601Duration('PT1H23M') !== 4980) {
          throw new Error()
      }
      
      if (parseISO8601Duration('PT1H45S') !== 3645) {
          throw new Error()
      }
      
      if (parseISO8601Duration('PT1H23M45S') !== 5025) {
          throw new Error()
      }
      
      if (parseISO8601Duration('P43W5DT5M54S') !== 26438754) {
          throw new Error()
      }
      
      if (parseISO8601Duration('P1Y43W5DT5M54S') !== 57974754) {
          throw new Error()
      }
      

      【讨论】:

        【解决方案8】:

        我编写了一个 CoffeeScript 变体(如果需要,您可以在 coffeescript.org 上轻松编译它)

        差异:返回的持续时间采用人类可读的格式(例如 04:20、01:05:48)

        String.prototype.parseDuration = ->
            m = @.match /[0-9]+[HMS]/g
            res = ""
            fS = fM = !1
            for part in m
                unit = part.slice -1
                val = part.slice 0, part.length - 1
                switch unit
                    when "H" then res += val.zeros( 2 ) + ":"
                    when "M"
                        fM = 1
                        res += val.zeros( 2 ) + ":"
                    when "S"
                        fS = 1
                        res += if fM then val.zeros 2 else "00:" + val.zeros 2
        
             if !fS then res += "00"
             res
        

        我还实现了这个辅助函数来用前导零填充

        String.prototype.zeros = ( x ) ->
            len = @length
            if !x or len >= x then return @
            zeros = ""
            zeros += "0" for [0..(x-len-1)]
            zeros + @
        

        3nj0y!!!

        【讨论】:

          【解决方案9】:

          我意识到 eval 不受欢迎,但这是我能想象到的最简单和最快的方法。享受吧。

          function formatDuration(x) {
             return eval(x.replace('PT','').replace('H','*3600+').replace('M','*60+').replace('S', '+').slice(0, -1));
          }
          

          【讨论】:

          • 我有点怀疑正则表达式更快,但这里有一个较小的代码版本 return eval(x.replace(/[PTS]/, '').replace('H', '*3600+') .replace('M', '*60+').slice(0,-1));
          【解决方案10】:

          我认为使用 moment.js 将是一个更简单的解决方案。但是,如果有人正在寻找自定义解决方案,这里有一个简单的正则表达式供您使用:

          var regex = /PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?/;
          var regex_result = regex.exec("PT1H11S"); //Can be anything like PT2M23S / PT2M / PT28S / PT5H22M31S / PT3H/ PT1H6M /PT1H6S
          var hours = parseInt(regex_result[1] || 0);
          var minutes = parseInt(regex_result[2] || 0);
          var seconds = parseInt(regex_result[3] || 0);
          var total_seconds = hours * 60 * 60 + minutes * 60 + seconds;
          

          【讨论】:

            【解决方案11】:

            我遇到了上述解决方案的问题。我决定把它写得尽可能钝。我还使用自己的“getIntValue”代替 parseInt 以获得额外的理智。

            只是认为其他搜索可能会欣赏此更新。

            Fiddle

                function convertYouTubeTimeFormatToSeconds(timeFormat) {
            
                if ( timeFormat === null || timeFormat.indexOf("PT") !== 0 ) {
                    return 0;
                }
            
                // match the digits into an array
                // each set of digits into an item
                var digitArray      = timeFormat.match(/\d+/g);
                var totalSeconds    = 0;
            
                // only 1 value in array
                if (timeFormat.indexOf('H') > -1 && timeFormat.indexOf('M') == -1 && timeFormat.indexOf('S') == -1) {
                    totalSeconds    += getIntValue(digitArray[0]) * 60 * 60;
                }
            
                else if (timeFormat.indexOf('H') == -1 && timeFormat.indexOf('M') > -1 && timeFormat.indexOf('S') == -1) {
                    totalSeconds    += getIntValue(digitArray[0]) * 60;
                }
            
                else if (timeFormat.indexOf('H') == -1 && timeFormat.indexOf('M') == -1 && timeFormat.indexOf('S') > -1) {
                    totalSeconds    += getIntValue(digitArray[0]);
                }
            
            
                // 2 values in array
                else if (timeFormat.indexOf('H') > -1 && timeFormat.indexOf('M') > -1 && timeFormat.indexOf('S') == -1) {
                    totalSeconds    += getIntValue(digitArray[0]) * 60 * 60;
                    totalSeconds    += getIntValue(digitArray[1]) * 60;
                }
            
                else if (timeFormat.indexOf('H') > -1 && timeFormat.indexOf('M') == -1 && timeFormat.indexOf('S') > -1) {
                    totalSeconds    += getIntValue(digitArray[0]) * 60 * 60;
                    totalSeconds    += getIntValue(digitArray[1]);
                }
            
                else if (timeFormat.indexOf('H') == -1 && timeFormat.indexOf('M') > -1 && timeFormat.indexOf('S') > -1) {
                    totalSeconds    += getIntValue(digitArray[0]) * 60;
                    totalSeconds    += getIntValue(digitArray[1]);
                }
            
            
                // all 3 values
                else if (timeFormat.indexOf('H') > -1 && timeFormat.indexOf('M') > -1 && timeFormat.indexOf('S') > -1) {
                    totalSeconds    += getIntValue(digitArray[0]) * 60 * 60;
                    totalSeconds    += getIntValue(digitArray[1]) * 60;
                    totalSeconds    += getIntValue(digitArray[2]);
                }
            
            //  console.log(timeFormat, totalSeconds);
            
                return totalSeconds;
            }
            function getIntValue(value) {
                if (value === null) {
                    return 0;
                }
            
                else {
            
                    var intValue = 0;
                    try {
                        intValue        = parseInt(value);
                        if (isNaN(intValue)) {
                            intValue    = 0;
                        }
                    } catch (ex) { }
            
                    return Math.floor(intValue);
                }
            }
            

            【讨论】:

            • 指定“使用上述”解决方案......并让人们知道“问题”是什么,不要只是判断。我的咖啡变体就像一个魅力,可以很容易地编译成 js。我实现了它,因为我需要第一个解决方案中提供的另一种输出格式。谢谢 ;)
            【解决方案12】:

            Python

            它通过一次解析输入字符串 1 个字符来工作,如果字符是数字,它只是将它(字符串添加,而不是数学添加)添加到正在解析的当前值。 如果它是“wdhms”之一,则将当前值分配给适当的变量(周、日、小时、分钟、秒),然后将值重置以准备取下一个值。 最后,它将 5 个解析值的秒数相加。

            def ytDurationToSeconds(duration): #eg P1W2DT6H21M32S
                week = 0
                day  = 0
                hour = 0
                min  = 0
                sec  = 0
            
                duration = duration.lower()
            
                value = ''
                for c in duration:
                    if c.isdigit():
                        value += c
                        continue
            
                    elif c == 'p':
                        pass
                    elif c == 't':
                        pass
                    elif c == 'w':
                        week = int(value) * 604800
                    elif c == 'd':
                        day = int(value)  * 86400
                    elif c == 'h':
                        hour = int(value) * 3600
                    elif c == 'm':
                        min = int(value)  * 60
                    elif c == 's':
                        sec = int(value)
            
                    value = ''
            
                return week + day + hour + min + sec
            

            【讨论】:

            • 虽然这个答案在技术上回答了这个问题,但对我们未来的访问者来说,包括它实际在做什么会很有帮助
            【解决方案13】:

            这不是特定于 java 的,但我想添加 JAVA sn-p 因为这可能对其他用户有帮助

            String duration = "PT1H23M45S";
            Pattern pattern = Pattern.compile("PT(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+)S)?");
            Matcher matcher = pattern.matcher(duration);
            long sec = 0;
            long min = 0;
            long hour = 0;
            if (matcher.find())
            {
                if(matcher.group(1)!=null)
                    hour = NumberUtils.toInt(matcher.group(1));
                if(matcher.group(2)!=null)
                    min = NumberUtils.toInt(matcher.group(2));
                if(matcher.group(3)!=null)
                    sec = NumberUtils.toInt(matcher.group(3));
            
            }
            long totalSec = (hour*3600)+(min*60)+sec;
            System.out.println(totalSec);
            

            【讨论】:

              【解决方案14】:

              假设输入有效,我们可以使用正则表达式exec方法对字符串进行迭代并按顺序提取组:

              const YOUTUBE_TIME_RE = /(\d+)([HMS])/g;
              const YOUTUBE_TIME_UNITS = {
                  'H': 3600,
                  'M': 60,
                  'S': 1
              }
              
              /**
               * Returns the # of seconds in a youtube time string
               */
              function parseYoutubeDate(date: string): number {
                  let ret = 0;
                  let match: RegExpExecArray;
                  while (match = YOUTUBE_TIME_RE.exec(date)) {
                      ret += (YOUTUBE_TIME_UNITS[match[2]]) * Number(match[1]);
                  }
                  return ret;
              }
              

              【讨论】:

                【解决方案15】:

                ES6:

                const durationToSec = formatted =>
                  formatted
                    .match(/PT(?:(\d*)H)?(?:(\d*)M)?(?:(\d*)S)?/)
                    .slice(1)
                    .map(v => (!v ? 0 : v))
                    .reverse()
                    .reduce((acc, v, k) => (acc += v * 60 ** k), 0);
                

                【讨论】:

                • 虽然这段代码可能会解决问题,但一个好的答案还应该解释它的作用以及它的帮助。
                • 它返回秒数以及上面的解决方案。不同的是这个方案更简洁
                【解决方案16】:

                Kotlin 版本:

                private val youtubeDurationPattern: Pattern =
                    Pattern.compile("PT(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+)S)?")
                
                fun String.parseDuration(): Int {
                    val matcher: Matcher = youtubeDurationPattern.matcher(this)
                    if (!matcher.find()) {
                        throw IllegalStateException("Cannot parse $this.")
                    }
                    val hour = matcher.group(1)?.toInt() ?: 0
                    val min = matcher.group(2)?.toInt() ?: 0
                    val sec = matcher.group(3)?.toInt() ?: 0
                    return hour * 3600 + min * 60 + sec
                }
                
                

                和测试:

                    @Test
                    fun testParseDuration() {
                        assertEquals(10 * 60, "PT10M".parseDuration())
                        assertEquals(10 * 60 + 30, "PT10M30S".parseDuration())
                        assertEquals(30, "PT30S".parseDuration())
                        assertEquals(2 * 3600 + 3 * 60 + 16, "PT2H3M16S".parseDuration())
                    }
                

                【讨论】:

                  猜你喜欢
                  • 2021-08-05
                  • 1970-01-01
                  • 2013-11-02
                  • 2014-08-15
                  • 2016-11-05
                  • 2013-05-20
                  • 1970-01-01
                  • 2017-07-31
                  • 2013-05-24
                  相关资源
                  最近更新 更多