]> source.dussan.org Git - poi.git/commitdiff
Bug 57150: Added EOMONTH function
authorCédric Walter <cedricwalter@apache.org>
Mon, 27 Oct 2014 12:29:32 +0000 (12:29 +0000)
committerCédric Walter <cedricwalter@apache.org>
Mon, 27 Oct 2014 12:29:32 +0000 (12:29 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1634515 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/ss/formula/atp/AnalysisToolPak.java
src/java/org/apache/poi/ss/formula/functions/EOMonth.java [new file with mode: 0644]
src/testcases/org/apache/poi/ss/formula/functions/RefValueImplementation.java [new file with mode: 0644]
src/testcases/org/apache/poi/ss/formula/functions/TestEDate.java
src/testcases/org/apache/poi/ss/formula/functions/TestEOMonth.java [new file with mode: 0644]

index ad9604d9c8ec61c737ffd4dd1d21e65271bb2c1c..66ca17ddd507f3cfd8a5dc19b94587ff573f80c5 100644 (file)
@@ -28,6 +28,7 @@ import org.apache.poi.ss.formula.functions.Dec2Bin;
 import org.apache.poi.ss.formula.functions.Dec2Hex;
 import org.apache.poi.ss.formula.functions.Delta;
 import org.apache.poi.ss.formula.functions.EDate;
+import org.apache.poi.ss.formula.functions.EOMonth;
 import org.apache.poi.ss.formula.functions.FactDouble;
 import org.apache.poi.ss.formula.functions.FreeRefFunction;
 import org.apache.poi.ss.formula.functions.Hex2Dec;
@@ -117,7 +118,7 @@ public final class AnalysisToolPak implements UDFFinder {
         r(m, "DURATION", null);
         r(m, "EDATE", EDate.instance);
         r(m, "EFFECT", null);
-        r(m, "EOMONTH", null);
+        r(m, "EOMONTH", EOMonth.instance);
         r(m, "ERF", null);
         r(m, "ERFC", null);
         r(m, "FACTDOUBLE", FactDouble.instance);
diff --git a/src/java/org/apache/poi/ss/formula/functions/EOMonth.java b/src/java/org/apache/poi/ss/formula/functions/EOMonth.java
new file mode 100644 (file)
index 0000000..8832e20
--- /dev/null
@@ -0,0 +1,81 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.ss.formula.functions;\r
+\r
+import java.util.Calendar;\r
+import java.util.Date;\r
+import java.util.GregorianCalendar;\r
+\r
+import org.apache.poi.ss.formula.OperationEvaluationContext;\r
+import org.apache.poi.ss.formula.eval.ErrorEval;\r
+import org.apache.poi.ss.formula.eval.EvaluationException;\r
+import org.apache.poi.ss.formula.eval.NumberEval;\r
+import org.apache.poi.ss.formula.eval.ValueEval;\r
+import org.apache.poi.ss.usermodel.DateUtil;\r
+\r
+/**\r
+ * Implementation for the Excel EOMONTH() function.<p/>\r
+ * <p/>\r
+ * EOMONTH() returns the date of the last day of a month..<p/>\r
+ * <p/>\r
+ * <b>Syntax</b>:<br/>\r
+ * <b>EOMONTH</b>(<b>start_date</b>,<b>months</b>)<p/>\r
+ * <p/>\r
+ * <b>start_date</b> is the starting date of the calculation\r
+ * <b>months</b> is the number of months to be added to <b>start_date</b>,\r
+ * to give a new date. For this new date, <b>EOMONTH</b> returns the date of\r
+ * the last day of the month. <b>months</b> may be positive (in the future),\r
+ * zero or negative (in the past).\r
+ */\r
+public class EOMonth implements FreeRefFunction {\r
+\r
+    public static final FreeRefFunction instance = new EOMonth();\r
+\r
+    @Override\r
+    public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {\r
+        if (args.length != 2) {\r
+            return ErrorEval.VALUE_INVALID;\r
+        }\r
+\r
+        try {\r
+            double startDateAsNumber = NumericFunction.singleOperandEvaluate(args[0], ec.getRowIndex(), ec.getColumnIndex());\r
+            int months = (int) NumericFunction.singleOperandEvaluate(args[1], ec.getRowIndex(), ec.getColumnIndex());\r
+\r
+            // Excel treats date 0 as 1900-01-00; EOMONTH results in 1900-01-31\r
+            if (startDateAsNumber >= 0.0 && startDateAsNumber < 1.0) {\r
+                startDateAsNumber = 1.0;\r
+            }\r
+\r
+            Date startDate = DateUtil.getJavaDate(startDateAsNumber, false);\r
+\r
+            Calendar cal = new GregorianCalendar();\r
+            cal.setTime(startDate);\r
+            cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), 0, 0, 0);\r
+            cal.set(Calendar.MILLISECOND, 0);\r
+\r
+            cal.add(Calendar.MONTH, months + 1);\r
+            cal.set(Calendar.DAY_OF_MONTH, 1);\r
+            cal.add(Calendar.DAY_OF_MONTH, -1);\r
+\r
+            return new NumberEval(DateUtil.getExcelDate(cal.getTime()));\r
+        } catch (EvaluationException e) {\r
+            return e.getErrorEval();\r
+        }\r
+    }\r
+\r
+}\r
diff --git a/src/testcases/org/apache/poi/ss/formula/functions/RefValueImplementation.java b/src/testcases/org/apache/poi/ss/formula/functions/RefValueImplementation.java
new file mode 100644 (file)
index 0000000..1a3fc1e
--- /dev/null
@@ -0,0 +1,68 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.ss.formula.functions;\r
+\r
+import org.apache.poi.ss.formula.eval.AreaEval;\r
+import org.apache.poi.ss.formula.eval.RefEval;\r
+import org.apache.poi.ss.formula.eval.ValueEval;\r
+\r
+final class RefEvalImplementation implements RefEval {\r
+\r
+    private final ValueEval value;\r
+\r
+    public RefEvalImplementation(ValueEval value) {\r
+        this.value = value;\r
+    }\r
+\r
+    @Override\r
+    public AreaEval offset(int relFirstRowIx, int relLastRowIx,\r
+            int relFirstColIx, int relLastColIx) {\r
+        throw new UnsupportedOperationException();\r
+    }\r
+\r
+    @Override\r
+    public ValueEval getInnerValueEval(int sheetIndex) {\r
+        return value;\r
+    }\r
+\r
+    @Override\r
+    public int getNumberOfSheets() {\r
+        return 1;\r
+    }\r
+\r
+    @Override\r
+    public int getFirstSheetIndex() {\r
+        return 0;\r
+    }\r
+\r
+    @Override\r
+    public int getLastSheetIndex() {\r
+        return 0;\r
+    }\r
+\r
+    @Override\r
+    public int getRow() {\r
+        throw new UnsupportedOperationException();\r
+    }\r
+\r
+    @Override\r
+    public int getColumn() {\r
+        throw new UnsupportedOperationException();\r
+    }\r
+\r
+}\r
index 3a5d527b771dc44a000ac88eda35f8d7e7f6e797..3917f44cb92383effed25695e33af29a9f1e79e4 100644 (file)
@@ -31,42 +31,7 @@ import org.apache.poi.ss.formula.eval.ValueEval;
 import org.apache.poi.ss.usermodel.DateUtil;
 import org.apache.poi.ss.usermodel.ErrorConstants;
 
