mirror of https://github.com/apache/poi.git
[github-189] Move date parsing logic to DateParser. Thanks to Miłosz Rembisz. This closes #189
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1880777 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
8329582036
commit
e86ba86f2d
|
@ -17,8 +17,8 @@
|
|||
|
||||
package org.apache.poi.ss.formula.atp;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.ss.formula.eval.AreaEvalBase;
|
||||
|
@ -27,6 +27,7 @@ import org.apache.poi.ss.formula.eval.OperandResolver;
|
|||
import org.apache.poi.ss.formula.eval.StringEval;
|
||||
import org.apache.poi.ss.formula.eval.ValueEval;
|
||||
import org.apache.poi.ss.usermodel.DateUtil;
|
||||
import org.apache.poi.ss.util.DateParser;
|
||||
|
||||
/**
|
||||
* Evaluator for formula arguments.
|
||||
|
@ -59,7 +60,7 @@ final class ArgumentsEvaluator {
|
|||
if (dVal != null) {
|
||||
return dVal.doubleValue();
|
||||
}
|
||||
Calendar date = DateParser.parseDate(strVal);
|
||||
LocalDate date = DateParser.parseLocalDate(strVal);
|
||||
return DateUtil.getExcelDate(date, false);
|
||||
}
|
||||
return OperandResolver.coerceValueToDouble(ve);
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
package org.apache.poi.ss.formula.atp;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.time.LocalDate;
|
||||
|
||||
import org.apache.poi.ss.formula.eval.ErrorEval;
|
||||
import org.apache.poi.ss.formula.eval.EvaluationException;
|
||||
|
@ -28,6 +28,8 @@ import org.apache.poi.ss.formula.eval.ValueEval;
|
|||
import org.apache.poi.ss.formula.functions.FreeRefFunction;
|
||||
import org.apache.poi.ss.formula.OperationEvaluationContext;
|
||||
import org.apache.poi.ss.usermodel.DateUtil;
|
||||
import org.apache.poi.ss.util.DateParser;
|
||||
|
||||
/**
|
||||
* Implementation of Excel 'Analysis ToolPak' function YEARFRAC()<br>
|
||||
*
|
||||
|
@ -90,7 +92,7 @@ final class YearFrac implements FreeRefFunction {
|
|||
if (dVal != null) {
|
||||
return dVal.doubleValue();
|
||||
}
|
||||
Calendar date = DateParser.parseDate(strVal);
|
||||
LocalDate date = DateParser.parseLocalDate(strVal);
|
||||
return DateUtil.getExcelDate(date, false);
|
||||
}
|
||||
return OperandResolver.coerceValueToDouble(ve);
|
||||
|
|
|
@ -17,23 +17,13 @@
|
|||
|
||||
package org.apache.poi.ss.formula.functions;
|
||||
|
||||
import java.text.DateFormatSymbols;
|
||||
import java.time.DateTimeException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.MatchResult;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.poi.ss.util.DateParser;
|
||||
import org.apache.poi.ss.formula.eval.BlankEval;
|
||||
import org.apache.poi.ss.formula.eval.ErrorEval;
|
||||
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.util.LocaleUtil;
|
||||
|
||||
/**
|
||||
* Implementation for the DATEVALUE() Excel function.<p>
|
||||
|
@ -56,32 +46,6 @@ import org.apache.poi.util.LocaleUtil;
|
|||
*/
|
||||
public class DateValue extends Fixed1ArgFunction {
|
||||
|
||||
private enum Format {
|
||||
YMD_DASHES("^(\\d{4})-(\\w+)-(\\d{1,2})$", "ymd"),
|
||||
DMY_DASHES("^(\\d{1,2})-(\\w+)-(\\d{4})$", "dmy"),
|
||||
MD_DASHES("^(\\w+)-(\\d{1,2})$", "md"),
|
||||
MDY_SLASHES("^(\\w+)/(\\d{1,2})/(\\d{4})$", "mdy"),
|
||||
YMD_SLASHES("^(\\d{4})/(\\w+)/(\\d{1,2})$", "ymd"),
|
||||
MD_SLASHES("^(\\w+)/(\\d{1,2})$", "md");
|
||||
|
||||
private Pattern pattern;
|
||||
private boolean hasYear;
|
||||
private int yearIndex;
|
||||
private int monthIndex;
|
||||
private int dayIndex;
|
||||
|
||||
Format(String patternString, String groupOrder) {
|
||||
this.pattern = Pattern.compile(patternString);
|
||||
this.hasYear = groupOrder.contains("y");
|
||||
if (hasYear) {
|
||||
yearIndex = groupOrder.indexOf("y");
|
||||
}
|
||||
monthIndex = groupOrder.indexOf("m");
|
||||
dayIndex = groupOrder.indexOf("d");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval dateTextArg) {
|
||||
try {
|
||||
|
@ -92,45 +56,9 @@ public class DateValue extends Fixed1ArgFunction {
|
|||
return BlankEval.instance;
|
||||
}
|
||||
|
||||
for (Format format : Format.values()) {
|
||||
Matcher matcher = format.pattern.matcher(dateText);
|
||||
if (matcher.find()) {
|
||||
MatchResult matchResult = matcher.toMatchResult();
|
||||
List<String> groups = new ArrayList<>();
|
||||
for (int i = 1; i <= matchResult.groupCount(); ++i) {
|
||||
groups.add(matchResult.group(i));
|
||||
}
|
||||
int year = format.hasYear
|
||||
? Integer.parseInt(groups.get(format.yearIndex))
|
||||
: LocalDate.now(LocaleUtil.getUserTimeZone().toZoneId()).getYear();
|
||||
int month = parseMonth(groups.get(format.monthIndex));
|
||||
int day = Integer.parseInt(groups.get(format.dayIndex));
|
||||
return new NumberEval(DateUtil.getExcelDate(LocalDate.of(year, month, day)));
|
||||
|
||||
}
|
||||
}
|
||||
} catch (DateTimeException e) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
return new NumberEval(DateUtil.getExcelDate(DateParser.parseLocalDate(dateText)));
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
private int parseMonth(String monthPart) {
|
||||
try {
|
||||
return Integer.parseInt(monthPart);
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
|
||||
|
||||
String[] months = DateFormatSymbols.getInstance(LocaleUtil.getUserLocale()).getMonths();
|
||||
for (int month = 0; month < months.length; ++month) {
|
||||
if (months[month].toLowerCase(LocaleUtil.getUserLocale()).startsWith(monthPart.toLowerCase(LocaleUtil.getUserLocale()))) {
|
||||
return month + 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
/* ====================================================================
|
||||
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.util;
|
||||
|
||||
import java.text.DateFormatSymbols;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.regex.MatchResult;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.poi.ss.formula.eval.ErrorEval;
|
||||
import org.apache.poi.ss.formula.eval.EvaluationException;
|
||||
import org.apache.poi.util.LocaleUtil;
|
||||
|
||||
/**
|
||||
* Parser for java dates.
|
||||
*/
|
||||
public class DateParser {
|
||||
private DateParser() {
|
||||
// enforcing singleton
|
||||
}
|
||||
|
||||
|
||||
private enum Format {
|
||||
YMD_DASHES("^(\\d{4})-(\\w+)-(\\d{1,2})( .*)?$", "ymd"),
|
||||
DMY_DASHES("^(\\d{1,2})-(\\w+)-(\\d{4})( .*)?$", "dmy"),
|
||||
MD_DASHES("^(\\w+)-(\\d{1,2})( .*)?$", "md"),
|
||||
MDY_SLASHES("^(\\w+)/(\\d{1,2})/(\\d{4})( .*)?$", "mdy"),
|
||||
YMD_SLASHES("^(\\d{4})/(\\w+)/(\\d{1,2})( .*)?$", "ymd"),
|
||||
MD_SLASHES("^(\\w+)/(\\d{1,2})( .*)?$", "md");
|
||||
|
||||
private Pattern pattern;
|
||||
private boolean hasYear;
|
||||
private int yearIndex;
|
||||
private int monthIndex;
|
||||
private int dayIndex;
|
||||
|
||||
Format(String patternString, String groupOrder) {
|
||||
this.pattern = Pattern.compile(patternString);
|
||||
this.hasYear = groupOrder.contains("y");
|
||||
if (hasYear) {
|
||||
yearIndex = groupOrder.indexOf("y");
|
||||
}
|
||||
monthIndex = groupOrder.indexOf("m");
|
||||
dayIndex = groupOrder.indexOf("d");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static int parseMonth(String monthPart) {
|
||||
try {
|
||||
return Integer.parseInt(monthPart);
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
|
||||
|
||||
String[] months = DateFormatSymbols.getInstance(LocaleUtil.getUserLocale()).getMonths();
|
||||
for (int month = 0; month < months.length; ++month) {
|
||||
if (months[month].toLowerCase(LocaleUtil.getUserLocale()).startsWith(monthPart.toLowerCase(LocaleUtil.getUserLocale()))) {
|
||||
return month + 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a date from a string.
|
||||
*
|
||||
* @param strVal a string with a date pattern.
|
||||
* @return a date parsed from argument.
|
||||
* @throws EvaluationException exception upon parsing.
|
||||
*/
|
||||
public static LocalDate parseLocalDate(String strVal) throws EvaluationException {
|
||||
for (Format format : Format.values()) {
|
||||
Matcher matcher = format.pattern.matcher(strVal);
|
||||
if (matcher.find()) {
|
||||
MatchResult matchResult = matcher.toMatchResult();
|
||||
List<String> groups = new ArrayList<>();
|
||||
for (int i = 1; i <= matchResult.groupCount(); ++i) {
|
||||
groups.add(matchResult.group(i));
|
||||
}
|
||||
int year = format.hasYear
|
||||
? Integer.parseInt(groups.get(format.yearIndex))
|
||||
: LocalDate.now(LocaleUtil.getUserTimeZone().toZoneId()).getYear();
|
||||
int month = parseMonth(groups.get(format.monthIndex));
|
||||
int day = Integer.parseInt(groups.get(format.dayIndex));
|
||||
return LocalDate.of(year, month, day);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
|
||||
public static Calendar parseDate(String strVal) throws EvaluationException {
|
||||
LocalDate date = parseLocalDate(strVal);
|
||||
return makeDate(date.getYear(), date.getMonthValue(), date.getDayOfMonth());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param month 1-based
|
||||
*/
|
||||
private static Calendar makeDate(int year, int month, int day) throws EvaluationException {
|
||||
if (month < 1 || month > 12) {
|
||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
Calendar cal = LocaleUtil.getLocaleCalendar(year, month - 1, 1, 0, 0, 0);
|
||||
if (day < 1 || day > cal.getActualMaximum(Calendar.DAY_OF_MONTH)) {
|
||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
cal.set(Calendar.DAY_OF_MONTH, day);
|
||||
return cal;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/* ====================================================================
|
||||
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.util;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
import org.apache.poi.ss.formula.eval.ErrorEval;
|
||||
import org.apache.poi.ss.formula.eval.EvaluationException;
|
||||
import org.apache.poi.util.LocaleUtil;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestDateParser {
|
||||
@Test
|
||||
public void testFailWhenNoDate() {
|
||||
try {
|
||||
DateParser.parseDate("potato");
|
||||
fail("Shouldn't parse potato!");
|
||||
} catch (EvaluationException e) {
|
||||
assertEquals(ErrorEval.VALUE_INVALID, e.getErrorEval());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailWhenLooksLikeDateButItIsnt() {
|
||||
try {
|
||||
DateParser.parseDate("potato/cucumber/banana");
|
||||
fail("Shouldn't parse this thing!");
|
||||
} catch (EvaluationException e) {
|
||||
assertEquals(ErrorEval.VALUE_INVALID, e.getErrorEval());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailWhenIsInvalidDate() {
|
||||
try {
|
||||
DateParser.parseDate("13/13/13");
|
||||
fail("Shouldn't parse this thing!");
|
||||
} catch (EvaluationException e) {
|
||||
assertEquals(ErrorEval.VALUE_INVALID, e.getErrorEval());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldParseValidDate() throws EvaluationException {
|
||||
Calendar expDate = LocaleUtil.getLocaleCalendar(1984, Calendar.OCTOBER, 20);
|
||||
Calendar actDate = DateParser.parseDate("1984/10/20");
|
||||
assertEquals("Had: " + expDate.getTime() + " and " + actDate.getTime() + "/" +
|
||||
expDate.getTimeInMillis() + "ms and " + actDate.getTimeInMillis() + "ms",
|
||||
expDate, actDate);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldIgnoreTimestamp() throws EvaluationException {
|
||||
Calendar expDate = LocaleUtil.getLocaleCalendar(1984, Calendar.OCTOBER, 20);
|
||||
Calendar actDate = DateParser.parseDate("1984/10/20 12:34:56");
|
||||
assertEquals("Had: " + expDate.getTime() + " and " + actDate.getTime() + "/" +
|
||||
expDate.getTimeInMillis() + "ms and " + actDate.getTimeInMillis() + "ms",
|
||||
expDate, actDate);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue