You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ni...@apache.org on 2011/03/25 23:52:12 UTC

svn commit: r1085591 - in /poi/trunk: src/documentation/content/xdocs/ src/java/org/apache/poi/ss/formula/eval/ src/java/org/apache/poi/ss/formula/functions/ src/testcases/org/apache/poi/hssf/usermodel/ src/testcases/org/apache/poi/ss/formula/functions...

Author: nick
Date: Fri Mar 25 22:52:12 2011
New Revision: 1085591

URL: http://svn.apache.org/viewvc?rev=1085591&view=rev
Log:
Fix bug #48968 - Implement support for HOUR, MINUTE and SECOND formulas
Includes some re-working of the existing Calendar functions, unit tests for the old and new Calendar functions, and a wider date+formula+formatting test for this area

Added:
    poi/trunk/src/testcases/org/apache/poi/ss/formula/functions/TestCalendarFieldFunction.java
    poi/trunk/test-data/spreadsheet/48968.xls   (with props)
Modified:
    poi/trunk/src/documentation/content/xdocs/status.xml
    poi/trunk/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java
    poi/trunk/src/java/org/apache/poi/ss/formula/functions/CalendarFieldFunction.java
    poi/trunk/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java

Modified: poi/trunk/src/documentation/content/xdocs/status.xml
URL: http://svn.apache.org/viewvc/poi/trunk/src/documentation/content/xdocs/status.xml?rev=1085591&r1=1085590&r2=1085591&view=diff
==============================================================================
--- poi/trunk/src/documentation/content/xdocs/status.xml (original)
+++ poi/trunk/src/documentation/content/xdocs/status.xml Fri Mar 25 22:52:12 2011
@@ -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>

Modified: poi/trunk/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java?rev=1085591&r1=1085590&r2=1085591&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java (original)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java Fri Mar 25 22:52:12 2011
@@ -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();

Modified: poi/trunk/src/java/org/apache/poi/ss/formula/functions/CalendarFieldFunction.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/functions/CalendarFieldFunction.java?rev=1085591&r1=1085590&r2=1085591&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/functions/CalendarFieldFunction.java (original)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/functions/CalendarFieldFunction.java Fri Mar 25 22:52:12 2011
@@ -29,30 +29,29 @@ import org.apache.poi.ss.formula.eval.Va
 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
 		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;
 	}
 }

Modified: poi/trunk/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java?rev=1085591&r1=1085590&r2=1085591&view=diff
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java (original)
+++ poi/trunk/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java Fri Mar 25 22:52:12 2011
@@ -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);
+    }
 }

Added: poi/trunk/src/testcases/org/apache/poi/ss/formula/functions/TestCalendarFieldFunction.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/ss/formula/functions/TestCalendarFieldFunction.java?rev=1085591&view=auto
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/ss/formula/functions/TestCalendarFieldFunction.java (added)
+++ poi/trunk/src/testcases/org/apache/poi/ss/formula/functions/TestCalendarFieldFunction.java Fri Mar 25 22:52:12 2011
@@ -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);
+    }
+}

Added: poi/trunk/test-data/spreadsheet/48968.xls
URL: http://svn.apache.org/viewvc/poi/trunk/test-data/spreadsheet/48968.xls?rev=1085591&view=auto
==============================================================================
Binary file - no diff available.

Propchange: poi/trunk/test-data/spreadsheet/48968.xls
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org