+emacs+为每日重复事项添加工作日判定
有的时候,我们只想在工作日处理一些工作相关的每日重复事项。
1. 设置节假日
排除掉周六、周日以及法定节假日(也许还有自定义的假日)之后,就是工作日。在 org-agenda-files
中任一文件加入以下内容:
;; 2025中国法定节假日 %%(diary-date 1 1 2025) 🏮元旦🏮 %%(diary-block 1 28 2025 2 4 2025) 🏮春节🏮 %%(diary-date 1 26 2025) 💼春节-上班💼 %%(diary-date 2 8 2025) 💼春节-上班💼 %%(diary-block 4 4 2025 4 6 2025) 🏮清明🏮 %%(diary-block 5 1 2025 5 5 2025) 🏮劳动节🏮 %%(diary-date 4 27 2025) 💼劳动节-上班💼 %%(diary-block 5 31 2025 6 2 2025) 🏮端午🏮 %%(diary-block 10 1 2025 10 8 2025) 🏮国庆、中秋🏮 %%(diary-date 9 28 2025) 💼国庆中秋-上班💼 %%(diary-date 10 11 2025) 💼国庆中秋-上班💼
即可在org-agenda中生成sexp表达式的日历条目,后续会用到。
2. 设置函数
(defun my/date-is-workday (date &optional offset) "工作日/调休日返回t,其余返回nil;offset指查看偏移天数的情况" (let* ((offset (or offset 0)) (timestamp (time-to-seconds (date-to-time date))) (offset-timestamp (time-add timestamp (seconds-to-time (* 24 60 60 offset)))) (date-string (format-time-string "%Y-%m-%d" offset-timestamp)) (start-day (time-to-days (org-read-date nil t date-string))) (parsed-time (parse-time-string date-string)) (year (nth 5 parsed-time)) (month (nth 4 parsed-time)) (day (nth 3 parsed-time)) (workdays nil) (holidays nil) (files (org-agenda-files nil 'ifmode)) (result-string " ") file rtn rtnall ) (setq date (calendar-gregorian-from-absolute start-day)) (while (setq file (pop files)) (catch 'nextfile (setq rtn (apply #'org-agenda-get-day-entries file date '(:sexp))) (when rtn (setq rtnall (append rtnall rtn))) )) (dolist (result rtnall) (setq result-string (concat result-string (substring-no-properties result))) ) (when (string-match "🏮" result-string) (setq holidays '123)) (when (string-match "💼" result-string) (setq workdays '123)) ;; Remove the custom command after use (if (or (= (calendar-day-of-week (list month day year)) 0) ; Sunday (= (calendar-day-of-week (list month day year)) 6)) ; Saturday (if workdays t nil) (if holidays nil t) ) ))
这个函数会根据输入日期+可选的偏移日期,通过匹配emoji来判断是否为工作日。
以下函数参考了这篇文章1
(defun my/org-hook-for-repeat-on-workday() "offset意味当天为假且加上负offset日期之后的那天为真时,则返回真" (when (and (org-entry-get nil "WORKDAY") (string-match "d" (org-get-repeat))) ;; Get time from item at POINT (let* ((offset (string-to-number (org-entry-get nil "WORKDAY"))) ;; Convert to timestamp - required for the next step (seconds-timestamp (org-time-string-to-seconds (org-entry-get (point) "SCHEDULED"))) ;; Convert to decoded time - required to find out the weekday (decoded-time)) (while (if (not (my/date-is-workday (format-time-string "%Y-%m-%d" seconds-timestamp))) (if (my/date-is-workday (format-time-string "%Y-%m-%d" seconds-timestamp) (* -1 offset)) nil t) nil) (setq seconds-timestamp (time-add seconds-timestamp (seconds-to-time (* 24 60 60)))) ) (let ((result-string (format-time-string "%Y-%m-%d %H:%M" seconds-timestamp))) (org-schedule nil result-string)) ) ) ) (add-hook 'org-todo-repeat-hook 'my/org-hook-for-repeat-on-workday)
这个函数会提取属性中的 WORKDAY
,如果有值且按日重复,则会启动。
偏移值的目的主要是用来处理「节假日第一天」和「节假日最后一天」的需求。如果有任务需要在「节假日第一天」和工作日重复,则将 WORKDAY
的值设定为1;如果有任务需要在「节假日最后一天」和工作日重复,则将 WORKDAY
的值设定为-1;如果都不需要,则将 WORKDAY
的值设定为0,仅会匹配工作日。
3. 使用效果
根据sexp表达式的条目中🏮和💼的emoji来判断。
-> 是周末
—> 有💼
–—> 工作日
—> 无💼
–—> 节假日
-> 不是周末
—> 有🏮
–—> 节假日
—> 无🏮
–—> 工作日