diff options
| author | Mattias EngdegÄrd | 2020-09-30 23:57:27 +0200 |
|---|---|---|
| committer | Mattias EngdegÄrd | 2020-10-02 10:35:52 +0200 |
| commit | 4cb16b6f42ea7ea088fa4134f8fe4ccfec16a56d (patch) | |
| tree | 618a3b8e1b61b6092fdb81dc7ead79c0794a453e | |
| parent | d037a6a2e6f92d793b1d5403dea4c7d3ca70883c (diff) | |
| download | emacs-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.el | 13 | ||||
| -rw-r--r-- | test/lisp/calc/calc-tests.el | 76 |
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 | ||