如何将持续时间分成两半

I'm trying to find the midpoint of a video, in other words divide the total length by 2. ffmpeg gives time in format HOURS:MM:SS.MICROSECONDS, so ignoring microseconds, how could I get the half way point? I've tried dividing each number (hours/minutes/seconds) by 2 but this fails in the case of 0:01:00, for example.

This can be much simpler (and maybe even faster) if we use the parsing and formatting logic provided by the time package.

func getDividedTime(times string, n int) (string, error) {
    t, err := time.Parse("15:04:05.000000", times)
    if err != nil {
        return "", err
    }
    d := t.Sub(time.Time{}) / time.Duration(n)
    return time.Time{}.Add(d).Format("15:04:05"), nil
}

Try it on the Go Playground. The weakness of this solution is that it "rejects" inputs having hours > 23.

Essentially, you want to:

  1. Convert everything to seconds
  2. Divide by 2
  3. Convert back to hours:minutes:seconds

(and do some formatting for ffmpeg). Also note that this answer can be generalized to dividing by n, instead of just halving.

import (
    "fmt"
    "strconv"
    "strings"
)

// ffmpeg uses the format HOURS:MM:SS.MICROSECONDS
func getDividedTime(time string, n int) string {
    times := strings.Split(time, ":")

    // get rid of microseconds
    times[2] = strings.Split(times[2], ".")[0]

    // conversions
    minutesToSeconds := 60
    hoursToSeconds := 60 * minutesToSeconds

    // convert everything to seconds
    seconds, _ := strconv.Atoi(times[2])
    minutes, _ := strconv.Atoi(times[1])
    hours, _ := strconv.Atoi(times[0])

    secMinutes := minutes * minutesToSeconds
    secHours := hours * hoursToSeconds

    totalSeconds := seconds + secHours + secMinutes
    totalSeconds /= n

    // update time and grab seconds
    newSeconds := totalSeconds % 60
    totalSeconds /= 60

    // update time and grab minutes
    newMinutes := totalSeconds % 60
    totalSeconds /= 60

    newHours := totalSeconds % 3600

    times[0] = strconv.Itoa(newHours)
    times[1] = strconv.Itoa(newMinutes)
    times[2] = strconv.Itoa(newSeconds)

    // zero padding for seconds and minutes (not hours)
    if newMinutes < 10 {
        times[1] = "0" + times[1]
    }
    if newSeconds < 10 {
        times[2] = "0" + times[2]
    }

    dividedTime := strings.Join(times[:], ":")
    return dividedTime 
}

func main() {
    // create some tests
    tables := []struct {
        inputTime   string
        correctTime string
    }{
        {"0:11:28.956000", "0:05:44"},
        {"1:00:00.111999", "0:30:00"},
        {"1:15:00.111999", "0:37:30"},
        {"1:45:00.111999", "0:52:30"},
        {"0:59:00.000000", "0:29:30"},
        {"2:04:22.123151", "1:02:11"},
        {"0:00:00.000000", "0:00:00"},
        {"0:00:03.000000", "0:00:01"},
        {"0:00:04.000000", "0:00:02"},
        {"0:20:00.000000", "0:10:00"},
        {"0:02:00.000000", "0:01:00"},
        {"99:00:00.000000", "49:30:00"},
    }

    // ensure output matches truth values
    for _, table := range tables {
        output := getDividedTime(table.inputTime, 2) // cut time in half
        if output != table.correctTime {
            fmt.Printf("failure: expected: %s, got: %s
", table.correctTime, output)
        }
    }
}

An even simpler solution that uses Duration and not Time. It allows parsing of hours > 24. This is usually desired when dealing with timers or in your case, video duration!

func getDividedTime(st string, div int) string {
    var h, m, s int
    fmt.Sscanf(st, "%d:%d:%d", &h, &m, &s)
    seconds := (h*60*60 + m*60 + s) / div
    d := time.Duration(int64(seconds) * int64(1e9))
    hh, mm, ss := int(d.Hours()), int(d.Minutes()), int(d.Seconds())
    return fmt.Sprintf("%d:%02d:%02d", hh, mm-(hh*60), ss-(mm*60))
}

Try it in Go Playground! Feel free to check for invalid input errors returned by fmt.Sscanf()!

Avoid parsing the stderr output of ffmpeg to get duration (that's what I'm assuming you're doing): it's not meant for machine parsing and is prone to breakage. That's what ffprobe is for and you won't have to perform additional processing to isolate the duration.

You can use ffprobe to get duration in seconds which is much easier to divide:

ffprobe -v error -show_entries format=duration -of csv=p=0 input.mp4

Example result:

48.012000

Then use your favorite function or tool to divide by 2. (Example using bc in a Bash script.)

If you require HOURS:MM:SS.MICROSECONDS then add the -sexagesimal option.