joestelmach / natty

Java natural language date parser
http://natty.joestelmach.com/
MIT License
520 stars 183 forks source link

NPE / NumberFormatExceptions in WalkerState on Seasons and year seaks with miscalculated target year #162

Open hmalmstrom1 opened 8 years ago

hmalmstrom1 commented 8 years ago

example offending problems: List<DateGroup> summerGroups = parser.parse("6 Summer Beauty Savers"); results in:

java.lang.NullPointerException
        at java.util.Calendar.setTime(Calendar.java:1770)
        at com.joestelmach.natty.WalkerState.seekToIcsEvent(WalkerState.java:570)
        at com.joestelmach.natty.WalkerState.seekToSeason(WalkerState.java:413)
        at com.joestelmach.natty.generated.DateWalker.seek(DateWalker.java:1376)
        at com.joestelmach.natty.generated.DateWalker.relative_date(DateWalker.java:702)
        at com.joestelmach.natty.generated.DateWalker.date(DateWalker.java:651)
        at com.joestelmach.natty.generated.DateWalker.date_time(DateWalker.java:577)
        at com.joestelmach.natty.generated.DateWalker.date_time_alternative(DateWalker.java:524)
        at com.joestelmach.natty.generated.DateWalker.parse(DateWalker.java:410)
        at com.joestelmach.natty.Parser.singleParse(Parser.java:206)
        at com.joestelmach.natty.Parser.parse(Parser.java:99)

and

parser.parse("11.02 00:51 -");

results in:

java.lang.NumberFormatException: For input string: "00:51"
        at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
        at java.lang.Integer.parseInt(Integer.java:580)
        at java.lang.Integer.parseInt(Integer.java:615)
        at com.joestelmach.natty.WalkerState.seekToYear(WalkerState.java:143)
        at com.joestelmach.natty.generated.DateWalker.explicit_seek(DateWalker.java:1566)
        at com.joestelmach.natty.generated.DateWalker.relative_date(DateWalker.java:724)
        at com.joestelmach.natty.generated.DateWalker.date(DateWalker.java:651)
        at com.joestelmach.natty.generated.DateWalker.date_time(DateWalker.java:577)
        at com.joestelmach.natty.generated.DateWalker.date_time_alternative(DateWalker.java:524)
        at com.joestelmach.natty.generated.DateWalker.parse(DateWalker.java:410)
        at com.joestelmach.natty.Parser.singleParse(Parser.java:206)
        at com.joestelmach.natty.Parser.parse(Parser.java:87)
hmalmstrom1 commented 8 years ago

Here's a potential fix:

Index: src/main/java/com/joestelmach/natty/WalkerState.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/main/java/com/joestelmach/natty/WalkerState.java    (revision 0fe495d829bd33a553becced2ace51375a7589c4)
+++ src/main/java/com/joestelmach/natty/WalkerState.java    (revision d18a6bf3ad8404b0c5e0579ef9e8c7e6847389f3)
@@ -568,7 +568,11 @@
           (seekAmountInt - (hasPassed ? 1 : 0)) * -1);

     cal.setTimeZone(_calendar.getTimeZone());
+    if (dates.containsKey(targetYear)) {
-    cal.setTime(dates.get(targetYear));
+      cal.setTime(dates.get(targetYear));
+    } else {
+      cal.setTime(dates.get(currentYear));
+    }
     _calendar.set(Calendar.YEAR, cal.get(Calendar.YEAR));
     _calendar.set(Calendar.MONTH, cal.get(Calendar.MONTH));
     _calendar.set(Calendar.DAY_OF_MONTH, cal.get(Calendar.DAY_OF_MONTH));
\ No newline at end of file
Index: src/test/java/com/joestelmach/natty/ParserTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/test/java/com/joestelmach/natty/ParserTest.java (revision d18a6bf3ad8404b0c5e0579ef9e8c7e6847389f3)
+++ src/test/java/com/joestelmach/natty/ParserTest.java (revision d18a6bf3ad8404b0c5e0579ef9e8c7e6847389f3)
@@ -0,0 +1,60 @@
+package com.joestelmach.natty;
+
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.*;
+
+
+public class ParserTest extends AbstractTest {
+
+    private static final Long MILLIS_IN_DAY = 86400000L;
+
+    @BeforeClass
+    public static void oneTime() {
+        Locale.setDefault(Locale.US);
+        TimeZone.setDefault(TimeZone.getTimeZone("US/Eastern"));
+        initCalendarAndParser();
+    }
+
+    @Test
+    public void testParseNFEandNPEIssue() throws Exception {
+        Parser parser = new Parser();
+        int currYear = Calendar.getInstance().get(Calendar.YEAR);
+        List<DateGroup> summerGroups = parser.parse("6 Summer Beauty Savers");
+        Assert.assertTrue(
+                grouplistContainsDate(summerGroups, currYear, Calendar.JUNE, 20) ||
+                grouplistContainsDate(summerGroups, currYear, Calendar.JUNE, 21) ||
+                grouplistContainsDate(summerGroups, currYear, Calendar.JUNE, 22)
+        );
+        Assert.assertTrue(grouplistContainsDate(parser.parse("11-03 00:18 -"), currYear, Calendar.NOVEMBER, 3));
+        Assert.assertTrue(grouplistContainsDate(parser.parse("The winning teams will be invited by the Canadian Curling Association to represent Canada at the FISU 2015 Winter Universiade in Granada, Spain, 4-14."), currYear, Calendar.APRIL, 14));
+        List<DateGroup> fallGroup = parser.parse("Pamper With Perfume: 8 Fall Scents For Every Woman’s Every Mood");
+        Assert.assertTrue(
+                grouplistContainsDate(fallGroup, currYear, Calendar.SEPTEMBER, 22) ||
+                grouplistContainsDate(fallGroup, currYear, Calendar.SEPTEMBER, 23) ||
+                grouplistContainsDate(fallGroup, currYear, Calendar.SEPTEMBER, 24)
+        );
+
+    }
+
+    private static boolean grouplistContainsDate(List<DateGroup> groups, int year, int month, int day) {
+        GregorianCalendar calendar = new GregorianCalendar(year, month, day);
+        Long checkDate = calendar.getTime().getTime();
+        for (DateGroup group : groups) {
+            if (group == null) { continue; }
+            if (group.getDates() == null) { continue; }
+            if (group.getDates().size() == 0) { continue; }
+            List<Date> dates = group.getDates();
+            for (Date dt : dates) {
+                if (dt == null) { continue; }
+                Long millisDiff = Math.abs(dt.getTime() - checkDate);
+                if (millisDiff < MILLIS_IN_DAY) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+}
\ No newline at end of file