aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias EngdegÄrd2020-09-30 23:57:27 +0200
committerMattias EngdegÄrd2020-10-02 10:35:52 +0200
commit4cb16b6f42ea7ea088fa4134f8fe4ccfec16a56d (patch)
tree618a3b8e1b61b6092fdb81dc7ead79c0794a453e
parentd037a6a2e6f92d793b1d5403dea4c7d3ca70883c (diff)
downloademacs-4cb16b6f42ea7ea088fa4134f8fe4ccfec16a56d.tar.gz
emacs-4cb16b6f42ea7ea088fa4134f8fe4ccfec16a56d.zip
Calc: fix business days calculation (bug43677)
The calculation of business days was broken in 2012 (probably 310e60d9454fe2 or thereabouts) when the date representation changed epoch so that Jan 1, 1 AD became day number 1 instead of 0. Repair this, along with an unrelated bug that prevented arbitrary holiday weekdays from working. Reported by Aaron Zeng. * lisp/calc/calc-forms.el (math-to-business-day) (math-from-business-day): Correct calculation of weekdays using Calc's current (Rata Die) chronology. Modify loop condition to cope with odd sets of holiday weekdays. * test/lisp/calc/calc-tests.el (calc-business-days): New test.
-rw-r--r--lisp/calc/calc-forms.el13
-rw-r--r--test/lisp/calc/calc-tests.el76
2 files changed, 83 insertions, 6 deletions
diff --git a/lisp/calc/calc-forms.el b/lisp/calc/calc-forms.el
index 5a8f0a38d24..6d70126c098 100644
--- a/lisp/calc/calc-forms.el
+++ b/lisp/calc/calc-forms.el
@@ -1870,8 +1870,8 @@ and ends on the last Sunday of October at 2 a.m."
1870 (and days (= day (car days)) 1870 (and days (= day (car days))
1871 (setq holiday t))) 1871 (setq holiday t)))
1872 (let* ((weekdays (nth 3 math-holidays-cache)) 1872 (let* ((weekdays (nth 3 math-holidays-cache))
1873 (weeks (1- (/ (+ day 6) 7))) 1873 (weeks (/ day 7))
1874 (wkday (- day 1 (* weeks 7)))) 1874 (wkday (mod day 7))) ; Day of week: 0=Sunday, 6=Saturday
1875 (setq delta (+ delta (* weeks (length weekdays)))) 1875 (setq delta (+ delta (* weeks (length weekdays))))
1876 (while (and weekdays (< (car weekdays) wkday)) 1876 (while (and weekdays (< (car weekdays) wkday))
1877 (setq weekdays (cdr weekdays) 1877 (setq weekdays (cdr weekdays)
@@ -1905,14 +1905,15 @@ and ends on the last Sunday of October at 2 a.m."
1905 (setq delta (1+ delta))) 1905 (setq delta (1+ delta)))
1906 (setq day (+ day delta))) 1906 (setq day (+ day delta)))
1907 (let* ((weekdays (nth 3 math-holidays-cache)) 1907 (let* ((weekdays (nth 3 math-holidays-cache))
1908 (bweek (- 7 (length weekdays))) 1908 (bweek (- 7 (length weekdays))) ; Business days in a week, 1..7.
1909 (weeks (1- (/ (+ day (1- bweek)) bweek))) 1909 (weeks (/ day bweek)) ; Whole weeks.
1910 (wkday (- day 1 (* weeks bweek))) 1910 (wkday (mod day bweek)) ; Business day in last week, 0..bweek-1
1911 (w 0)) 1911 (w 0))
1912 (setq day (+ day (* weeks (length weekdays)))) 1912 (setq day (+ day (* weeks (length weekdays))))
1913 ;; Add business days in the last week; `w' is weekday, 0..6.
1913 (while (if (memq w weekdays) 1914 (while (if (memq w weekdays)
1914 (setq day (1+ day)) 1915 (setq day (1+ day))
1915 (> (setq wkday (1- wkday)) 0)) 1916 (>= (setq wkday (1- wkday)) 0))
1916 (setq w (1+ w))) 1917 (setq w (1+ w)))
1917 (let ((hours (nth 7 math-holidays-cache))) 1918 (let ((hours (nth 7 math-holidays-cache)))
1918 (if hours 1919 (if hours
diff --git a/test/lisp/calc/calc-tests.el b/test/lisp/calc/calc-tests.el
index dce82b6f536..4dded007f79 100644
--- a/test/lisp/calc/calc-tests.el
+++ b/test/lisp/calc/calc-tests.el
@@ -458,6 +458,82 @@ An existing calc stack is reused, otherwise a new one is created."
458 (calcFunc-choose '(frac -15 2) 3)) 458 (calcFunc-choose '(frac -15 2) 3))
459 (calc-tests--choose -7.5 3)))) 459 (calc-tests--choose -7.5 3))))
460 460
461(ert-deftest calc-business-days ()
462 (cl-flet ((m (s) (math-parse-date s))
463 (b+ (a b) (calcFunc-badd a b))
464 (b- (a b) (calcFunc-bsub a b)))
465 ;; Sanity check.
466 (should (equal (m "2020-09-07") '(date 737675)))
467
468 ;; Test with standard business days (Mon-Fri):
469 (should (equal (b+ (m "2020-09-07") 1) (m "2020-09-08"))) ; Mon->Tue
470 (should (equal (b+ (m "2020-09-08") 1) (m "2020-09-09"))) ; Tue->Wed
471 (should (equal (b+ (m "2020-09-09") 1) (m "2020-09-10"))) ; Wed->Thu
472 (should (equal (b+ (m "2020-09-10") 1) (m "2020-09-11"))) ; Thu->Fri
473 (should (equal (b+ (m "2020-09-11") 1) (m "2020-09-14"))) ; Fri->Mon
474
475 (should (equal (b+ (m "2020-09-07") 4) (m "2020-09-11"))) ; Mon->Fri
476 (should (equal (b+ (m "2020-09-07") 6) (m "2020-09-15"))) ; Mon->Tue
477
478 (should (equal (b+ (m "2020-09-12") 1) (m "2020-09-14"))) ; Sat->Mon
479 (should (equal (b+ (m "2020-09-13") 1) (m "2020-09-14"))) ; Sun->Mon
480
481 (should (equal (b- (m "2020-09-11") 1) (m "2020-09-10"))) ; Fri->Thu
482 (should (equal (b- (m "2020-09-10") 1) (m "2020-09-09"))) ; Thu->Wed
483 (should (equal (b- (m "2020-09-09") 1) (m "2020-09-08"))) ; Wed->Tue
484 (should (equal (b- (m "2020-09-08") 1) (m "2020-09-07"))) ; Tue->Mon
485 (should (equal (b- (m "2020-09-07") 1) (m "2020-09-04"))) ; Mon->Fri
486
487 (should (equal (b- (m "2020-09-11") 4) (m "2020-09-07"))) ; Fri->Mon
488 (should (equal (b- (m "2020-09-15") 6) (m "2020-09-07"))) ; Tue->Mon
489
490 (should (equal (b- (m "2020-09-12") 1) (m "2020-09-11"))) ; Sat->Fri
491 (should (equal (b- (m "2020-09-13") 1) (m "2020-09-11"))) ; Sun->Fri
492
493 ;; Stepping fractional days
494 (should (equal (b+ (m "2020-09-08 21:00") '(frac 1 2))
495 (m "2020-09-09 09:00")))
496 (should (equal (b+ (m "2020-09-11 21:00") '(frac 1 2))
497 (m "2020-09-14 09:00")))
498 (should (equal (b- (m "2020-09-08 21:00") '(frac 1 2))
499 (m "2020-09-08 09:00")))
500 (should (equal (b- (m "2020-09-14 06:00") '(frac 1 2))
501 (m "2020-09-11 18:00")))
502
503 ;; Test with a couple of extra days off:
504 (let ((var-Holidays (list 'vec
505 '(var sat var-sat) '(var sun var-sun)
506 (m "2020-09-09") (m "2020-09-11"))))
507
508 (should (equal (b+ (m "2020-09-07") 1) (m "2020-09-08"))) ; Mon->Tue
509 (should (equal (b+ (m "2020-09-08") 1) (m "2020-09-10"))) ; Tue->Thu
510 (should (equal (b+ (m "2020-09-10") 1) (m "2020-09-14"))) ; Thu->Mon
511 (should (equal (b+ (m "2020-09-14") 1) (m "2020-09-15"))) ; Mon->Tue
512 (should (equal (b+ (m "2020-09-15") 1) (m "2020-09-16"))) ; Tue->Wed
513
514 (should (equal (b- (m "2020-09-16") 1) (m "2020-09-15"))) ; Wed->Tue
515 (should (equal (b- (m "2020-09-15") 1) (m "2020-09-14"))) ; Tue->Mon
516 (should (equal (b- (m "2020-09-14") 1) (m "2020-09-10"))) ; Mon->Thu
517 (should (equal (b- (m "2020-09-10") 1) (m "2020-09-08"))) ; Thu->Tue
518 (should (equal (b- (m "2020-09-08") 1) (m "2020-09-07"))) ; Tue->Mon
519 )
520
521 ;; Test with odd non-business weekdays (Tue, Wed, Sat):
522 (let ((var-Holidays '(vec (var tue var-tue)
523 (var wed var-wed)
524 (var sat var-sat))))
525 (should (equal (b+ (m "2020-09-07") 1) (m "2020-09-10"))) ; Mon->Thu
526 (should (equal (b+ (m "2020-09-10") 1) (m "2020-09-11"))) ; Thu->Fri
527 (should (equal (b+ (m "2020-09-11") 1) (m "2020-09-13"))) ; Fri->Sun
528 (should (equal (b+ (m "2020-09-13") 1) (m "2020-09-14"))) ; Sun->Mon
529
530 (should (equal (b- (m "2020-09-14") 1) (m "2020-09-13"))) ; Mon->Sun
531 (should (equal (b- (m "2020-09-13") 1) (m "2020-09-11"))) ; Sun->Fri
532 (should (equal (b- (m "2020-09-11") 1) (m "2020-09-10"))) ; Fri->Thu
533 (should (equal (b- (m "2020-09-10") 1) (m "2020-09-07"))) ; Thu->Mon
534 )
535 ))
536
461(provide 'calc-tests) 537(provide 'calc-tests)
462;;; calc-tests.el ends here 538;;; calc-tests.el ends here
463 539