]> source.dussan.org Git - poi.git/commitdiff
[github-189] Move date parsing logic to DateParser. Thanks to MiƂosz Rembisz. This...
authorPJ Fanning <fanningpj@apache.org>
Tue, 11 Aug 2020 16:59:29 +0000 (16:59 +0000)
committerPJ Fanning <fanningpj@apache.org>
Tue, 11 Aug 2020 16:59:29 +0000 (16:59 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1880777 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/ss/formula/atp/ArgumentsEvaluator.java
src/java/org/apache/poi/ss/formula/atp/YearFrac.java
src/java/org/apache/poi/ss/formula/functions/DateValue.java
src/java/org/apache/poi/ss/util/DateParser.java [new file with mode: 0644]
src/testcases/org/apache/poi/ss/util/TestDateParser.java [new file with mode: 0644]

index f97728c34142e18c73dcb38bbfaa7258ead22a70..0c3f537dd64c40302861da7e62c513b68af55951 100644 (file)
@@ -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);
index bb2134a951ba71e246e25758df1ab9d2164e1082..a4f14b5436e584fb158095b21cbc493c74485740 100644 (file)
@@ -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);
index ea62ee156d5853a13509fcb16f04e47e2f73e7d7..fa241a7389335093d252941bfcabe1a0243a3ecf 100644 (file)
 
 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;
     }
 }
diff --git a/src/java/org/apache/poi/ss/util/DateParser.java b/src/java/org/apache/poi/ss/util/DateParser.java
new file mode 100644 (file)
index 0000000..2926300
--- /dev/null
@@ -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;
+    }
+
+}
diff --git a/src/testcases/org/apache/poi/ss/util/TestDateParser.java b/src/testcases/org/apache/poi/ss/util/TestDateParser.java
new file mode 100644 (file)
index 0000000..d20d234
--- /dev/null
@@ -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);
+    }
+
+}