diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index 5f674c3..b984114 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -31,7 +31,7 @@ jobs:
run: env GO111MODULE=on go test -v -timeout 30m -race ./... -coverprofile=coverage.txt -covermode=atomic
- name: Codecov
- uses: codecov/codecov-action@v1
+ uses: codecov/codecov-action@v2
with:
file: coverage.txt
flags: unittests
diff --git a/calc.go b/calc.go
index 1c6edca..a4ecb66 100644
--- a/calc.go
+++ b/calc.go
@@ -55,13 +55,6 @@ const (
criteriaG
criteriaErr
criteriaRegexp
- // Numeric precision correct numeric values as legacy Excel application
- // https://en.wikipedia.org/wiki/Numeric_precision_in_Microsoft_Excel In the
- // top figure the fraction 1/9000 in Excel is displayed. Although this number
- // has a decimal representation that is an infinite string of ones, Excel
- // displays only the leading 15 figures. In the second line, the number one
- // is added to the fraction, and again Excel displays only 15 figures.
- numericPrecision = 1000000000000000
maxFinancialIterations = 128
financialPercision = 1.0e-08
// Date and time format regular expressions
@@ -511,6 +504,7 @@ type formulaFuncs struct {
// WEIBULL.DIST
// XOR
// YEAR
+// YEARFRAC
// Z.TEST
// ZTEST
//
@@ -533,7 +527,7 @@ func (f *File) CalcCellValue(sheet, cell string) (result string, err error) {
result = token.TValue
isNum, precision := isNumeric(result)
if isNum && precision > 15 {
- num, _ := roundPrecision(result)
+ num := roundPrecision(result, -1)
result = strings.ToUpper(num)
}
return
@@ -6689,6 +6683,157 @@ func (fn *formulaFuncs) YEAR(argsList *list.List) formulaArg {
return newNumberFormulaArg(float64(timeFromExcelTime(num.Number, false).Year()))
}
+// yearFracBasisCond is an implementation of the yearFracBasis1.
+func yearFracBasisCond(sy, sm, sd, ey, em, ed int) bool {
+ return (isLeapYear(sy) && (sm < 2 || (sm == 2 && sd <= 29))) || (isLeapYear(ey) && (em > 2 || (em == 2 && ed == 29)))
+}
+
+// yearFracBasis0 function returns the fraction of a year that between two
+// supplied dates in US (NASD) 30/360 type of day.
+func yearFracBasis0(startDate, endDate float64) (dayDiff, daysInYear float64) {
+ startTime, endTime := timeFromExcelTime(startDate, false), timeFromExcelTime(endDate, false)
+ sy, smM, sd := startTime.Date()
+ ey, emM, ed := endTime.Date()
+ sm, em := int(smM), int(emM)
+ if sd == 31 {
+ sd--
+ }
+ if sd == 30 && ed == 31 {
+ ed--
+ } else if leap := isLeapYear(sy); sm == 2 && ((leap && sd == 29) || (!leap && sd == 28)) {
+ sd = 30
+ if leap := isLeapYear(ey); em == 2 && ((leap && ed == 29) || (!leap && ed == 28)) {
+ ed = 30
+ }
+ }
+ dayDiff = float64((ey-sy)*360 + (em-sm)*30 + (ed - sd))
+ daysInYear = 360
+ return
+}
+
+// yearFracBasis1 function returns the fraction of a year that between two
+// supplied dates in actual type of day.
+func yearFracBasis1(startDate, endDate float64) (dayDiff, daysInYear float64) {
+ startTime, endTime := timeFromExcelTime(startDate, false), timeFromExcelTime(endDate, false)
+ sy, smM, sd := startTime.Date()
+ ey, emM, ed := endTime.Date()
+ sm, em := int(smM), int(emM)
+ dayDiff = endDate - startDate
+ isYearDifferent := sy != ey
+ if isYearDifferent && (ey != sy+1 || sm < em || (sm == em && sd < ed)) {
+ dayCount := 0
+ for y := sy; y <= ey; y++ {
+ dayCount += getYearDays(y, 1)
+ }
+ daysInYear = float64(dayCount) / float64(ey-sy+1)
+ } else {
+ if !isYearDifferent && isLeapYear(sy) {
+ daysInYear = 366
+ } else {
+ if isYearDifferent && yearFracBasisCond(sy, sm, sd, ey, em, ed) {
+ daysInYear = 366
+ } else {
+ daysInYear = 365
+ }
+ }
+ }
+ return
+}
+
+// yearFracBasis4 function returns the fraction of a year that between two
+// supplied dates in European 30/360 type of day.
+func yearFracBasis4(startDate, endDate float64) (dayDiff, daysInYear float64) {
+ startTime, endTime := timeFromExcelTime(startDate, false), timeFromExcelTime(endDate, false)
+ sy, smM, sd := startTime.Date()
+ ey, emM, ed := endTime.Date()
+ sm, em := int(smM), int(emM)
+ if sd == 31 {
+ sd--
+ }
+ if ed == 31 {
+ ed--
+ }
+ dayDiff = float64((ey-sy)*360 + (em-sm)*30 + (ed - sd))
+ daysInYear = 360
+ return
+}
+
+// yearFrac is an implementation of the formula function YEARFRAC.
+func yearFrac(startDate, endDate float64, basis int) formulaArg {
+ startTime, endTime := timeFromExcelTime(startDate, false), timeFromExcelTime(endDate, false)
+ if startTime == endTime {
+ return newNumberFormulaArg(0)
+ }
+ var dayDiff, daysInYear float64
+ switch basis {
+ case 0:
+ dayDiff, daysInYear = yearFracBasis0(startDate, endDate)
+ case 1:
+ dayDiff, daysInYear = yearFracBasis1(startDate, endDate)
+ case 2:
+ dayDiff = endDate - startDate
+ daysInYear = 360
+ case 3:
+ dayDiff = endDate - startDate
+ daysInYear = 365
+ case 4:
+ dayDiff, daysInYear = yearFracBasis4(startDate, endDate)
+ default:
+ return newErrorFormulaArg(formulaErrorNUM, "invalid basis")
+ }
+ return newNumberFormulaArg(dayDiff / daysInYear)
+}
+
+// getYearDays return days of the year with specifying the type of day count
+// basis to be used.
+func getYearDays(year, basis int) int {
+ switch basis {
+ case 1:
+ if isLeapYear(year) {
+ return 366
+ }
+ return 365
+ case 3:
+ return 365
+ default:
+ return 360
+ }
+}
+
+// YEARFRAC function returns the fraction of a year that is represented by the
+// number of whole days between two supplied dates. The syntax of the
+// function is:
+//
+// YEARFRAC(start_date,end_date,[basis])
+//
+func (fn *formulaFuncs) YEARFRAC(argsList *list.List) formulaArg {
+ if argsList.Len() != 2 && argsList.Len() != 3 {
+ return newErrorFormulaArg(formulaErrorVALUE, "YEARFRAC requires 3 or 4 arguments")
+ }
+ var basisArg formulaArg
+ startArg, endArg := argsList.Front().Value.(formulaArg).ToNumber(), argsList.Front().Next().Value.(formulaArg).ToNumber()
+ args := list.New().Init()
+ if startArg.Type != ArgNumber {
+ args.PushBack(argsList.Front().Value.(formulaArg))
+ if startArg = fn.DATEVALUE(args); startArg.Type != ArgNumber {
+ return startArg
+ }
+ }
+ if endArg.Type != ArgNumber {
+ args.Init()
+ args.PushBack(argsList.Front().Next().Value.(formulaArg))
+ if endArg = fn.DATEVALUE(args); endArg.Type != ArgNumber {
+ return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
+ }
+ }
+ if argsList.Len() == 3 {
+ if basisArg = argsList.Back().Value.(formulaArg).ToNumber(); basisArg.Type != ArgNumber {
+ return basisArg
+ }
+ }
+ return yearFrac(startArg.Number, endArg.Number, int(basisArg.Number))
+}
+
// NOW function returns the current date and time. The function receives no
// arguments and therefore. The syntax of the function is:
//
diff --git a/calc_test.go b/calc_test.go
index a391a19..de34723 100644
--- a/calc_test.go
+++ b/calc_test.go
@@ -57,12 +57,12 @@ func TestCalcCellValue(t *testing.T) {
// BESSELJ
"=BESSELJ(1.9,2)": "0.329925727692387",
// BESSELK
- "=BESSELK(0.05,0)": "3.114234034289662",
+ "=BESSELK(0.05,0)": "3.11423403428966",
"=BESSELK(0.05,1)": "19.90967432724863",
"=BESSELK(0.05,2)": "799.501207124235",
- "=BESSELK(3,2)": "0.061510458561912",
+ "=BESSELK(3,2)": "0.0615104585619118",
// BESSELY
- "=BESSELY(0.05,0)": "-1.979311006841528",
+ "=BESSELY(0.05,0)": "-1.97931100684153",
"=BESSELY(0.05,1)": "-12.789855163794034",
"=BESSELY(0.05,2)": "-509.61489554491976",
"=BESSELY(9,2)": "-0.229082087487741",
@@ -169,7 +169,7 @@ func TestCalcCellValue(t *testing.T) {
"=IMCOS(0.5)": "0.877582561890373",
"=IMCOS(\"3+0.5i\")": "-1.1163412445261518-0.0735369737112366i",
// IMCOSH
- "=IMCOSH(0.5)": "1.127625965206381",
+ "=IMCOSH(0.5)": "1.12762596520638",
"=IMCOSH(\"3+0.5i\")": "8.835204606500994+4.802825082743033i",
"=IMCOSH(\"2-i\")": "2.0327230070196656-3.0518977991518i",
"=IMCOSH(COMPLEX(1,-1))": "0.8337300251311491-0.9888977057628651i",
@@ -188,7 +188,7 @@ func TestCalcCellValue(t *testing.T) {
"=IMDIV(COMPLEX(5,2),COMPLEX(0,1))": "2-5i",
// IMEXP
"=IMEXP(0)": "1",
- "=IMEXP(0.5)": "1.648721270700128",
+ "=IMEXP(0.5)": "1.64872127070013",
"=IMEXP(\"1-2i\")": "-1.1312043837568135-2.4717266720048183i",
"=IMEXP(COMPLEX(1,-1))": "1.4686939399158851-2.2873552871788423i",
// IMLN
@@ -243,7 +243,7 @@ func TestCalcCellValue(t *testing.T) {
"=IMSUM(COMPLEX(5,2),COMPLEX(0,1))": "5+3i",
// IMTAN
"=IMTAN(-0)": "0",
- "=IMTAN(0.5)": "0.546302489843791",
+ "=IMTAN(0.5)": "0.54630248984379",
"=IMTAN(\"3+0.5i\")": "-0.11162105077158344+0.46946999342588536i",
"=IMTAN(\"2-i\")": "-0.24345820118572523-1.16673625724092i",
"=IMTAN(COMPLEX(1,-1))": "0.2717525853195117-1.0839233273386948i",
@@ -275,21 +275,21 @@ func TestCalcCellValue(t *testing.T) {
"=ABS(ABS(-1))": "1",
// ACOS
"=ACOS(-1)": "3.141592653589793",
- "=ACOS(0)": "1.570796326794897",
- "=ACOS(ABS(0))": "1.570796326794897",
+ "=ACOS(0)": "1.5707963267949",
+ "=ACOS(ABS(0))": "1.5707963267949",
// ACOSH
"=ACOSH(1)": "0",
"=ACOSH(2.5)": "1.566799236972411",
- "=ACOSH(5)": "2.292431669561178",
- "=ACOSH(ACOSH(5))": "1.471383321536679",
+ "=ACOSH(5)": "2.29243166956118",
+ "=ACOSH(ACOSH(5))": "1.47138332153668",
// ACOT
"=_xlfn.ACOT(1)": "0.785398163397448",
"=_xlfn.ACOT(-2)": "2.677945044588987",
- "=_xlfn.ACOT(0)": "1.570796326794897",
+ "=_xlfn.ACOT(0)": "1.5707963267949",
"=_xlfn.ACOT(_xlfn.ACOT(0))": "0.566911504941009",
// ACOTH
"=_xlfn.ACOTH(-5)": "-0.202732554054082",
- "=_xlfn.ACOTH(1.1)": "1.522261218861711",
+ "=_xlfn.ACOTH(1.1)": "1.52226121886171",
"=_xlfn.ACOTH(2)": "0.549306144334055",
"=_xlfn.ACOTH(ABS(-2))": "0.549306144334055",
// ARABIC
@@ -299,12 +299,12 @@ func TestCalcCellValue(t *testing.T) {
"=_xlfn.ARABIC(\"\")": "0",
"=_xlfn.ARABIC(\" ll lc \")": "-50",
// ASIN
- "=ASIN(-1)": "-1.570796326794897",
+ "=ASIN(-1)": "-1.5707963267949",
"=ASIN(0)": "0",
"=ASIN(ASIN(0))": "0",
// ASINH
"=ASINH(0)": "0",
- "=ASINH(-0.5)": "-0.481211825059604",
+ "=ASINH(-0.5)": "-0.481211825059603",
"=ASINH(2)": "1.44363547517881",
"=ASINH(ASINH(0))": "0",
// ATAN
@@ -383,22 +383,22 @@ func TestCalcCellValue(t *testing.T) {
"=COS(COS(0))": "0.54030230586814",
// COSH
"=COSH(0)": "1",
- "=COSH(0.5)": "1.127625965206381",
- "=COSH(-2)": "3.762195691083632",
- "=COSH(COSH(0))": "1.543080634815244",
+ "=COSH(0.5)": "1.12762596520638",
+ "=COSH(-2)": "3.76219569108363",
+ "=COSH(COSH(0))": "1.54308063481524",
// _xlfn.COT
- "=_xlfn.COT(0.785398163397448)": "1.000000000000001",
+ "=_xlfn.COT(0.785398163397448)": "1",
"=_xlfn.COT(_xlfn.COT(0.45))": "-0.545473116787229",
// _xlfn.COTH
- "=_xlfn.COTH(-3.14159265358979)": "-1.003741873197322",
- "=_xlfn.COTH(_xlfn.COTH(1))": "1.156014018113954",
+ "=_xlfn.COTH(-3.14159265358979)": "-1.00374187319732",
+ "=_xlfn.COTH(_xlfn.COTH(1))": "1.15601401811395",
// _xlfn.CSC
- "=_xlfn.CSC(-6)": "3.578899547254406",
+ "=_xlfn.CSC(-6)": "3.57889954725441",
"=_xlfn.CSC(1.5707963267949)": "1",
- "=_xlfn.CSC(_xlfn.CSC(1))": "1.077851840310882",
+ "=_xlfn.CSC(_xlfn.CSC(1))": "1.07785184031088",
// _xlfn.CSCH
- "=_xlfn.CSCH(-3.14159265358979)": "-0.086589537530047",
- "=_xlfn.CSCH(_xlfn.CSCH(1))": "1.044510103955183",
+ "=_xlfn.CSCH(-3.14159265358979)": "-0.0865895375300472",
+ "=_xlfn.CSCH(_xlfn.CSCH(1))": "1.04451010395518",
// _xlfn.DECIMAL
`=_xlfn.DECIMAL("1100",2)`: "12",
`=_xlfn.DECIMAL("186A0",16)`: "100000",
@@ -419,9 +419,9 @@ func TestCalcCellValue(t *testing.T) {
"=EVEN((0))": "0",
// EXP
"=EXP(100)": "2.6881171418161356E+43",
- "=EXP(0.1)": "1.105170918075648",
+ "=EXP(0.1)": "1.10517091807565",
"=EXP(0)": "1",
- "=EXP(-5)": "0.006737946999085",
+ "=EXP(-5)": "0.00673794699908547",
"=EXP(EXP(0))": "2.718281828459045",
// FACT
"=FACT(3)": "6",
@@ -502,18 +502,18 @@ func TestCalcCellValue(t *testing.T) {
"=LN(1)": "0",
"=LN(100)": "4.605170185988092",
"=LN(0.5)": "-0.693147180559945",
- "=LN(LN(100))": "1.527179625807901",
+ "=LN(LN(100))": "1.5271796258079",
// LOG
"=LOG(64,2)": "6",
"=LOG(100)": "2",
"=LOG(4,0.5)": "-2",
- "=LOG(500)": "2.698970004336019",
+ "=LOG(500)": "2.69897000433602",
"=LOG(LOG(100))": "0.301029995663981",
// LOG10
"=LOG10(100)": "2",
"=LOG10(1000)": "3",
"=LOG10(0.001)": "-3",
- "=LOG10(25)": "1.397940008672038",
+ "=LOG10(25)": "1.39794000867204",
"=LOG10(LOG10(100))": "0.301029995663981",
// IMLOG2
"=IMLOG2(\"5+2i\")": "2.4289904975637864+0.5489546632866347i",
@@ -626,9 +626,9 @@ func TestCalcCellValue(t *testing.T) {
"=_xlfn.SEC(0)": "1",
"=_xlfn.SEC(_xlfn.SEC(0))": "0.54030230586814",
// SECH
- "=_xlfn.SECH(-3.14159265358979)": "0.086266738334055",
+ "=_xlfn.SECH(-3.14159265358979)": "0.0862667383340547",
"=_xlfn.SECH(0)": "1",
- "=_xlfn.SECH(_xlfn.SECH(0))": "0.648054273663886",
+ "=_xlfn.SECH(_xlfn.SECH(0))": "0.648054273663885",
// SIGN
"=SIGN(9.5)": "1",
"=SIGN(-9.5)": "-1",
@@ -665,10 +665,10 @@ func TestCalcCellValue(t *testing.T) {
"=STDEVA(MUNIT(2))": "0.577350269189626",
"=STDEVA(0,INT(0))": "0",
// POISSON.DIST
- "=POISSON.DIST(20,25,FALSE)": "0.051917468608491",
+ "=POISSON.DIST(20,25,FALSE)": "0.0519174686084913",
"=POISSON.DIST(35,40,TRUE)": "0.242414197690103",
// POISSON
- "=POISSON(20,25,FALSE)": "0.051917468608491",
+ "=POISSON(20,25,FALSE)": "0.0519174686084913",
"=POISSON(35,40,TRUE)": "0.242414197690103",
// SUM
"=SUM(1,2)": "3",
@@ -760,15 +760,15 @@ func TestCalcCellValue(t *testing.T) {
"=GAMMA(1.5)": "0.886226925452758",
"=GAMMA(5.5)": "52.34277778455352",
// GAMMALN
- "=GAMMALN(4.5)": "2.453736570842443",
+ "=GAMMALN(4.5)": "2.45373657084244",
"=GAMMALN(INT(1))": "0",
// HARMEAN
- "=HARMEAN(2.5,3,0.5,1,3)": "1.229508196721312",
- "=HARMEAN(\"2.5\",3,0.5,1,INT(3),\"\")": "1.229508196721312",
+ "=HARMEAN(2.5,3,0.5,1,3)": "1.22950819672131",
+ "=HARMEAN(\"2.5\",3,0.5,1,INT(3),\"\")": "1.22950819672131",
// KURT
- "=KURT(F1:F9)": "-1.033503502551368",
- "=KURT(F1,F2:F9)": "-1.033503502551368",
- "=KURT(INT(1),MUNIT(2))": "-3.333333333333336",
+ "=KURT(F1:F9)": "-1.03350350255137",
+ "=KURT(F1,F2:F9)": "-1.03350350255137",
+ "=KURT(INT(1),MUNIT(2))": "-3.33333333333334",
// NORM.DIST
"=NORM.DIST(0.8,1,0.3,TRUE)": "0.252492537546923",
"=NORM.DIST(50,40,20,FALSE)": "0.017603266338215",
@@ -993,6 +993,29 @@ func TestCalcCellValue(t *testing.T) {
"=YEAR(42171)": "2015",
"=YEAR(\"29-May-2015\")": "2015",
"=YEAR(\"05/03/1984\")": "1984",
+ // YEARFRAC
+ "=YEARFRAC(42005,42005)": "0",
+ "=YEARFRAC(42005,42094)": "0.25",
+ "=YEARFRAC(42005,42094,0)": "0.25",
+ "=YEARFRAC(42005,42094,1)": "0.243835616438356",
+ "=YEARFRAC(42005,42094,2)": "0.247222222222222",
+ "=YEARFRAC(42005,42094,3)": "0.243835616438356",
+ "=YEARFRAC(42005,42094,4)": "0.247222222222222",
+ "=YEARFRAC(\"01/01/2015\",\"03/31/2015\")": "0.25",
+ "=YEARFRAC(\"01/01/2015\",\"03/31/2015\",0)": "0.25",
+ "=YEARFRAC(\"01/01/2015\",\"03/31/2015\",1)": "0.243835616438356",
+ "=YEARFRAC(\"01/01/2015\",\"03/31/2015\",2)": "0.247222222222222",
+ "=YEARFRAC(\"01/01/2015\",\"03/31/2015\",3)": "0.243835616438356",
+ "=YEARFRAC(\"01/01/2015\",\"03/31/2015\",4)": "0.247222222222222",
+ "=YEARFRAC(\"01/01/2015\",42094)": "0.25",
+ "=YEARFRAC(42005,\"03/31/2015\",0)": "0.25",
+ "=YEARFRAC(\"01/31/2015\",\"03/31/2015\")": "0.166666666666667",
+ "=YEARFRAC(\"01/30/2015\",\"03/31/2015\")": "0.166666666666667",
+ "=YEARFRAC(\"02/29/2000\", \"02/29/2008\")": "8",
+ "=YEARFRAC(\"02/29/2000\", \"02/29/2008\",1)": "7.998175182481752",
+ "=YEARFRAC(\"02/29/2000\", \"01/29/2001\",1)": "0.915300546448087",
+ "=YEARFRAC(\"02/29/2000\", \"03/29/2000\",1)": "0.0792349726775956",
+ "=YEARFRAC(\"01/31/2000\", \"03/29/2000\",4)": "0.163888888888889",
// Text Functions
// CHAR
"=CHAR(65)": "A",
@@ -1246,7 +1269,7 @@ func TestCalcCellValue(t *testing.T) {
"=ISPMT(0.05/12,2,60,50000)": "-201.38888888888886",
"=ISPMT(0.05/12,2,1,50000)": "208.33333333333334",
// NOMINAL
- "=NOMINAL(0.025,12)": "0.024718035238113",
+ "=NOMINAL(0.025,12)": "0.0247180352381129",
// NPER
"=NPER(0.04,-6000,50000)": "10.338035071507665",
"=NPER(0,-6000,50000)": "8.333333333333334",
@@ -2062,6 +2085,12 @@ func TestCalcCellValue(t *testing.T) {
"=YEAR(-1)": "YEAR only accepts positive argument",
"=YEAR(\"text\")": "#VALUE!",
"=YEAR(\"January 25, 100\")": "#VALUE!",
+ // YEARFRAC
+ "=YEARFRAC()": "YEARFRAC requires 3 or 4 arguments",
+ "=YEARFRAC(42005,42094,5)": "invalid basis",
+ "=YEARFRAC(\"\",42094,5)": "#VALUE!",
+ "=YEARFRAC(42005,\"\",5)": "#VALUE!",
+ "=YEARFRAC(42005,42094,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
// NOW
"=NOW(A1)": "NOW accepts no arguments",
// TODAY
@@ -2412,8 +2441,8 @@ func TestCalcCellValue(t *testing.T) {
"=(-2-SUM(-4+A2))*5": "0",
"=SUM(Sheet1!A1:Sheet1!A1:A2,A2)": "5",
"=SUM(A1,A2,A3)*SUM(2,3)": "30",
- "=1+SUM(SUM(A1+A2/A3)*(2-3),2)": "1.333333333333334",
- "=A1/A2/SUM(A1:A2:B1)": "0.041666666666667",
+ "=1+SUM(SUM(A1+A2/A3)*(2-3),2)": "1.33333333333333",
+ "=A1/A2/SUM(A1:A2:B1)": "0.0416666666666667",
"=A1/A2/SUM(A1:A2:B1)*A3": "0.125",
"=SUM(B1:D1)": "4",
"=SUM(\"X\")": "0",
@@ -2760,7 +2789,7 @@ func TestCalcMIRR(t *testing.T) {
cellData := [][]interface{}{{-100}, {18}, {22.5}, {28}, {35.5}, {45}}
f := prepareCalcData(cellData)
formulaList := map[string]string{
- "=MIRR(A1:A5,0.055,0.05)": "0.025376365108071",
+ "=MIRR(A1:A5,0.055,0.05)": "0.0253763651080707",
"=MIRR(A1:A6,0.055,0.05)": "0.1000268752662",
}
for formula, expected := range formulaList {
@@ -2848,3 +2877,9 @@ func TestStrToDate(t *testing.T) {
_, _, _, _, err := strToDate("")
assert.Equal(t, formulaErrorVALUE, err.Error)
}
+
+func TestGetYearDays(t *testing.T) {
+ for _, data := range [][]int{{2021, 0, 360}, {2000, 1, 366}, {2021, 1, 365}, {2000, 3, 365}} {
+ assert.Equal(t, data[2], getYearDays(data[0], data[1]))
+ }
+}
diff --git a/cell.go b/cell.go
index b95db9c..41a9517 100644
--- a/cell.go
+++ b/cell.go
@@ -70,15 +70,6 @@ func (f *File) GetCellValue(sheet, axis string, opts ...Options) (string, error)
// GetCellType provides a function to get the cell's data type by given
// worksheet name and axis in spreadsheet file.
func (f *File) GetCellType(sheet, axis string) (CellType, error) {
- cellTypes := map[string]CellType{
- "b": CellTypeBool,
- "d": CellTypeDate,
- "n": CellTypeNumber,
- "e": CellTypeError,
- "s": CellTypeString,
- "str": CellTypeString,
- "inlineStr": CellTypeString,
- }
var (
err error
cellTypeStr string
@@ -1046,9 +1037,16 @@ func (f *File) formattedValue(s int, v string, raw bool) string {
precise := v
isNum, precision := isNumeric(v)
if isNum && precision > 10 {
- precise, _ = roundPrecision(v)
+ precise = roundPrecision(v, -1)
+ }
+ if raw {
+ return v
+ }
+ if !isNum {
+ v = roundPrecision(v, 15)
+ precise = v
}
- if s == 0 || raw {
+ if s == 0 {
return precise
}
styleSheet := f.stylesReader()
@@ -1063,7 +1061,7 @@ func (f *File) formattedValue(s int, v string, raw bool) string {
ok := builtInNumFmtFunc[numFmtID]
if ok != nil {
- return ok(v, builtInNumFmt[numFmtID])
+ return ok(precise, builtInNumFmt[numFmtID])
}
if styleSheet == nil || styleSheet.NumFmts == nil {
return precise
diff --git a/cell_test.go b/cell_test.go
index 126e561..0395ef4 100644
--- a/cell_test.go
+++ b/cell_test.go
@@ -245,10 +245,66 @@ func TestGetCellValue(t *testing.T) {
assert.NoError(t, err)
f.Sheet.Delete("xl/worksheets/sheet1.xml")
- f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(fmt.Sprintf(sheetData, `2422.30000000000022422.300000000000212.49641101.5999999999999275.3999999999999868.90000000000000644385.2083333333365.09999999999999965.11000000000000035.09999999999999965.11099999999999985.11110000000000042422.0123456782422.012345678912.0123456789019641101.5999999999999275.3999999999999868.900000000000006
`)))
+ f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(fmt.Sprintf(sheetData, `
+ 2422.3000000000002
+ 2422.3000000000002
+ 12.4
+ 964
+ 1101.5999999999999
+ 275.39999999999998
+ 68.900000000000006
+ 44385.208333333336
+ 5.0999999999999996
+ 5.1100000000000003
+ 5.0999999999999996
+ 5.1109999999999998
+ 5.1111000000000004
+ 2422.012345678
+ 2422.0123456789
+ 12.012345678901
+ 964
+ 1101.5999999999999
+ 275.39999999999998
+ 68.900000000000006
+ 8.8880000000000001E-2
+ 4.0000000000000003E-5
+ 2422.3000000000002
+ 1101.5999999999999
+ 275.39999999999998
+ 68.900000000000006
+ 1.1000000000000001
+
`)))
f.checked = nil
rows, err = f.GetRows("Sheet1")
- assert.Equal(t, [][]string{{"2422.3", "2422.3", "12.4", "964", "1101.6", "275.4", "68.9", "44385.20833333333", "5.1", "5.11", "5.1", "5.111", "5.1111", "2422.012345678", "2422.0123456789", "12.012345678901", "964", "1101.6", "275.4", "68.9"}}, rows)
+ assert.Equal(t, [][]string{{
+ "2422.3",
+ "2422.3",
+ "12.4",
+ "964",
+ "1101.6",
+ "275.4",
+ "68.9",
+ "44385.2083333333",
+ "5.1",
+ "5.11",
+ "5.1",
+ "5.111",
+ "5.1111",
+ "2422.012345678",
+ "2422.0123456789",
+ "12.012345678901",
+ "964",
+ "1101.6",
+ "275.4",
+ "68.9",
+ "0.08888",
+ "0.00004",
+ "2422.3",
+ "1101.6",
+ "275.4",
+ "68.9",
+ "1.1",
+ }}, rows)
assert.NoError(t, err)
}
diff --git a/rows.go b/rows.go
index 3d8c247..3171ab1 100644
--- a/rows.go
+++ b/rows.go
@@ -18,6 +18,7 @@ import (
"io"
"log"
"math"
+ "math/big"
"os"
"strconv"
@@ -421,14 +422,19 @@ func (c *xlsxC) getValueFrom(f *File, d *xlsxSST, raw bool) (string, error) {
}
}
-// roundPrecision round precision for numeric.
-func roundPrecision(value string) (result string, err error) {
- var num float64
- if num, err = strconv.ParseFloat(value, 64); err != nil {
- return
+// roundPrecision provides a function to format floating-point number text
+// with precision, if the given text couldn't be parsed to float, this will
+// return the original string.
+func roundPrecision(text string, prec int) string {
+ decimal := big.Float{}
+ if _, ok := decimal.SetString(text); ok {
+ flt, _ := decimal.Float64()
+ if prec == -1 {
+ return decimal.Text('G', 15)
+ }
+ return strconv.FormatFloat(flt, 'f', -1, 64)
}
- result = fmt.Sprintf("%g", math.Round(num*numericPrecision)/numericPrecision)
- return
+ return text
}
// SetRowVisible provides a function to set visible of a single row by given
diff --git a/rows_test.go b/rows_test.go
index 0ebe59d..19ed866 100644
--- a/rows_test.go
+++ b/rows_test.go
@@ -884,11 +884,6 @@ func TestGetValueFromNumber(t *testing.T) {
}
}
-func TestRoundPrecision(t *testing.T) {
- _, err := roundPrecision("")
- assert.EqualError(t, err, "strconv.ParseFloat: parsing \"\": invalid syntax")
-}
-
func TestErrSheetNotExistError(t *testing.T) {
err := ErrSheetNotExist{SheetName: "Sheet1"}
assert.EqualValues(t, err.Error(), "sheet Sheet1 is not exist")