From c05b9fe8a6e6cadad0de7821bd33fa5cc283c8e4 Mon Sep 17 00:00:00 2001
From: xuri <xuri.me@gmail.com>
Date: Fri, 24 Sep 2021 08:19:48 +0800
Subject: new formula function: DAYS, ref #65

---
 calc.go      | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 calc_test.go | 10 ++++++++++
 2 files changed, 63 insertions(+)

diff --git a/calc.go b/calc.go
index 3e402e6..266491f 100644
--- a/calc.go
+++ b/calc.go
@@ -323,6 +323,7 @@ type formulaFuncs struct {
 //    DATEDIF
 //    DATEVALUE
 //    DAY
+//    DAYS
 //    DB
 //    DDB
 //    DEC2BIN
@@ -6574,6 +6575,58 @@ func (fn *formulaFuncs) DAY(argsList *list.List) formulaArg {
 	return newNumberFormulaArg(float64(timeFromExcelTime(num.Number, false).Day()))
 }
 
+// DAYS function returns the number of days between two supplied dates. The
+// syntax of the function is:
+//
+//    DAYS(end_date,start_date)
+//
+func (fn *formulaFuncs) DAYS(argsList *list.List) formulaArg {
+	if argsList.Len() != 2 {
+		return newErrorFormulaArg(formulaErrorVALUE, "DAYS requires 2 arguments")
+	}
+	var end, start float64
+	endArg, startArg := argsList.Front().Value.(formulaArg), argsList.Back().Value.(formulaArg)
+	switch endArg.Type {
+	case ArgNumber:
+		end = endArg.Number
+	case ArgString:
+		endNum := endArg.ToNumber()
+		if endNum.Type == ArgNumber {
+			end = endNum.Number
+		} else {
+			args := list.New()
+			args.PushBack(endArg)
+			endValue := fn.DATEVALUE(args)
+			if endValue.Type == ArgError {
+				return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
+			}
+			end = endValue.Number
+		}
+	default:
+		return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
+	}
+	switch startArg.Type {
+	case ArgNumber:
+		start = startArg.Number
+	case ArgString:
+		startNum := startArg.ToNumber()
+		if startNum.Type == ArgNumber {
+			start = startNum.Number
+		} else {
+			args := list.New()
+			args.PushBack(startArg)
+			startValue := fn.DATEVALUE(args)
+			if startValue.Type == ArgError {
+				return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
+			}
+			start = startValue.Number
+		}
+	default:
+		return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
+	}
+	return newNumberFormulaArg(end - start)
+}
+
 // MONTH function returns the month of a date represented by a serial number.
 // The month is given as an integer, ranging from 1 (January) to 12
 // (December). The syntax of the function is:
diff --git a/calc_test.go b/calc_test.go
index 1e4d99b..54629a6 100644
--- a/calc_test.go
+++ b/calc_test.go
@@ -979,6 +979,10 @@ func TestCalcCellValue(t *testing.T) {
 		"=DAY(\"3-February-2008\")":              "3",
 		"=DAY(\"01/25/20\")":                     "25",
 		"=DAY(\"01/25/31\")":                     "25",
+		// DAYS
+		"=DAYS(2,1)":                           "1",
+		"=DAYS(INT(2),INT(1))":                 "1",
+		"=DAYS(\"02/02/2015\",\"01/01/2015\")": "32",
 		// MONTH
 		"=MONTH(42171)":           "6",
 		"=MONTH(\"31-May-2015\")": "5",
@@ -2040,6 +2044,12 @@ func TestCalcCellValue(t *testing.T) {
 		"=DAY(\"3-January-9223372036854775808\")":                             "#VALUE!",
 		"=DAY(\"9223372036854775808-January-1900\")":                          "#VALUE!",
 		"=DAY(\"0-January-1900\")":                                            "#VALUE!",
+		// DAYS
+		"=DAYS()":       "DAYS requires 2 arguments",
+		"=DAYS(\"\",0)": "#VALUE!",
+		"=DAYS(0,\"\")": "#VALUE!",
+		"=DAYS(NA(),0)": "#VALUE!",
+		"=DAYS(0,NA())": "#VALUE!",
 		// MONTH
 		"=MONTH()":                    "MONTH requires exactly 1 argument",
 		"=MONTH(0,0)":                 "MONTH requires exactly 1 argument",
-- 
cgit v1.2.1