How to count days excluding weekends and holidays in the Emacs calendar

In the Emacs calendar, you can count the days between two dates (including the start and end dates) using M-=which executes the command calendar-count-days-region. How can I count days excluding weekends (Saturday and Sunday), and if certain holidays come from variables: holiday-general-holidaysand holiday-local-holidays?

+4
source share
1 answer

I think this essentially breaks down into three parts:

  • Count days in a region
  • subtract weekend days
  • subtract holidays

Emacs already has the first part covered in M-=( calendar-count-days-region), so let's look at this function .

, , , . , :

(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!, № 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 , .

+3

Source: https://habr.com/ru/post/1539924/


All Articles