首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在Emacs日历中计算不包括周末和假日的天数

如何在Emacs日历中计算不包括周末和假日的天数
EN

Stack Overflow用户
提问于 2014-05-09 13:34:15
回答 1查看 1.1K关注 0票数 4

在Emacs日历中,可以使用运行命令M-=calendar-count-days-region来计算两个日期之间的天数(包括开始日期和结束日期)。我如何计算除周末(星期六和星期日)以外的天数,如果从变量( holiday-general-holidaysholiday-local-holidays )中定义假期

EN

Stack Overflow用户

回答已采纳

发布于 2014-05-16 02:58:15

我认为这主要分为三部分:

  • 数数一个地区的日数
  • 减去周末
  • 减去假期

Emacs已经有了M-= (calendar-count-days-region)的第一部分,所以让我们来看看这一职能

有帮助,但不幸的是,它读取缓冲区并直接发送输出。让我们制作一个通用版本,它接受开始日期和结束日期参数,并返回天数,而不是打印它们:

代码语言:javascript
复制
(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函数的一个副本,但没有缓冲区读写功能。一些测试:

代码语言:javascript
复制
(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,我找不到任何内置函数来计算日期范围的周末天数(令人惊讶!)幸运的是,在使用绝对日期时,这/可能/非常简单。这里有一个非常天真的尝试,它简单地遍历范围内的所有绝对日期,并寻找星期六和星期日:

代码语言:javascript
复制
(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天不会是周末,所以没有必要转换和测试它们),但是对于这个例子,我认为它非常清晰和简单。对现在来说已经够好了。一些测试:

代码语言:javascript
复制
(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-holidaysholiday-local-holidays,则需要适当地设置calendar-holidays变量。详细信息请参见C-h v calendar-holidays

现在,我们可以在一个新的交互函数中完成所有这些,完成上述三个步骤。这实际上是另一个修改过的calendar-count-days-region版本,它在打印结果之前减去周末和假日(请参阅下面的编辑,然后运行):

代码语言:javascript
复制
(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,这样我们就可以消除因周末而取消的假期:

代码语言:javascript
复制
 (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以使用这个新函数。

票数 4
EN
查看全部 1 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23566000

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档