From: PJ Fanning Date: Sat, 28 May 2022 18:12:36 +0000 (+0000) Subject: [bug-58468] implement DAYS function X-Git-Tag: REL_5_2_3~267 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=e40639a04d61eb714b5cc93c3b9488bfd167da0b;p=poi.git [bug-58468] implement DAYS function git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1901361 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/poi/src/main/java/org/apache/poi/ss/formula/atp/AnalysisToolPak.java b/poi/src/main/java/org/apache/poi/ss/formula/atp/AnalysisToolPak.java index 0824942f37..399762db81 100644 --- a/poi/src/main/java/org/apache/poi/ss/formula/atp/AnalysisToolPak.java +++ b/poi/src/main/java/org/apache/poi/ss/formula/atp/AnalysisToolPak.java @@ -98,6 +98,7 @@ public final class AnalysisToolPak implements UDFFinder { r(m, "CUBEVALUE", null); r(m, "CUMIPMT", null); r(m, "CUMPRINC", null); + r(m, "DAYS", Days.instance); r(m, "DEC2BIN", Dec2Bin.instance); r(m, "DEC2HEX", Dec2Hex.instance); r(m, "DEC2OCT", null); diff --git a/poi/src/main/java/org/apache/poi/ss/formula/functions/Days.java b/poi/src/main/java/org/apache/poi/ss/formula/functions/Days.java new file mode 100644 index 0000000000..a5a55943b2 --- /dev/null +++ b/poi/src/main/java/org/apache/poi/ss/formula/functions/Days.java @@ -0,0 +1,93 @@ +/* ==================================================================== + 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.time.LocalDate; +import java.time.temporal.ChronoUnit; +import java.util.Date; + +import org.apache.poi.ss.formula.OperationEvaluationContext; +import org.apache.poi.ss.formula.eval.EvaluationException; +import org.apache.poi.ss.formula.eval.NumberEval; +import org.apache.poi.ss.formula.eval.OperandResolver; +import org.apache.poi.ss.formula.eval.ValueEval; +import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.ss.util.DateParser; +import org.apache.poi.util.LocaleUtil; + +/** + *

Calculates the number of days between two dates based on a real year, + * which is used in some accounting calculations.

+ * + *

+ * {@code DAYS(end_date,start_date)} + * + *

+ *

+ * + * @see DAYS function - Microsoft Office + */ +public class Days implements FreeRefFunction { + + public static final Days instance = new Days(); + + private Days() {} + + @Override + public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) { + return evaluate(ec.getRowIndex(), ec.getColumnIndex(), args[0], args[1]); + } + + private ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { + double result; + try { + LocalDate d0 = getDate(arg0, srcRowIndex, srcColumnIndex); + LocalDate d1 = getDate(arg1, srcRowIndex, srcColumnIndex); + result = evaluate(d0, d1); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + return new NumberEval(result); + } + + private static double evaluate(LocalDate endDate, LocalDate startDate) { + return ChronoUnit.DAYS.between(startDate, endDate); + } + + private static LocalDate getDate(ValueEval eval, int srcRowIndex, int srcColumnIndex) throws EvaluationException { + ValueEval ve = OperandResolver.getSingleValue(eval, srcRowIndex, srcColumnIndex); + try { + double d0 = NumericFunction.singleOperandEvaluate(ve, srcRowIndex, srcColumnIndex); + return getDate(d0); + } catch (Exception e) { + String strText1 = OperandResolver.coerceValueToString(ve); + return DateParser.parseLocalDate(strText1); + } + } + + private static LocalDate getDate(double date) { + Date d = DateUtil.getJavaDate(date, false); + return d.toInstant() + .atZone(LocaleUtil.getUserTimeZone().toZoneId()) + .toLocalDate(); + } +} diff --git a/poi/src/test/java/org/apache/poi/ss/formula/functions/TestDays.java b/poi/src/test/java/org/apache/poi/ss/formula/functions/TestDays.java new file mode 100644 index 0000000000..575eb7a5aa --- /dev/null +++ b/poi/src/test/java/org/apache/poi/ss/formula/functions/TestDays.java @@ -0,0 +1,69 @@ + +/* ==================================================================== + 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.hssf.usermodel.HSSFCell; +import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator; +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.ss.usermodel.FormulaError; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.time.LocalDate; + +import static org.apache.poi.ss.util.Utils.addRow; +import static org.apache.poi.ss.util.Utils.assertDouble; +import static org.apache.poi.ss.util.Utils.assertError; + +/** + * Testcase for DAYS() functions + */ +public class TestDays { + + //https://support.microsoft.com/en-us/office/days-function-57740535-d549-4395-8728-0f07bff0b9df + @Test + void testMicrosoftExample1() throws IOException { + try (HSSFWorkbook wb = initWorkbook1()) { + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + HSSFCell cell = wb.getSheetAt(0).getRow(0).createCell(12); + assertDouble(fe, cell, "DAYS(\"15-MAR-2021\",\"1-FEB-2021\")", 42, 0.00000000001); + assertDouble(fe, cell, "DAYS(A2,A3)", 364, 0.00000000001); + } + } + + @Test + void testInvalid() throws IOException { + try (HSSFWorkbook wb = initWorkbook1()) { + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + HSSFCell cell = wb.getSheetAt(0).getRow(0).createCell(12); + assertError(fe, cell, "DAYS(\"15-XYZ\",\"1-FEB-2021\")", FormulaError.VALUE); + assertError(fe, cell, "DAYS(\"15-MAR-2021\",\"1-XYZ\")", FormulaError.VALUE); + } + } + + private HSSFWorkbook initWorkbook1() { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet = wb.createSheet(); + addRow(sheet, 0, "Data"); + addRow(sheet, 1, DateUtil.getExcelDate(LocalDate.parse("2021-12-31"))); + addRow(sheet, 2, DateUtil.getExcelDate(LocalDate.parse("2021-01-01"))); + return wb; + } +}