diff -r ./../poi-4.1.0/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java ./src/java/org/apache/poi/ss/formula/eval/FunctionEval.java 183c247 < // 140: DATEVALUE --- > retval[140] = DateFunc.DATEVALUE;// 140: DATEVALUE diff -r ./../poi-4.1.0/src/java/org/apache/poi/ss/formula/functions/DateFunc.java ./src/java/org/apache/poi/ss/formula/functions/DateFunc.java 19a20 > import java.text.DateFormatSymbols; 20a22,23 > import java.util.GregorianCalendar; > import java.util.StringTokenizer; 24a28 > import org.apache.poi.ss.formula.eval.OperandResolver; 29d32 < 33,34c36 < public final class DateFunc extends Fixed3ArgFunction { < public static final Function instance = new DateFunc(); --- > public final class DateFunc { 36,108c38,170 < private DateFunc() { < // no fields to initialise < } < public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, < ValueEval arg2) { < double result; < try { < double d0 = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex); < double d1 = NumericFunction.singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex); < double d2 = NumericFunction.singleOperandEvaluate(arg2, srcRowIndex, srcColumnIndex); < result = evaluate(getYear(d0), (int) (d1 - 1), (int) d2); < NumericFunction.checkValue(result); < } catch (EvaluationException e) { < return e.getErrorEval(); < } < return new NumberEval(result); < } < < /** < * Note - works with Java Calendar months, not Excel months < */ < private static double evaluate(int year, int month, int pDay) throws EvaluationException { < // We don't support negative years yet < if (year < 0) { < throw new EvaluationException(ErrorEval.VALUE_INVALID); < } < // Negative months are fairly easy < while (month < 0) { < year--; < month += 12; < } < // Negative days are handled by the Java Calendar < < // Excel has bugs around leap years in 1900, handle them < // Special case for the non-existant 1900 leap year < if (year == 1900 && month == Calendar.FEBRUARY && pDay == 29) { < return 60.0; < } < < // If they give a date in 1900 in Jan/Feb, with the days < // putting it past the leap year, adjust < int day = pDay; < if (year == 1900) { < if ((month == Calendar.JANUARY && day >= 60) || < (month == Calendar.FEBRUARY && day >= 30)) { < day--; < } < } < < // Turn this into a Java date < Calendar c = LocaleUtil.getLocaleCalendar(year, month, day); < < // Handle negative days of the week, that pull us across < // the 29th of Feb 1900 < if (pDay < 0 && c.get(Calendar.YEAR) == 1900 && < month > Calendar.FEBRUARY && < c.get(Calendar.MONTH) < Calendar.MARCH) { < c.add(Calendar.DATE, 1); < } < < // TODO Identify if we're doing 1900 or 1904 date windowing < boolean use1904windowing = false; < < // Have this Java date turned back into an Excel one < return DateUtil.getExcelDate(c.getTime(), use1904windowing); < } < < private static int getYear(double d) { < int year = (int)d; < < if (year < 0) { < return -1; < } --- > public static final Function instance = new Fixed3ArgFunction() { > public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, ValueEval arg2) { > double result; > try { > double d0 = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex); > double d1 = NumericFunction.singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex); > double d2 = NumericFunction.singleOperandEvaluate(arg2, srcRowIndex, srcColumnIndex); > result = DateFunc.evaluate(getYear(d0), (int) (d1 - 1), (int) d2); > NumericFunction.checkValue(result); > } catch (EvaluationException e) { > return e.getErrorEval(); > } > return new NumberEval(result); > } > }; > > public static final Function DATEVALUE = new Fixed1ArgFunction() { > public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) { > try { > ValueEval ve = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex); > String arg = OperandResolver.coerceValueToString(ve); > > StringTokenizer t = new StringTokenizer(arg, "-/"); // Excel should work also with argument "20-08/2011" > String A = t.nextToken(); > String B = t.nextToken(); > String C = t.hasMoreTokens() ? t.nextToken() : null; > if (B.length() > 3) { > throw new EvaluationException(ErrorEval.VALUE_INVALID); > } > > int year; > int month; > int day; > > int a = Integer.valueOf(A); > if (B.chars().allMatch(Character::isDigit)) { // Possible to replace with org.apache.commons.lang.StringUtils.isNumeric() > int b = Integer.valueOf(B); > int c = Integer.valueOf(C); > if (a >= 1900) { // =DATEVALUE("2011/02/23") > year = a; > month = b; > day = c; > } else { // =DATEVALUE("8/22/2011") > month = a; > day = b; > year = c; > } > month--; > } else { > > String monthName = B.toLowerCase(); > String months[] = new DateFormatSymbols(LocaleUtil.getUserLocale()).getMonths(); > for (month = 0; month < 12; month++) { > System.out.println(months[month] + ";" + monthName); > if (months[month].toLowerCase().startsWith(monthName)) { > break; > } > } > if (month >= 12) { > throw new EvaluationException(ErrorEval.VALUE_INVALID); > } > > if (C == null) { > // =DATEVALUE("22-MAY") > GregorianCalendar gc = new GregorianCalendar(); > year = gc.get(Calendar.YEAR); > day = a; > } else { > // =DATEVALUE("22-MAY-2011") > year = Integer.valueOf(C); > day = a; > } > } > > return new NumberEval(DateFunc.evaluate(year, month, day)); > } catch (EvaluationException e) { > return e.getErrorEval(); > } > } > }; > > /** > * Note - works with Java Calendar months, not Excel months > */ > private static double evaluate(int year, int month, int pDay) throws EvaluationException { > // We don't support negative years yet > if (year < 0) { > throw new EvaluationException(ErrorEval.VALUE_INVALID); > } > // Negative months are fairly easy > while (month < 0) { > year--; > month += 12; > } > // Negative days are handled by the Java Calendar > > // Excel has bugs around leap years in 1900, handle them > // Special case for the non-existant 1900 leap year > if (year == 1900 && month == Calendar.FEBRUARY && pDay == 29) { > return 60.0; > } > > // If they give a date in 1900 in Jan/Feb, with the days > // putting it past the leap year, adjust > int day = pDay; > if (year == 1900) { > if ((month == Calendar.JANUARY && day >= 60) || (month == Calendar.FEBRUARY && day >= 30)) { > day--; > } > } > > // Turn this into a Java date > Calendar c = LocaleUtil.getLocaleCalendar(year, month, day); > > // Handle negative days of the week, that pull us across > // the 29th of Feb 1900 > if (pDay < 0 && c.get(Calendar.YEAR) == 1900 && month > Calendar.FEBRUARY && c.get(Calendar.MONTH) < Calendar.MARCH) { > c.add(Calendar.DATE, 1); > } > > // TODO Identify if we're doing 1900 or 1904 date windowing > boolean use1904windowing = false; > > // Have this Java date turned back into an Excel one > return DateUtil.getExcelDate(c.getTime(), use1904windowing); > } > > private static int getYear(double d) { > int year = (int) d; > > if (year < 0) { > return -1; > } 110,111c172,173 < return year < 1900 ? 1900 + year : year; < } --- > return year < 1900 ? 1900 + year : year; > } diff -r ./../poi-4.1.0/src/testcases/org/apache/poi/ss/formula/functions/TestDate.java ./src/testcases/org/apache/poi/ss/formula/functions/TestDate.java 19a20,23 > import java.util.Calendar; > import java.util.GregorianCalendar; > import java.util.Locale; > 25a30,31 > import org.apache.poi.ss.usermodel.DateUtil; > import org.apache.poi.util.LocaleUtil; 47,48c53 < * Test disabled pending a fix in the formula evaluator < * TODO - create MissingArgEval and modify the formula evaluator to handle this --- > * Test disabled pending a fix in the formula evaluator TODO - create MissingArgEval and modify the formula evaluator to handle this 79a85,102 > public void testDateValue() { > LocaleUtil.setUserLocale(Locale.ENGLISH); > > // // EXCEL > confirm("DATEVALUE(\"8/22/2011\")", 40777); // Serial number of a date entered as text. > confirm("DATEVALUE(\"22-MAY-2011\")", 40685); // Serial number of a date entered as text. > confirm("DATEVALUE(\"2011/02/23\")", 40597); // Serial number of a date entered as text. > > // Serial number of a date entered as text, using the 1900 date system, and assuming the computer's built-in clock returns 2011 as the current year. > GregorianCalendar gc = new GregorianCalendar(); > confirm("DATEVALUE(\"5-JUL\")", evaluate(gc.get(Calendar.YEAR), (7 - 1), 5)); > > // LibreOffice compatibility > confirm("DATEVALUE(\"1954-07-20\")", 19925); > > LocaleUtil.setUserLocale(null); > } > 88a112,157 > } > > /** > * Note - works with Java Calendar months, not Excel months > */ > private static double evaluate(int year, int month, int pDay) { > // We don't support negative years yet > if (year < 0) { > throw new RuntimeException("Argument year < 0"); > } > // Negative months are fairly easy > while (month < 0) { > year--; > month += 12; > } > // Negative days are handled by the Java Calendar > > // Excel has bugs around leap years in 1900, handle them > // Special case for the non-existant 1900 leap year > if (year == 1900 && month == Calendar.FEBRUARY && pDay == 29) { > return 60.0; > } > > // If they give a date in 1900 in Jan/Feb, with the days > // putting it past the leap year, adjust > int day = pDay; > if (year == 1900) { > if ((month == Calendar.JANUARY && day >= 60) || (month == Calendar.FEBRUARY && day >= 30)) { > day--; > } > } > > // Turn this into a Java date > Calendar c = LocaleUtil.getLocaleCalendar(year, month, day); > > // Handle negative days of the week, that pull us across > // the 29th of Feb 1900 > if (pDay < 0 && c.get(Calendar.YEAR) == 1900 && month > Calendar.FEBRUARY && c.get(Calendar.MONTH) < Calendar.MARCH) { > c.add(Calendar.DATE, 1); > } > > // TODO Identify if we're doing 1900 or 1904 date windowing > boolean use1904windowing = false; > > // Have this Java date turned back into an Excel one > return DateUtil.getExcelDate(c.getTime(), use1904windowing);