aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/documentation/content/xdocs/status.xml1
-rw-r--r--src/java/org/apache/poi/ss/formula/eval/FunctionEval.java5
-rw-r--r--src/java/org/apache/poi/ss/formula/functions/CalendarFieldFunction.java45
-rw-r--r--src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java57
-rw-r--r--src/testcases/org/apache/poi/ss/formula/functions/TestCalendarFieldFunction.java85
-rw-r--r--test-data/spreadsheet/48968.xlsbin0 -> 22016 bytes
6 files changed, 171 insertions, 22 deletions
diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml
index 23f60da20b..3277a33a9f 100644
--- a/src/documentation/content/xdocs/status.xml
+++ b/src/documentation/content/xdocs/status.xml
@@ -34,6 +34,7 @@
<changes>
<release version="3.8-beta2" date="2011-??-??">
+ <action dev="poi-developers" type="add">48968 - Support for HOUR, MINUTE and SECOND date formulas</action>
<action dev="poi-developers" type="add">Added NPOIFS constructors to most POIDocument classes and their extractors, and more widely deprecated the Document(DirectoryNode, POIFSFileSystem) constructor in favour of the more general Document(DirectoryNode) one</action>
<action dev="poi-developers" type="fix">Fixed NPOIFS handling of new and empty Document Nodes</action>
<action dev="poi-developers" type="fix">Fixed NPOIFS access to Document Nodes not in the top level directory</action>
diff --git a/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java b/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java
index 7c84a5860b..428b6d797d 100644
--- a/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java
+++ b/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java
@@ -94,7 +94,7 @@ public final class FunctionEval {
retval[38] = BooleanFunction.NOT;
retval[39] = NumericFunction.MOD;
- retval[46] = AggregateFunction.VAR;
+ retval[46] = AggregateFunction.VAR;
retval[48] = TextFunction.TEXT;
retval[56] = FinanceFunction.PV;
@@ -111,6 +111,9 @@ public final class FunctionEval {
retval[68] = CalendarFieldFunction.MONTH;
retval[69] = CalendarFieldFunction.YEAR;
+ retval[71] = CalendarFieldFunction.HOUR;
+ retval[72] = CalendarFieldFunction.MINUTE;
+ retval[73] = CalendarFieldFunction.SECOND;
retval[74] = new Now();
retval[76] = new Rows();
diff --git a/src/java/org/apache/poi/ss/formula/functions/CalendarFieldFunction.java b/src/java/org/apache/poi/ss/formula/functions/CalendarFieldFunction.java
index 2d95c96d00..73a4b2341e 100644
--- a/src/java/org/apache/poi/ss/formula/functions/CalendarFieldFunction.java
+++ b/src/java/org/apache/poi/ss/formula/functions/CalendarFieldFunction.java
@@ -29,30 +29,29 @@ import org.apache.poi.ss.formula.eval.ValueEval;
import org.apache.poi.ss.usermodel.DateUtil;
/**
- * Implementation of Excel functions DAY, MONTH and YEAR
- *
- *
- * @author Guenter Kickinger g.kickinger@gmx.net
+ * Implementation of Excel functions Date parsing functions:
+ * Date - DAY, MONTH and YEAR
+ * Time - HOUR, MINUTE and SECOND
*/
public final class CalendarFieldFunction extends Fixed1ArgFunction {
-
- public static final Function YEAR = new CalendarFieldFunction(Calendar.YEAR, false);
- public static final Function MONTH = new CalendarFieldFunction(Calendar.MONTH, true);
- public static final Function DAY = new CalendarFieldFunction(Calendar.DAY_OF_MONTH, false);
+ public static final Function YEAR = new CalendarFieldFunction(Calendar.YEAR);
+ public static final Function MONTH = new CalendarFieldFunction(Calendar.MONTH);
+ public static final Function DAY = new CalendarFieldFunction(Calendar.DAY_OF_MONTH);
+ public static final Function HOUR = new CalendarFieldFunction(Calendar.HOUR_OF_DAY);
+ public static final Function MINUTE = new CalendarFieldFunction(Calendar.MINUTE);
+ public static final Function SECOND = new CalendarFieldFunction(Calendar.SECOND);
private final int _dateFieldId;
- private final boolean _needsOneBaseAdjustment;
- private CalendarFieldFunction(int dateFieldId, boolean needsOneBaseAdjustment) {
+ private CalendarFieldFunction(int dateFieldId) {
_dateFieldId = dateFieldId;
- _needsOneBaseAdjustment = needsOneBaseAdjustment;
}
public final ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
- int val;
+ double val;
try {
ValueEval ve = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex);
- val = OperandResolver.coerceValueToInt(ve);
+ val = OperandResolver.coerceValueToDouble(ve);
} catch (EvaluationException e) {
return e.getErrorEval();
}
@@ -62,26 +61,30 @@ public final class CalendarFieldFunction extends Fixed1ArgFunction {
return new NumberEval(getCalField(val));
}
- private int getCalField(int serialDay) {
- if (serialDay == 0) {
- // Special weird case
- // day zero should be 31-Dec-1899, but Excel seems to think it is 0-Jan-1900
+ private int getCalField(double serialDate) {
+ // For some reason, a date of 0 in Excel gets shown
+ // as the non existant 1900-01-00
+ if(((int)serialDate) == 0) {
switch (_dateFieldId) {
case Calendar.YEAR: return 1900;
case Calendar.MONTH: return 1;
case Calendar.DAY_OF_MONTH: return 0;
}
- throw new IllegalStateException("bad date field " + _dateFieldId);
+ // They want time, that's normal
}
- Date d = DateUtil.getJavaDate(serialDay, false); // TODO fix 1900/1904 problem
+
+ // TODO Figure out if we're in 1900 or 1904
+ Date d = DateUtil.getJavaDate(serialDate, false);
Calendar c = new GregorianCalendar();
c.setTime(d);
-
int result = c.get(_dateFieldId);
- if (_needsOneBaseAdjustment) {
+
+ // Month is a special case due to C semantics
+ if (_dateFieldId == Calendar.MONTH) {
result++;
}
+
return result;
}
}
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java
index 590d096e11..d82674d733 100644
--- a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java
+++ b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java
@@ -22,10 +22,16 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
import java.util.Iterator;
import java.util.List;
+import java.util.Locale;
+import java.util.TimeZone;
import junit.framework.AssertionFailedError;
@@ -46,6 +52,7 @@ import org.apache.poi.ss.formula.ptg.Ptg;
import org.apache.poi.ss.usermodel.BaseTestBugzillaIssues;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Name;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
@@ -2042,4 +2049,54 @@ if(1==2) {
assertEquals(1, wb.getNumberOfSheets());
assertEquals("DGATE", wb.getSheetAt(0).getRow(1).getCell(0).getStringCellValue());
}
+
+ public void test48968() throws Exception {
+ HSSFWorkbook wb = openSample("48968.xls");
+ assertEquals(1, wb.getNumberOfSheets());
+
+ // Check the dates
+ HSSFSheet s = wb.getSheetAt(0);
+ Date d20110325 = s.getRow(0).getCell(0).getDateCellValue();
+ Date d19000102 = s.getRow(11).getCell(0).getDateCellValue();
+ Date d19000100 = s.getRow(21).getCell(0).getDateCellValue();
+ assertEquals(s.getRow(0).getCell(3).getStringCellValue(), timeToUTC(d20110325));
+ assertEquals(s.getRow(11).getCell(3).getStringCellValue(), timeToUTC(d19000102));
+ // There is no such thing as 00/01/1900...
+ assertEquals("00/01/1900 06:14:24", s.getRow(21).getCell(3).getStringCellValue());
+ assertEquals("31/12/1899 06:14:24", timeToUTC(d19000100));
+
+ // Check the cached values
+ assertEquals("HOUR(A1)", s.getRow(5).getCell(0).getCellFormula());
+ assertEquals(11.0, s.getRow(5).getCell(0).getNumericCellValue());
+ assertEquals("MINUTE(A1)", s.getRow(6).getCell(0).getCellFormula());
+ assertEquals(39.0, s.getRow(6).getCell(0).getNumericCellValue());
+ assertEquals("SECOND(A1)", s.getRow(7).getCell(0).getCellFormula());
+ assertEquals(54.0, s.getRow(7).getCell(0).getNumericCellValue());
+
+ // Re-evaulate and check
+ HSSFFormulaEvaluator.evaluateAllFormulaCells(wb);
+ assertEquals("HOUR(A1)", s.getRow(5).getCell(0).getCellFormula());
+ assertEquals(11.0, s.getRow(5).getCell(0).getNumericCellValue());
+ assertEquals("MINUTE(A1)", s.getRow(6).getCell(0).getCellFormula());
+ assertEquals(39.0, s.getRow(6).getCell(0).getNumericCellValue());
+ assertEquals("SECOND(A1)", s.getRow(7).getCell(0).getCellFormula());
+ assertEquals(54.0, s.getRow(7).getCell(0).getNumericCellValue());
+
+ // Push the time forward a bit and check
+ double date = s.getRow(0).getCell(0).getNumericCellValue();
+ s.getRow(0).getCell(0).setCellValue(date + 1.26);
+
+ HSSFFormulaEvaluator.evaluateAllFormulaCells(wb);
+ assertEquals("HOUR(A1)", s.getRow(5).getCell(0).getCellFormula());
+ assertEquals(11.0+6.0, s.getRow(5).getCell(0).getNumericCellValue());
+ assertEquals("MINUTE(A1)", s.getRow(6).getCell(0).getCellFormula());
+ assertEquals(39.0+14.0+1, s.getRow(6).getCell(0).getNumericCellValue());
+ assertEquals("SECOND(A1)", s.getRow(7).getCell(0).getCellFormula());
+ assertEquals(54.0+24.0-60, s.getRow(7).getCell(0).getNumericCellValue());
+ }
+ private String timeToUTC(Date d) {
+ SimpleDateFormat fmt = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss", Locale.UK);
+ fmt.setTimeZone(TimeZone.getTimeZone("UTC"));
+ return fmt.format(d);
+ }
}
diff --git a/src/testcases/org/apache/poi/ss/formula/functions/TestCalendarFieldFunction.java b/src/testcases/org/apache/poi/ss/formula/functions/TestCalendarFieldFunction.java
new file mode 100644
index 0000000000..ede20de393
--- /dev/null
+++ b/src/testcases/org/apache/poi/ss/formula/functions/TestCalendarFieldFunction.java
@@ -0,0 +1,85 @@
+/* ====================================================================
+ 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 junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+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.Cell;
+import org.apache.poi.ss.usermodel.CellValue;
+
+/**
+ * Test for YEAR / MONTH / DAY / HOUR / MINUTE / SECOND
+ */
+public final class TestCalendarFieldFunction extends TestCase {
+
+ private HSSFCell cell11;
+ private HSSFFormulaEvaluator evaluator;
+
+ public void setUp() {
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet sheet = wb.createSheet("new sheet");
+ cell11 = sheet.createRow(0).createCell(0);
+ cell11.setCellType(HSSFCell.CELL_TYPE_FORMULA);
+ evaluator = new HSSFFormulaEvaluator(wb);
+ }
+
+ public void testValid() {
+ confirm("YEAR(2.26)", 1900);
+ confirm("MONTH(2.26)", 1);
+ confirm("DAY(2.26)", 2);
+ confirm("HOUR(2.26)", 6);
+ confirm("MINUTE(2.26)", 14);
+ confirm("SECOND(2.26)", 24);
+
+ confirm("YEAR(40627.4860417)", 2011);
+ confirm("MONTH(40627.4860417)", 3);
+ confirm("DAY(40627.4860417)", 25);
+ confirm("HOUR(40627.4860417)", 11);
+ confirm("MINUTE(40627.4860417)", 39);
+ confirm("SECOND(40627.4860417)", 54);
+ }
+
+ public void testBugDate() {
+ confirm("YEAR(0.0)", 1900);
+ confirm("MONTH(0.0)", 1);
+ confirm("DAY(0.0)", 0);
+
+ confirm("YEAR(0.26)", 1900);
+ confirm("MONTH(0.26)", 1);
+ confirm("DAY(0.26)", 0);
+ confirm("HOUR(0.26)", 6);
+ confirm("MINUTE(0.26)", 14);
+ confirm("SECOND(0.26)", 24);
+ }
+
+ private void confirm(String formulaText, double expectedResult) {
+ cell11.setCellFormula(formulaText);
+ evaluator.clearAllCachedResultValues();
+ CellValue cv = evaluator.evaluate(cell11);
+ if (cv.getCellType() != Cell.CELL_TYPE_NUMERIC) {
+ throw new AssertionFailedError("Wrong result type: " + cv.formatAsString());
+ }
+ double actualValue = cv.getNumberValue();
+ assertEquals(expectedResult, actualValue, 0);
+ }
+}
diff --git a/test-data/spreadsheet/48968.xls b/test-data/spreadsheet/48968.xls
new file mode 100644
index 0000000000..bbafffe9c1
--- /dev/null
+++ b/test-data/spreadsheet/48968.xls
Binary files differ