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