mirror of https://github.com/apache/poi.git
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 git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1085591 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
22ad89d056
commit
8dc1647c4c
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
Binary file not shown.
Loading…
Reference in New Issue