在有些情况下,我们需要的不是准确的时间,而是更加人性化的时间。即当前的时间(或指定一个时间)距离过去的特定时间经过了多久,就像论坛每个帖子的“活跃” 一栏。
用法
使用 user-friendly-time
函数。第一个参数是过去的某个时间。第二个参数为 t 是得到更精确的时间,即当前时间单位向下再精确一个单位。第三个参数默认是当前的时间,用于得到过去时间到现在时间的差,也可以指定一个特定的时间。
例子
(user-friendly-time (date-to-time "2021-05-02 15:28:33"))
(user-friendly-time
(date-to-time "2021-05-02 15:28:33") t) ;; 最后一个参数默认是当前时间。
(user-friendly-time
(date-to-time "2021-05-02 15:28:33") t
(date-to-time "2021-05-02 15:30:00")) ;; => 1 minute 27 seconds
(user-friendly-time
(date-to-time "2021-05-01 15:28:33") nil
(date-to-time "2021-05-01 15:30:00")) ;; => 1 minute
代码
有类似需求的,欢迎取用。
点击展开代码
(defvar uft-unit-secs
`(("second" . 1)
("minute" . 60)
("hour" . ,(* 60 60))
("day" . ,(* 60 60 24))
("month" . ,(* 60 60 24 30.5))
("year" . ,(* 60 60 24 365.5))))
(defun uft--grammar-correct (string)
"When the number in STRING is '1', make time unit
a singular form. Otherwise, keep the original format."
(when string
(let* ((lst (split-string string " "))
(unit-num (car lst))
(unit-name (cadr lst)))
(unless (string= unit-num "1")
(setq unit-name (concat unit-name "s")))
(concat unit-num " " unit-name))))
(defun uft--accurater (unit-name interval accurater)
"Accurate to the lower level time unit."
(pcase unit-name
("second" (if accurater
(uft--grammar-correct
(concat (number-to-string interval) " " unit-name))
"just now"))
(_
(let* ((units uft-unit-secs)
(unit-secs (floor (cdr (assoc unit-name units))))
(unit-num (/ interval unit-secs))
(unit-str (uft--grammar-correct
(format "%s %s" unit-num unit-name)))
(lower-unit
(nth (1- (seq-position units (assoc unit-name units))) units))
(lower-unit-name (car lower-unit))
(lower-unit-secs (cdr lower-unit))
(lower-unit-num (floor (/ (% interval unit-secs) lower-unit-secs)))
(lower-unit-str (uft--grammar-correct
(unless (= lower-unit-num 0)
(format "%s %s" lower-unit-num
lower-unit-name)))))
(if (and accurater lower-unit-str)
(concat unit-str " " lower-unit-str)
unit-str)))))
(defun user-friendly-time (specified-time &optional accurater latest-time)
"Return a user-friendly string of SPECIFIED-TIME.
SPECIFIED-TIME is a time value to convert to float.
See ‘format-time-string’ for the various forms of a time value.
If the optional argument ACCURATER is non-nil, return the accurater
format of string. The optional argument LATEST-TIME is current-time
by default."
(let* ((latest-time (or latest-time (current-time)))
(passed-sec (floor (time-to-seconds specified-time)))
(curr-sec (floor (time-to-seconds latest-time)))
interval)
(if (> passed-sec curr-sec)
(error "the specified-time shouldn't be later than current time!")
(setq interval (- curr-sec passed-sec))
(pcase interval
((pred (> 60))
(uft--accurater "second" interval accurater))
((and (pred (<= 60))
(pred (> (* 60 60))))
(uft--accurater "minute" interval accurater))
((and (pred (<= (* 60 60)))
(pred (> (* 60 60 24))))
(uft--accurater "hour" interval accurater))
((and (pred (<= (* 60 60 24)))
(pred (> (* 60 60 24 30.5))))
(uft--accurater "day" interval accurater))
((and (pred (<= (* 60 60 24 30.5)))
(pred (> (* 60 60 24 365.5))))
(uft--accurater "month" interval accurater))
((pred (<= (* 60 60 24 365.5)))
(uft--accurater "year" interval accurater))))))