Function: format-seconds

format-seconds is an autoloaded and byte-compiled function defined in time-date.el.gz.

Signature

(format-seconds STRING SECONDS)

Documentation

Use format control STRING to format the number SECONDS.

The valid format specifiers are:
%y is the number of (365-day) years.
%d is the number of days.
%h is the number of hours.
%m is the number of minutes.
%s is the number of seconds.
%z is a non-printing control flag (see below).
%% is a literal "%".

Upper-case specifiers are followed by the unit-name (e.g. "years"). Lower-case specifiers return only the unit.

"%" may be followed by a number specifying a width, with an
optional leading "." for zero-padding. For example, "%.3Y" will return something of the form "001 year".

The "%s" spec takes an additional optional parameter, introduced by the "," character, to say how many decimals to use. "%,1s" means "use one decimal".

The "%z" specifier does not print anything. When it is used, specifiers must be given in order of decreasing size. To the left of "%z", nothing is output until the first non-zero unit is encountered.

The "%x" specifier does not print anything. When it is used, specifiers must be given in order of decreasing size. To the right of "%x", trailing zero units are not output.

View in manual

Probably introduced at or before Emacs version 23.1.

Aliases

org-format-seconds (obsolete since 9.0)

Source Code

;; Defined in /usr/src/emacs/lisp/calendar/time-date.el.gz
;;;###autoload
(defun format-seconds (string seconds)
  "Use format control STRING to format the number SECONDS.
The valid format specifiers are:
%y is the number of (365-day) years.
%d is the number of days.
%h is the number of hours.
%m is the number of minutes.
%s is the number of seconds.
%z is a non-printing control flag (see below).
%% is a literal \"%\".

Upper-case specifiers are followed by the unit-name (e.g. \"years\").
Lower-case specifiers return only the unit.

\"%\" may be followed by a number specifying a width, with an
optional leading \".\" for zero-padding.  For example, \"%.3Y\" will
return something of the form \"001 year\".

The \"%s\" spec takes an additional optional parameter,
introduced by the \",\" character, to say how many decimals to
use.  \"%,1s\" means \"use one decimal\".

The \"%z\" specifier does not print anything.  When it is used, specifiers
must be given in order of decreasing size.  To the left of \"%z\", nothing
is output until the first non-zero unit is encountered.

The \"%x\" specifier does not print anything.  When it is used,
specifiers must be given in order of decreasing size.  To the
right of \"%x\", trailing zero units are not output."
  (let ((start 0)
        (units '(("y" "year"   31536000)
                 ("d" "day"       86400)
                 ("h" "hour"       3600)
                 ("m" "minute"       60)
                 ("s" "second"        1)
                 ("z")
                 ("x")))
        (case-fold-search t)
        spec match usedunits zeroflag larger prev name unit num
	leading-zeropos trailing-zeropos fraction minus
        chop-leading chop-trailing)
    (while (string-match "%\\.?[0-9]*\\(,[0-9]\\)?\\(.\\)" string start)
      (setq start (match-end 0)
            spec (match-string 2 string))
      (unless (string-equal spec "%")
        (or (setq match (assoc (downcase spec) units))
            (error "Bad format specifier: `%s'" spec))
        (if (assoc (downcase spec) usedunits)
            (error "Multiple instances of specifier: `%s'" spec))
        (if (or (string-equal (car match) "z")
                (string-equal (car match) "x"))
            (setq zeroflag t)
          (unless larger
            (setq unit (nth 2 match)
                  larger (and prev (> unit prev))
                  prev unit)))
        (push match usedunits)))
    (when (and zeroflag larger)
      (error "Units are not in decreasing order of size"))
    (unless (numberp seconds)
      (setq seconds (float-time seconds)))
    (setq minus (when (< seconds 0) "-") ; Treat -0.0 like 0.0.
	  seconds (abs seconds)
	  seconds (let ((s (floor seconds)))
		    (setq fraction (- seconds s))
		    s))
    (dolist (u units)
      (setq spec (car u)
            name (cadr u)
            unit (nth 2 u))
      (when (string-match
             (format "%%\\(\\.?[0-9]+\\)?\\(,[0-9]+\\)?\\(%s\\)" spec)
             string)
        (cond
         ((string-equal spec "z")
          (setq chop-leading
                (if leading-zeropos
                    (min leading-zeropos (match-beginning 0))
                  ;; The entire spec is zero, get past "%z" to last 0.
                  (+ 2 (match-beginning 0)))))
         ((string-equal spec "x")
          (setq chop-trailing t))
         (t
          ;; Cf article-make-date-line in gnus-art.
          (setq num (floor seconds unit)
                seconds (- seconds (* num unit)))
          (let ((is-zero (zerop (if (= unit 1)
                                    (+ num fraction)
                                  num))))
            ;; Start position of the first non-zero unit.
            (when (and (not leading-zeropos)
                       (not is-zero))
              (setq leading-zeropos (match-beginning 0)))
            (unless is-zero
              (setq trailing-zeropos nil))
            (when (and (not trailing-zeropos)
                       is-zero)
              (setq trailing-zeropos (match-beginning 0))))
          (setq string
                (replace-match
                 (format (if (match-string 2 string)
                             (concat
                              "%"
                              (and (match-string 1 string)
                                   (if (= (elt (match-string 1 string) 0) ?.)
                                       (concat "0" (substring
                                                    (match-string 1 string) 1))
                                     (match-string 1 string)))
                              (concat "." (substring
                                           (match-string 2 string) 1))
                              "f%s")
                           (concat "%" (match-string 1 string) "d%s"))
                         (if (= unit 1)
                             (+ num fraction)
                           num)
                         (if (string-equal (match-string 3 string) spec)
                             ""         ; lower-case, no unit-name
                           (format " %s%s" name
                                   (if (= num 1) "" "s"))))
                 t t string))))))
    (let ((pre string))
      (when (and chop-trailing trailing-zeropos)
        (setq string (substring string 0 trailing-zeropos)))
      (when chop-leading
        (setq string (substring string chop-leading)))
      ;; If we ended up removing everything, return the formatted
      ;; string in full.
      (when (equal string "")
        (setq string pre)))
    (setq string (replace-regexp-in-string "%[zx]" "" string))
    (concat minus (string-trim (string-replace "%%" "%" string)))))