在Emacs日历中,可以使用运行命令M-=的calendar-count-days-region来计算两个日期之间的天数(包括开始日期和结束日期)。我如何计算除周末(星期六和星期日)以外的天数,如果从变量( holiday-general-holidays和holiday-local-holidays )中定义假期
发布于 2014-05-16 02:58:15
我认为这主要分为三部分:
Emacs已经有了M-= (calendar-count-days-region)的第一部分,所以让我们来看看这一职能。
有帮助,但不幸的是,它读取缓冲区并直接发送输出。让我们制作一个通用版本,它接受开始日期和结束日期参数,并返回天数,而不是打印它们:
(defun my-calendar-count-days(d1 d2)
(let* ((days (- (calendar-absolute-from-gregorian d1)
(calendar-absolute-from-gregorian d2)))
(days (1+ (if (> days 0) days (- days)))))
days))这只是calendar-count-days-region函数的一个副本,但没有缓冲区读写功能。一些测试:
(ert-deftest test-count-days ()
"Test my-calendar-count-days function"
(should (equal (my-calendar-count-days '(5 1 2014) '(5 31 2014)) 31))
(should (equal (my-calendar-count-days '(12 29 2013) '(1 4 2014)) 7))
(should (equal (my-calendar-count-days '(2 28 2012) '(3 1 2012)) 3))
(should (equal (my-calendar-count-days '(2 28 2014) '(3 1 2014)) 2)))现在,对于步骤2,我找不到任何内置函数来计算日期范围的周末天数(令人惊讶!)幸运的是,在使用绝对日期时,这/可能/非常简单。这里有一个非常天真的尝试,它简单地遍历范围内的所有绝对日期,并寻找星期六和星期日:
(defun my-calendar-count-weekend-days(date1 date2)
(let* ((tmp-date (if (< date1 date2) date1 date2))
(end-date (if (> date1 date2) date1 date2))
(weekend-days 0))
(while (<= tmp-date end-date)
(let ((day-of-week (calendar-day-of-week
(calendar-gregorian-from-absolute tmp-date))))
(if (or (= day-of-week 0)
(= day-of-week 6))
(incf weekend-days ))
(incf tmp-date)))
weekend-days))这个函数应该被优化,因为它做了很多不必要的循环(例如,我们知道周日之后的5天不会是周末,所以没有必要转换和测试它们),但是对于这个例子,我认为它非常清晰和简单。对现在来说已经够好了。一些测试:
(ert-deftest test-count-weekend-days ()
"Test my-calendar-count-weekend-days function"
(should (equal (my-calendar-count-weekend-days
(calendar-absolute-from-gregorian '(5 1 2014))
(calendar-absolute-from-gregorian '(5 31 2014))) 9))
(should (equal (my-calendar-count-weekend-days
(calendar-absolute-from-gregorian '(4 28 2014))
(calendar-absolute-from-gregorian '(5 2 2014))) 0))
(should (equal (my-calendar-count-weekend-days
(calendar-absolute-from-gregorian '(2 27 2004))
(calendar-absolute-from-gregorian '(2 29 2004))) 2)))最后,我们需要知道范围内的假期,emacs在holiday-in-range函数中提供了这一点!请注意,此函数调用calendar-holiday-list以确定包含哪些假日,因此如果您确实希望只搜索holiday-general-holidays和holiday-local-holidays,则需要适当地设置calendar-holidays变量。详细信息请参见C-h v calendar-holidays。
现在,我们可以在一个新的交互函数中完成所有这些,完成上述三个步骤。这实际上是另一个修改过的calendar-count-days-region版本,它在打印结果之前减去周末和假日(请参阅下面的编辑,然后运行):
(defun calendar-count-days-region2 ()
"Count the number of days (inclusive) between point and the mark
excluding weekends and holidays."
(interactive)
(let* ((d1 (calendar-cursor-to-date t))
(d2 (car calendar-mark-ring))
(date1 (calendar-absolute-from-gregorian d1))
(date2 (calendar-absolute-from-gregorian d2))
(start-date (if (< date1 date2) date1 date2))
(end-date (if (> date1 date2) date1 date2))
(days (- (my-calendar-count-days d1 d2)
(+ (my-calendar-count-weekend-days start-date end-date)
(my-calendar-count-holidays-on-weekdays-in-range
start-date end-date)))))
(message "Region has %d workday%s (inclusive)"
days (if (> days 1) "s" ""))))我相信对lisp/elisp有更多了解的人可以大大简化/改进这些示例,但我希望它至少可以作为一个起点。
实际上,现在我已经经历过了,我希望有人随时都会来,并指出有一个emacs包已经做到了.
编辑: DOH!,Bug #001:如果一个假日是在周末,那一天被移除两次.
曾经的解决方案是简单地包装holiday-in-range,这样我们就可以消除因周末而取消的假期:
(defun my-calendar-count-holidays-on-weekdays-in-range (start end)
(let ((holidays (holiday-in-range start end))
(counter 0))
(dolist (element holidays)
(let ((day (calendar-day-of-week (car element))))
(if (and (> day 0)
(< day 6))
(incf counter))))
counter))我已经更新了上面的calendar-count-days-region2以使用这个新函数。
https://stackoverflow.com/questions/23566000
复制相似问题