Problem

I have some org-mode tables with a column containing the duration (or length) of musical tracks:

|---+---------------------------------+-----------------------------------+----------|
| # | Artist                          | Title                             | Duration |
|---+---------------------------------+-----------------------------------+----------|
| 1 | Lorem ipsum dolor sit amet      | consectetuer adipiscing elit      |     7:32 |
| 2 | Donec hendrerit tempor tellus   | Donec pretium posuere tellus      |  1:09:27 |
| 3 | Proin quam nisl, tincidunt et   | mattis eget, convallis nec, purus |    10:15 |
| 4 | Cum sociis natoque penatibus et | magnis dis parturient montes      |     4:25 |
|---+---------------------------------+-----------------------------------+----------|

I'd like to convert the durations to start times. The result of the converstion should look like this:

|---+---------------------------------+-----------------------------------+------------|
| # | Artist                          | Title                             | Start time |
|---+---------------------------------+-----------------------------------+------------|
| 1 | Lorem ipsum dolor sit amet      | consectetuer adipiscing elit      |       0:00 |
| 2 | Donec hendrerit tempor tellus   | Donec pretium posuere tellus      |       7:32 |
| 3 | Proin quam nisl, tincidunt et   | mattis eget, convallis nec, purus |    1:16:59 |
| 4 | Cum sociis natoque penatibus et | magnis dis parturient montes      |    1:27:14 |
|---+---------------------------------+-----------------------------------+------------|

Note

In the duration input

  • Durations will be given in terms of hours, minutes, and seconds, separated by colons
  • leading zeroes may or may not be present (ie. a duration may look like "09:25" or "9:25")
  • the hours field may or may not be present (ie. a duration might look like "9:25" or "1:09:25" or even "0:09:25")

In the start time output

  • minutes and seconds should always be present, even if they're zero (ex: "0:00" and "0:32" are both correct)
  • if the left-most field is not a minute field and is 0, it and the colon following it should be omitted (ie. output "12:24", not "0:12:24")
  • a field should be padded with leading zeroes if there is another field to its left (ie. these are all correct: "1:09:26", "1:00:26", "1:00:00", "9:26", and these are wrong: "0:09:26", "00:09:26")
up vote 1 down vote accepted

The following provides my-durations-to-start-times to convert durations to start-times, e.g.,

(my-durations-to-start-times '("7:32" "1:09:27" "10:15" "4:25"))
;; => ("0:00" "7:32" "1:16:59" "1:27:14")
(defun my-duration-to-seconds (duration)
  "Return number of seconds of DURATION string."
  (let ((seconds 0)
        (unit 1))
    (dolist (n (nreverse (mapcar #'string-to-number (split-string duration ":"))))
      (setq seconds (+ seconds (* n unit)))
      (setq unit (* unit 60)))
    seconds))

(cl-assert (= (my-duration-to-seconds "04") 4))
(cl-assert (= (my-duration-to-seconds "7:32") (+ (* 7 60) 32)))
(cl-assert (= (my-duration-to-seconds "1:09:27") 4167))

(defun my-format-seconds (seconds)
  (let* ((h (floor seconds 3600))
         (seconds (- seconds (* 3600 h)))
         (m (floor seconds 60))
         (seconds (- seconds (* 60 m)))
         (s seconds))
    (if (zerop h)
        (format "%d:%02d" m s)
      (format "%d:%02d:%02d" h m s))))

(cl-assert (string= (my-format-seconds (my-duration-to-seconds "0")) "0:00"))
(cl-assert (string= (my-format-seconds (my-duration-to-seconds "00:09:26")) "9:26"))

(defun my-durations-to-start-times (durations)
  (let ((start 0))
    (mapcar (lambda (duration)
              (prog1 (my-format-seconds start)
                (setq start (+ start (my-duration-to-seconds duration)))))
            durations)))

(cl-assert (equal (my-durations-to-start-times '("7:32" "1:09:27" "10:15" "4:25"))
                  '("0:00" "7:32" "1:16:59" "1:27:14")))

Use my-durations-to-start-times in org-mode table formula

|---+----------+------------|
|   | Duration | Start time |
|---+----------+------------|
| 1 |     7:32 |       0:00 |
| 2 |  1:09:27 |       7:32 |
| 3 |    10:15 |    1:16:59 |
| 4 |     4:25 |    1:27:14 |
|---+----------+------------|
#+TBLFM: $>='(nth (1- (string-to-number $1)) (my-durations-to-start-times '(@2$>>..@5$>>)))
  • Fantastic. Thank you! Not to look a gift horse in the mouth, but is it possible to just replace the durations in the Duration column with the respective start times rather than putting the start times in a separate column? – izkon Dec 4 at 7:41
  • @izkon I don't know. I don't have much experience with org-mode table formula. The above formula needs to call my-durations-to-start-times 4 times, instead of just 1 time, it assumes the Duration column doesn't change, thus simply replacing $>= with $>>= doesn't work. – xuchunyang Dec 4 at 8:09

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.