Added tests for day convention arithmetic and other miscellaneous functions
This commit is contained in:
parent
9bb852795e
commit
28604b19d4
3 changed files with 163 additions and 122 deletions
|
|
@ -27,7 +27,6 @@
|
|||
(in-package :cl-dates-test)
|
||||
|
||||
(deftest julian ()
|
||||
(format t "Julian date conversions ...~%")
|
||||
(check
|
||||
(= (ymd->date 2017 2 5) 2457789.5)
|
||||
(= (ymd->date 1959 12 29) 2436931.5)
|
||||
|
|
@ -41,12 +40,16 @@
|
|||
(= (jday-number (ymd->date 2017 6 18)) 2457923)))
|
||||
|
||||
(deftest misc-fns ()
|
||||
(format t "Miscellaneous date functions ...~%")
|
||||
(check
|
||||
(valid-date-p 2000 2 29)
|
||||
(null (valid-date-p 1900 2 29))
|
||||
(null (valid-date-p 1234 65 789))
|
||||
|
||||
(null (leap-year-p 1900))
|
||||
(leap-year-p 2000)
|
||||
(null (leap-year-p 2001))
|
||||
(leap-year-p 2004)
|
||||
|
||||
(eq :monday (day-of-week (ymd->date 2017 2 6)))
|
||||
(eq :wednesday (day-of-week (ymd->date 1959 9 23)))
|
||||
|
||||
|
|
@ -55,10 +58,15 @@
|
|||
;; overflow month - returns last tuesday
|
||||
(= (nth-day-of-week (ymd->date 2017 2 5) :tuesday 7) (ymd->date 2017 2 28))
|
||||
;; any number <= 1 should return 1st tuesday
|
||||
(= (nth-day-of-week (ymd->date 2017 2 5) :tuesday -7) (ymd->date 2017 2 7))))
|
||||
(= (nth-day-of-week (ymd->date 2017 2 5) :tuesday -7) (ymd->date 2017 2 7))
|
||||
|
||||
(date= (ymd->date 2007 2 3 10 35 42) (ymd->date 2007 2 3 11 30 20) (ymd->date 2007 2 3 12 45 12))
|
||||
(date/= (ymd->date 2007 2 4) (ymd->date 2007 2 3) (ymd->date 2007 2 3))
|
||||
(date< (ymd->date 2007 2 3 10 35 42) (ymd->date 2007 2 4 11 30 20) (ymd->date 2007 2 5 12 45 12))
|
||||
(not (date< (ymd->date 2007 2 3 10 35 42) (ymd->date 2007 2 3 11 30 20) (ymd->date 2007 2 5 12 45 12)))
|
||||
(date<= (ymd->date 2007 2 3 10 35 42) (ymd->date 2007 2 3 11 30 20) (ymd->date 2007 2 5 12 45 12))))
|
||||
|
||||
(deftest print-fns ()
|
||||
(format t "Conversions to strings ...~%")
|
||||
(let ((dt1 (ymd->date 2017 2 16))
|
||||
(dt2 (ymd->date 2017 2 16 17 30 25 +9)))
|
||||
(check
|
||||
|
|
@ -102,7 +110,6 @@
|
|||
(string= (date->string dt2 :zone "JST" :format :rfc-850) "Thu, 16-Feb-2017 17:30:25 +0900"))))
|
||||
|
||||
(deftest special-dates ()
|
||||
(format t "Computation of special dates ...~%")
|
||||
(check
|
||||
(= (easter-day 2001) (ymd->date 2001 4 15))
|
||||
(= (easter-day 2002) (ymd->date 2002 3 31))
|
||||
|
|
@ -126,8 +133,39 @@
|
|||
(= (winter-solstice 1959)
|
||||
(ymd->date 1959 12 22 14 34 33.68946969509125d0))))
|
||||
|
||||
(deftest date-arith ()
|
||||
(check
|
||||
(= (date+ (ymd->date 2016 2 28) 1) (ymd->date 2016 2 29))
|
||||
(= (date+ (ymd->date 2016 2 28) 2) (ymd->date 2016 3 1))
|
||||
(= (date- (ymd->date 2016 3 1) 2) (ymd->date 2016 2 28))
|
||||
(= (date- (ymd->date 2016 3 1) 1) (ymd->date 2016 2 29))
|
||||
|
||||
(= (add-months (ymd->date 2016 2 29) 6) (ymd->date 2016 8 29))
|
||||
(= (add-months (ymd->date 2016 2 29) 6 :eom-rule :eom-normal) (ymd->date 2016 8 31))
|
||||
(= (add-months (ymd->date 2016 2 29) 6 :eom-rule :eom-no-leap-day) (ymd->date 2016 8 31))
|
||||
(= (add-months (ymd->date 2016 2 28) 6 :eom-rule :eom-normal) (ymd->date 2016 8 28))
|
||||
(= (add-months (ymd->date 2016 2 28) 6 :eom-rule :eom-no-leap-day) (ymd->date 2016 8 31))
|
||||
(= (add-months (ymd->date 2016 2 29) 12) (ymd->date 2017 2 28))
|
||||
(= (add-months (ymd->date 2016 2 29) 48) (ymd->date 2020 2 29))
|
||||
|
||||
(= (add-years (ymd->date 2016 2 29) 4) (add-months (ymd->date 2016 2 29) 48))
|
||||
|
||||
(= (add-months (ymd->date 2016 1 31) 1) (ymd->date 2016 2 29))
|
||||
(= (add-months (ymd->date 2016 1 31) 2) (ymd->date 2016 3 31))
|
||||
(= (add-months (ymd->date 2016 1 31) 3) (ymd->date 2016 4 30))
|
||||
|
||||
(= (add-months (ymd->date 2016 4 30) 1) (ymd->date 2016 5 30))
|
||||
(= (add-months (ymd->date 2016 4 30) 1 :eom-rule :eom-normal) (ymd->date 2016 5 31))
|
||||
(= (add-months (ymd->date 2016 4 30) 1 :eom-rule :eom-no-leap-day) (ymd->date 2016 5 31))
|
||||
(= (add-months (ymd->date 2016 4 30) 46 :eom-rule :eom-normal) (ymd->date 2020 2 29))
|
||||
(= (add-months (ymd->date 2016 4 30) 46 :eom-rule :eom-no-leap-day) (ymd->date 2020 2 28))
|
||||
|
||||
(= 0 (diff-days (ymd->date 2007 2 3 10 35 42) (ymd->date 2007 2 3 11 30 20)))
|
||||
(= 2 (diff-days (ymd->date 2008 2 28 10 35 42) (ymd->date 2008 3 1 11 30 20)))
|
||||
(= 366 (diff-days (ymd->date 2008 1 1 10 35 42) (ymd->date 2009 1 1 11 30 20)))
|
||||
(= 365 (diff-days (ymd->date 2007 1 1 10 35 42) (ymd->date 2008 1 1 11 30 20)))))
|
||||
|
||||
(deftest date-arith-360 ()
|
||||
(format t "Checking 30/360 and Actual/360 day basis computations~%")
|
||||
(let ((test-cases
|
||||
;; Start date End Date Bond Basis 30E/360 30E/360 ISDA Act/360
|
||||
'(("2007-01-15" "2007-01-30" 0.041666667 0.041666667 0.041666667 0.041666667)
|
||||
|
|
@ -154,8 +192,9 @@
|
|||
(term-date (ymd->date 2009 2 28)))
|
||||
(labels ((a= (a b)
|
||||
(< (abs (- a b)) 0.000001)))
|
||||
(dolist (test test-cases)
|
||||
(let ((d1 (string->date (first test)))
|
||||
(combine-results
|
||||
(loop for test in test-cases
|
||||
collect (let ((d1 (string->date (first test)))
|
||||
(d2 (string->date (second test)))
|
||||
(bond-basis (third test))
|
||||
(euro-basis (fourth test))
|
||||
|
|
@ -165,10 +204,9 @@
|
|||
(a= (diff-years d1 d2 :30a-360) bond-basis)
|
||||
(a= (diff-years d1 d2 :30e-360) euro-basis)
|
||||
(a= (diff-years d1 d2 :30e-360-isda :termination-date term-date) german)
|
||||
(a= (diff-years d1 d2 :act-360) actual)))))))
|
||||
(a= (diff-years d1 d2 :act-360) actual))))))))
|
||||
|
||||
(deftest date-arith-act-act ()
|
||||
(format t "Checking Actual/Actual day basis computations~%")
|
||||
(let ((test-cases
|
||||
(list
|
||||
;; Start End Date F LC AFB ISDA ISMA
|
||||
|
|
@ -182,8 +220,9 @@
|
|||
(list "2000-01-30" "2000-06-30" 2 t 152/366 152/366 (/ 152 (* 2 182)))
|
||||
(list "1999-11-30" "2000-04-30" 4 t 152/366 (+ 32/365 120/366) (+ (/ 91 (* 4 91))
|
||||
(/ 61 (* 4 92)))))))
|
||||
(dolist (test test-cases)
|
||||
(let ((d1 (string->date (first test)))
|
||||
(combine-results
|
||||
(loop for test in test-cases
|
||||
collect (let ((d1 (string->date (first test)))
|
||||
(d2 (string->date (second test)))
|
||||
(freq (third test))
|
||||
(last-cpn (fourth test))
|
||||
|
|
@ -193,4 +232,4 @@
|
|||
(check
|
||||
(= (diff-years d1 d2 :act-act-afb) afb)
|
||||
(= (diff-years d1 d2 :act-act) isda)
|
||||
(= (diff-years d1 d2 :act-act-isma :frequency freq :is-last-coupon last-cpn) isma))))))
|
||||
(= (diff-years d1 d2 :act-act-isma :frequency freq :is-last-coupon last-cpn) isma)))))))
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@
|
|||
cases."
|
||||
`(defun ,name ,parameters
|
||||
(let ((*test-name* (append *test-name* (list ',name))))
|
||||
(format t "~a ... from test #~d~%" *test-name* (1+ *total-tests*))
|
||||
,@body)))
|
||||
|
||||
(defmacro check (&body forms)
|
||||
|
|
@ -54,12 +55,9 @@
|
|||
`(combine-results
|
||||
,@(loop for f in forms collect `(report-result ,f ',f))))
|
||||
|
||||
(defmacro combine-results (&body forms)
|
||||
"Combine the results (as booleans) of evaluating 'forms' in order."
|
||||
(let ((result (gensym)))
|
||||
`(let ((,result t))
|
||||
,@(loop for f in forms collect `(unless ,f (setf ,result nil)))
|
||||
,result)))
|
||||
(defun combine-results (&rest exprs)
|
||||
"Combine the results (as booleans) of evaluating 'exprs' in order."
|
||||
(every #'identity exprs))
|
||||
|
||||
(defun report-result (result form)
|
||||
"Report the results of a single test case. Called by 'check'."
|
||||
|
|
@ -67,19 +65,20 @@
|
|||
(when (not result)
|
||||
(incf *failed-tests*))
|
||||
(if *verbose-results*
|
||||
(format t "~:[FAIL~;pass~] ... ~a: ~a~%" result *test-name* form)
|
||||
(format t "#~d: ~:[FAIL~;pass~] ... ~a: ~a~%" *total-tests* result *test-name* form)
|
||||
(when (not result)
|
||||
(format t "FAIL ... ~a: ~a~%" *test-name* form)))
|
||||
(format t "#~d: FAIL ... ~a: ~a~%" *total-tests* *test-name* form)))
|
||||
result)
|
||||
|
||||
(defun run-all-tests (&key (verbose nil))
|
||||
(let ((*total-tests* 0)
|
||||
(let* ((*total-tests* 0)
|
||||
(*failed-tests* 0)
|
||||
(*verbose-results* verbose)
|
||||
(status (combine-results
|
||||
(julian)
|
||||
(misc-fns)
|
||||
(special-dates)
|
||||
(date-arith)
|
||||
(date-arith-360)
|
||||
(date-arith-act-act)
|
||||
(print-fns)
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@
|
|||
(< (abs (- a b)) +max-error+))
|
||||
|
||||
(deftest parse-dates ()
|
||||
(format t "Parse dates from strings ...~%")
|
||||
(let ((dt (ymd->date 2003 9 25 1 36 28)))
|
||||
(check
|
||||
;; Fully specified date with time and timezone
|
||||
|
|
@ -200,4 +199,8 @@
|
|||
(= (string->date "01h02s" :reference-date dt) (ymd->date 2003 9 25 1 36 2))
|
||||
(= (string->date "01m02" :reference-date dt) (ymd->date 2003 9 25 1 1 2))
|
||||
(a= (string->date "01m02h" :reference-date dt) (ymd->date 2003 9 25 2 1 28))
|
||||
(= (string->date "2004 10 April 11h30m" :reference-date dt) (ymd->date 2004 4 10 11 30 28)))))
|
||||
(= (string->date "2004 10 April 11h30m" :reference-date dt) (ymd->date 2004 4 10 11 30 28))
|
||||
;; The next one works coincidentally because Japanese uses a y/m/d format by default.
|
||||
;; However, it demonstrates that as long as date components are present, the presence
|
||||
;; of extraneous characters does not matter
|
||||
(= (string->date "2004年8月9日") (ymd->date 2004 8 9)))))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue