[2024-12-17 周二 14:21]
有的时候,我们只想在工作日处理一些工作相关的每日重复事项。
设置节假日
排除掉周六、周日以及法定节假日(也许还有自定义的假日)之后,就是工作日。在 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表达式的日历条目,后续会用到。
设置函数
(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来判断是否为工作日。
以下函数参考了这篇文章
(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,仅会匹配工作日。
使用效果
根据sexp表达式的条目中🏮和💼的emoji来判断。
→ 是周末
—> 有💼
–—> 工作日
—> 无💼
–—> 节假日
→ 不是周末
—> 有🏮
–—> 节假日
—> 无🏮
–—> 工作日