EMACS-DOCUMENT

=============>集思广益

Org-mode中的日期计算方式

我总是使用 org-read-date 来输入日期或时间(不一定非的是Org相关的),因为我觉得它相当的灵活. 你可以用 +3 来表示三天后, fri 表示下周五, +2tue 表示下下个星期二, +1w 表示一周后, +1m 表示一个月后. 在Emacs Lisp中调用 org-read-date 也很简单. 只需要 (org-read-date) 就行了,它会返回一个类似 2015-08-06 这样格式的时间.

我也用 org-read-date 来进行日期相关的计算. 比如,我想知道明天是什么时候,我可以给 org-read-date 的第三个参数赋值(字符串):

(org-read-date nil nil "+1")

还可以计算指定日期的相对日期. 你可以为 org-read-date 指定基础日期也可以用另一个 org-read-date 来获取这个基础日期. 在下例中,我想知道2015-08-31之后的第一个星期一是什么时候(请注意,这里用了两个 + 号,而不是一个 + 号).

(org-read-date nil nil "++mon" nil (org-time-string-to-time "2015-08-31"))

org-time-string-to-time 将一个表示日期或时间的字符串转换成时间的内部表示方式. 之后你可以通过 decode-time 抽取出其中的各部分的内容(比如月份之类), 你也可以通过 time-to-seconds 将其转换成至纪元开始所经过的秒数. 当然,你也可以直接用 org-time-to-seconds 来将Org时间字符串直接转换成秒数.

如果你想计算天数, 可以用 org-time-string-to-absolute 来转换日期字符串. 例如, 你可以用下面的方式计算两个日期之间的天数(包含减的日期,而不包含被减的日期):

(let ((start-date (org-read-date))
      (end-date (org-read-date)))
  (- (org-time-string-to-absolute end-date)
     (org-time-string-to-absolute start-date)))

要获得日期中的年月日信息,可以联合使用 org-time-string-to-timedecode-time, 你也可以联合使用 org-time-string-to-secondscalendar-gregorian-from-absolute.

要将内部的时间表示结构体转换成Org格式的日期, 我比较推崇 (format-time-string "%Y-%m-%d" ...). 而 encode-time 则能用来将其他东西转换成内部表示的时间格式.

如果你要处理至纪元以来的绝对天数,你可以将之转换成Gregorian历,然后再格式化成字符串.

那么, 要遍历两个日期之间的所有天,你可以用类似下面的代码来实现:

(let* ((start-date (org-read-date))
       (end-date (org-read-date))
       (current (org-time-string-to-absolute start-date))
       (end (org-time-string-to-absolute end-date))
       gregorian-date
       formatted-date)
  (while (< current end)
    (setq gregorian-date (calendar-gregorian-from-absolute current))
    (setq formatted-date
          (format "%04d-%02d-%02d"
                  (elt gregorian-date 2) ; month
                  (elt gregorian-date 0) ; day
                  (elt gregorian-date 1))) ; year
    ;; Do something here; ex:
    (message "%s" formatted-date)
    ;; Move to the next date
    (setq current (1+ current))))

此外, 你也可以用 org-read-date 来对日期进行运算,可以将上面代码简化成这样:

(let* ((start-date (org-read-date))
       (end-date (org-read-date))
       (current start-date))
  (while (string< current end-date)
    ;; Do something here; ex:
    (message "%s" current)
    ;; Move to the next date
    (setq current (org-read-date nil nil "++1" nil (org-time-string-to-time current)))))

当然可能还有更优雅的方式来重写此代码,如果你找到了,请与我们分享.

总之, 希望文本能对你们有帮助!