-public class TestEDate extends TestCase{
-
-    private final class RefEvalImplementation implements RefEval {
-        private final ValueEval value;
-        
-        public RefEvalImplementation(ValueEval value) {
-            super();
-            this.value = value;
-        }
-
-        public AreaEval offset(int relFirstRowIx, int relLastRowIx,
-                int relFirstColIx, int relLastColIx) {
-            throw new UnsupportedOperationException();
-        }
-
-        public ValueEval getInnerValueEval(int sheetIndex) {
-            return value;
-        }
-        
-        public int getNumberOfSheets() {
-            return 1;
-        }
-        public int getFirstSheetIndex() {
-            return 0;
-        }
-        public int getLastSheetIndex() {
-            return 0;
-        }        
-        
-        public int getRow() {
-            throw new UnsupportedOperationException();
-        }
-        public int getColumn() {
-            throw new UnsupportedOperationException();
-        }
-    }
+public class TestEDate extends TestCase {
 
     public void testEDateProperValues() {
         // verify some border-case combinations of startDate and month-increase
diff --git a/src/testcases/org/apache/poi/ss/formula/functions/TestEOMonth.java b/src/testcases/org/apache/poi/ss/formula/functions/TestEOMonth.java
new file mode 100644 (file)
index 0000000..55372b0
--- /dev/null
@@ -0,0 +1,136 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.ss.formula.functions;\r
+\r
+import java.util.Calendar;\r
+import java.util.Date;\r
+\r
+import junit.framework.TestCase;\r
+import org.apache.poi.ss.formula.OperationEvaluationContext;\r
+\r
+import org.apache.poi.ss.formula.eval.BlankEval;\r
+import org.apache.poi.ss.formula.eval.ErrorEval;\r
+import org.apache.poi.ss.formula.eval.NumberEval;\r
+import org.apache.poi.ss.formula.eval.StringEval;\r
+import org.apache.poi.ss.formula.eval.ValueEval;\r
+import org.apache.poi.ss.usermodel.DateUtil;\r
+import org.apache.poi.ss.usermodel.ErrorConstants;\r
+\r
+public class TestEOMonth extends TestCase{\r
+\r
+    private static final double BAD_DATE = -1.0;\r
+\r
+    private static final double DATE_1900_01_01 = 1.0;\r
+    private static final double DATE_1900_01_31 = 31.0;\r
+    private static final double DATE_1900_02_28 = 59.0;\r
+    private static final double DATE_1902_09_26 = 1000.0;\r
+    private static final double DATE_1902_09_30 = 1004.0;\r
+    private static final double DATE_2034_06_09 = 49104.0;\r
+    private static final double DATE_2034_06_30 = 49125.0;\r
+    private static final double DATE_2034_07_31 = 49156.0;\r
+\r
+    private final FreeRefFunction eOMonth = EOMonth.instance;\r
+    private final OperationEvaluationContext ec = new OperationEvaluationContext(null, null, 0, 0, 0, null);\r
+\r
+    public void testEOMonthProperValues() {\r
+        // verify some border-case combinations of startDate and month-increase\r
+        checkValue(DATE_1900_01_01, 0, DATE_1900_01_31);\r
+        checkValue(DATE_1900_01_01, 1, DATE_1900_02_28);\r
+        checkValue(DATE_1902_09_26, 0, DATE_1902_09_30);\r
+        checkValue(DATE_2034_06_09, 0, DATE_2034_06_30);\r
+        checkValue(DATE_2034_06_09, 1, DATE_2034_07_31);\r
+    }\r
+\r
+    public void testEOMonthBadDateValues() {\r
+        checkValue(0.0, -2, BAD_DATE);\r
+        checkValue(0.0, -3, BAD_DATE);\r
+        checkValue(DATE_1900_01_31, -1, BAD_DATE);\r
+    }\r
+\r
+    private void checkValue(double startDate, int monthInc, double expectedResult) {\r
+        NumberEval result = (NumberEval) eOMonth.evaluate(new ValueEval[] {new NumberEval(startDate), new NumberEval(monthInc)}, ec);\r
+        assertEquals(expectedResult, result.getNumberValue());\r
+    }\r
+\r
+    public void testEOMonthZeroDate() {\r
+        NumberEval result = (NumberEval) eOMonth.evaluate(new ValueEval[] {new NumberEval(0), new NumberEval(0)}, ec);\r
+        assertEquals("0 startDate is 1900-01-00", DATE_1900_01_31, result.getNumberValue());\r
+\r
+        result = (NumberEval) eOMonth.evaluate(new ValueEval[] {new NumberEval(0), new NumberEval(1)}, ec);\r
+        assertEquals("0 startDate is 1900-01-00", DATE_1900_02_28, result.getNumberValue());\r
+    }\r
+\r
+    public void testEOMonthInvalidArguments() {\r
+        ValueEval result = eOMonth.evaluate(new ValueEval[] {new NumberEval(DATE_1902_09_26)}, ec);\r
+        assertTrue(result instanceof ErrorEval);\r
+        assertEquals(ErrorConstants.ERROR_VALUE, ((ErrorEval) result).getErrorCode());\r
+\r
+        result = eOMonth.evaluate(new ValueEval[] {new StringEval("a"), new StringEval("b")}, ec);\r
+        assertTrue(result instanceof ErrorEval);\r
+        assertEquals(ErrorConstants.ERROR_VALUE, ((ErrorEval) result).getErrorCode());\r
+    }\r
+\r
+    public void testEOMonthIncrease() {\r
+        checkOffset(new Date(), 2);\r
+    }\r
+\r
+    public void testEOMonthDecrease() {\r
+        checkOffset(new Date(), -2);\r
+    }\r
+\r
+    private void checkOffset(Date startDate, int offset) {\r
+        NumberEval result = (NumberEval) eOMonth.evaluate(new ValueEval[] {new NumberEval(DateUtil.getExcelDate(startDate)), new NumberEval(offset)}, ec);\r
+        Date resultDate = DateUtil.getJavaDate(result.getNumberValue());\r
+        Calendar instance = Calendar.getInstance();\r
+        instance.setTime(startDate);\r
+        instance.add(Calendar.MONTH, offset);\r
+        instance.add(Calendar.MONTH, 1);\r
+        instance.set(Calendar.DAY_OF_MONTH, 1);\r
+        instance.add(Calendar.DAY_OF_MONTH, -1);\r
+        instance.set(Calendar.HOUR_OF_DAY, 0);\r
+        instance.set(Calendar.MINUTE, 0);\r
+        instance.set(Calendar.SECOND, 0);\r
+        instance.set(Calendar.MILLISECOND, 0);\r
+        assertEquals(instance.getTime(), resultDate);\r
+    }\r
+\r
+    public void testBug56688() {\r
+        NumberEval result = (NumberEval) eOMonth.evaluate(new ValueEval[] {new NumberEval(DATE_1902_09_26), new RefEvalImplementation(new NumberEval(0))}, ec);\r
+        assertEquals(DATE_1902_09_30, result.getNumberValue());\r
+    }\r
+\r
+    public void testRefEvalStartDate() {\r
+        NumberEval result = (NumberEval) eOMonth.evaluate(new ValueEval[] {new RefEvalImplementation(new NumberEval(DATE_1902_09_26)), new NumberEval(0)}, ec);\r
+        assertEquals(DATE_1902_09_30, result.getNumberValue());\r
+    }\r
+\r
+    public void testEOMonthBlankValueEval() {\r
+        NumberEval evaluate = (NumberEval) eOMonth.evaluate(new ValueEval[] {BlankEval.instance, new NumberEval(0)}, ec);\r
+        assertEquals("Blank is handled as 0", DATE_1900_01_31, evaluate.getNumberValue());\r
+    }\r
+\r
+    public void testEOMonthBlankRefValueEval() {\r
+        NumberEval result = (NumberEval) eOMonth.evaluate(new ValueEval[] {new RefEvalImplementation(BlankEval.instance), new NumberEval(1)}, ec);\r
+        assertEquals("Blank is handled as 0",\r
+                DATE_1900_02_28, result.getNumberValue());\r
+\r
+        result = (NumberEval) eOMonth.evaluate(new ValueEval[] {new NumberEval(1), new RefEvalImplementation(BlankEval.instance)}, ec);\r
+        assertEquals("Blank is handled as 0",\r
+                DATE_1900_01_31, result.getNumberValue());\r
+    }\r
+}\r