diff --git a/.gitignore b/.gitignore index eba5e86e..3e7a90fa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ .classpath .settings .project +.idea +.idea/misc.xml +workspace.xml bin target src/main/java/com/joestelmach/natty/generated/* diff --git a/.idea/libraries/Maven__antlr_antlr_2_7_7.xml b/.idea/libraries/Maven__antlr_antlr_2_7_7.xml new file mode 100644 index 00000000..b8d93d8e --- /dev/null +++ b/.idea/libraries/Maven__antlr_antlr_2_7_7.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__backport_util_concurrent_backport_util_concurrent_3_1.xml b/.idea/libraries/Maven__backport_util_concurrent_backport_util_concurrent_3_1.xml new file mode 100644 index 00000000..c5ed7930 --- /dev/null +++ b/.idea/libraries/Maven__backport_util_concurrent_backport_util_concurrent_3_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__commons_codec_commons_codec_1_5.xml b/.idea/libraries/Maven__commons_codec_commons_codec_1_5.xml new file mode 100644 index 00000000..d0e20adb --- /dev/null +++ b/.idea/libraries/Maven__commons_codec_commons_codec_1_5.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__commons_lang_commons_lang_2_6.xml b/.idea/libraries/Maven__commons_lang_commons_lang_2_6.xml new file mode 100644 index 00000000..2ec83767 --- /dev/null +++ b/.idea/libraries/Maven__commons_lang_commons_lang_2_6.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__commons_logging_commons_logging_1_1_1.xml b/.idea/libraries/Maven__commons_logging_commons_logging_1_1_1.xml new file mode 100644 index 00000000..b770f56a --- /dev/null +++ b/.idea/libraries/Maven__commons_logging_commons_logging_1_1_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__junit_junit_4_1.xml b/.idea/libraries/Maven__junit_junit_4_1.xml new file mode 100644 index 00000000..fd926eb3 --- /dev/null +++ b/.idea/libraries/Maven__junit_junit_4_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_antlr_antlr_3_2.xml b/.idea/libraries/Maven__org_antlr_antlr_3_2.xml new file mode 100644 index 00000000..7bafdd32 --- /dev/null +++ b/.idea/libraries/Maven__org_antlr_antlr_3_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_antlr_antlr_runtime_3_2.xml b/.idea/libraries/Maven__org_antlr_antlr_runtime_3_2.xml new file mode 100644 index 00000000..899879a7 --- /dev/null +++ b/.idea/libraries/Maven__org_antlr_antlr_runtime_3_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_antlr_gunit_3_2.xml b/.idea/libraries/Maven__org_antlr_gunit_3_2.xml new file mode 100644 index 00000000..7b126b33 --- /dev/null +++ b/.idea/libraries/Maven__org_antlr_gunit_3_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_antlr_stringtemplate_3_2.xml b/.idea/libraries/Maven__org_antlr_stringtemplate_3_2.xml new file mode 100644 index 00000000..d3e1ac9f --- /dev/null +++ b/.idea/libraries/Maven__org_antlr_stringtemplate_3_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_mnode_ical4j_ical4j_1_0_2.xml b/.idea/libraries/Maven__org_mnode_ical4j_ical4j_1_0_2.xml new file mode 100644 index 00000000..37391498 --- /dev/null +++ b/.idea/libraries/Maven__org_mnode_ical4j_ical4j_1_0_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..c857101c --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + 1.8 + + + + + + + + + diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml new file mode 100644 index 00000000..922003b8 --- /dev/null +++ b/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/README b/README index 570e8f73..69d4bdd0 100644 --- a/README +++ b/README @@ -1,35 +1,7 @@ -Natty is a natural language date parser written in Java. Given a date -expression, natty will apply standard language recognition and translation +Natty is a natural language date parser written in Java. Given a date +expression, natty will apply standard language recognition and translation techniques to produce a list of corresponding dates with optional parse and -syntax information. Visit http://natty.joestelmach.com for complete documentation. +syntax information. -========== USAGE ========== - -import com.joestelmach.natty.*; - -Parser parser = new Parser(); -ParseResult result = parser.parse("next monday"); -List dates = result.getDates(); - -========== CHANGELOG ========== - -* v.0.2.1 - - - implemented beginning/end syntax: - 'beginning of next month' - 'start of last week' - 'last day of 3 octobers from now' - - - implemented day of month: - 'the thirtieth day of 3 aprils ago' - - - bug fix: when seeking to a month whose maximum number of days is less than - the current day, the parser would roll into the next month. - -* v.0.2.0 - - - implemented explicit relative dates like 'wed of next week' - -* v.0.1.0 - - initial release +Complete documentation can be found here: +http://natty.joestelmach.com diff --git a/natty.iml b/natty.iml new file mode 100644 index 00000000..a02b06a8 --- /dev/null +++ b/natty.iml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index b995ea01..1d1fbd30 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ com.joestelmach natty jar - 0.2.2-SNAPSHOT + 0.9-SNAPSHOT Natty Date Parser natural language date parser http://natty.joestelmach.com @@ -70,9 +70,10 @@ maven-compiler-plugin + 2.3.2 - 1.5 - 1.5 + 1.6 + 1.6 @@ -93,46 +94,6 @@ - - - maven-antrun-plugin - - - createDebugGrammars - validate - - run - - - - - - - - - - - - - - deleteDebugGrammars - test - - run - - - - - - - - - - - - - - org.antlr @@ -140,37 +101,44 @@ 3.2 - - non-debug + debug antlr - false + true + src/main/antlr3/com/joestelmach/natty/generated/imports src/main/java + 10000 + + com/joestelmach/natty/generated/DateLexer.g + com/joestelmach/natty/generated/DateParser.g + - com/joestelmach/natty/generated/DebugDateParser.g - com/joestelmach/natty/generated/DebugDateWalker.g + com/joestelmach/natty/generated/imports/NumericRules.g - - + - debug + non-debug antlr - true + false src/main/java - - com/joestelmach/natty/generated/DebugDateParser.g - com/joestelmach/natty/generated/DebugDateWalker.g - + src/main/antlr3/com/joestelmach/natty/generated/imports + 10000 + + com/joestelmach/natty/generated/DateLexer.g + com/joestelmach/natty/generated/DateParser.g + com/joestelmach/natty/generated/imports/NumericRules.g + + @@ -209,6 +177,7 @@ org.apache.maven.plugins maven-gpg-plugin + 1.4 sign-artifacts @@ -224,6 +193,7 @@ org.apache.maven.plugins maven-javadoc-plugin + 2.9.1 attach-javadocs @@ -240,6 +210,7 @@ org.apache.maven.plugins maven-source-plugin + 2.2.1 attach-sources @@ -262,11 +233,11 @@ 3.2 - - antlr - antlr - 2.7.7 - + + org.mnode.ical4j + ical4j + 1.0.2 + junit diff --git a/src/main/antlr3/com/joestelmach/natty/generated/DateLexer.g b/src/main/antlr3/com/joestelmach/natty/generated/DateLexer.g index d892d9dc..a8e87bbc 100644 --- a/src/main/antlr3/com/joestelmach/natty/generated/DateLexer.g +++ b/src/main/antlr3/com/joestelmach/natty/generated/DateLexer.g @@ -2,51 +2,72 @@ lexer grammar DateLexer; @header { package com.joestelmach.natty.generated; } +@members { + private java.util.logging.Logger _logger = java.util.logging.Logger.getLogger("com.joestelmach.natty"); + + public void displayRecognitionError(String[] tokenNames, RecognitionException re) { + String message = getErrorHeader(re); + try { message += getErrorMessage(re, tokenNames); } catch(Exception e) {} + _logger.fine(message); + } +} + // ********** date rules ********** -JANUARY : 'january' 's'? | 'jan' DOT?; -FEBRUARY : 'february' 's'? | 'feb' DOT?; -MARCH : 'march' 'es'? | 'mar' DOT?; -APRIL : 'april' 's'? | 'apr' DOT?; -MAY : 'may' 's'?; -JUNE : 'june' 's'? | 'jun' DOT?; -JULY : 'july' 's'? | 'jul' DOT?; -AUGUST : 'august' 's'? | 'aug' DOT?; -SEPTEMBER : 'september' 's'? | 'sep' DOT? | 'sept' DOT?; -OCTOBER : 'october' 's'? | 'oct' DOT?; -NOVEMBER : 'november' 's'? | 'nov' DOT?; -DECEMBER : 'december' 's'? | 'dec' DOT?; +JANUARY : 'january' 's'? | 'jan' DOT?; +FEBRUARY : 'february' 's'? | 'feb' DOT?; +MARCH : 'march' 'es'? | 'mar' DOT?; +APRIL : 'april' 's'? | 'apr' DOT?; +MAY : 'may' 's'?; +JUNE : 'june' 's'? | 'jun' DOT?; +JULY : 'july' 's'? | 'jul' DOT?; +AUGUST : 'august' 's'? | 'aug' DOT?; +SEPTEMBER : 'september' 's'? | 'sep' DOT? | 'sept' DOT?; +OCTOBER : 'october' 's'? | 'oct' DOT?; +NOVEMBER : 'november' 's'? | 'nov' DOT?; +DECEMBER : 'december' 's'? | 'dec' DOT?; -SUNDAY : 'sunday' 's'? | 'sun' DOT? | 'suns' DOT?; -MONDAY : 'monday' 's'? | 'mon' DOT? | 'mons' DOT?; -TUESDAY : 'tuesday' 's'? | 'tues' DOT? | 'tue' DOT?; -WEDNESDAY : 'wednesday' 's'? | 'wed' DOT? | 'weds' DOT?; -THURSDAY : 'thursday' 's'? | 'thur' DOT? | 'thu' DOT? | 'thus' DOT? | 'thurs' DOT?; -FRIDAY : 'friday' 's'? | 'fri' DOT? | 'fris' DOT?; -SATURDAY : 'saturday' 's'? | 'sat' DOT? | 'sats' DOT? | 'weekend'; +SUNDAY : 'sunday' 's'? | 'sun' DOT? | 'suns' DOT?; +MONDAY : 'monday' 's'? | 'mon' DOT? | 'mons' DOT?; +TUESDAY : 'tuesday' 's'? | 'tues' DOT? | 'tue' DOT?; +WEDNESDAY : 'wednesday' 's'? | 'wed' DOT? | 'weds' DOT?; +THURSDAY : 'thursday' 's'? | 'thur' DOT? | 'thu' DOT? | 'thus' DOT? | 'thurs' DOT?; +FRIDAY : 'friday' 's'? | 'fri' DOT? | 'fris' DOT?; +SATURDAY : 'saturday' 's'? | 'sat' DOT? | 'sats' DOT? | 'weekend'; -HOUR : 'hour' | 'hours' ; -DAY : 'day' | 'days' ; -WEEK : 'week' | 'weeks' ; -MONTH : 'month' | 'months' ; -YEAR : 'year' | 'years' ; +HOUR : 'hour' | 'hours' | 'hr' DOT? | 'hrs' DOT?; +MINUTE : 'minute' | 'minutes' | 'min' DOT? | 'mins' DOT?; +DAY : 'day' | 'days' ; +WEEK : 'week' | 'weeks' | 'wks' DOT?; +MONTH : 'month' | 'months'; +YEAR : 'year' | 'year' SINGLE_QUOTE? 's' | 'yrs' DOT?; TODAY : 'today'; TOMORROW : 'tomorow' | 'tomorrow' | 'tommorow' | 'tommorrow'; +TONIGHT : 'tonight'; YESTERDAY : 'yesterday'; +// ********** recurrence rules ********** + +EVERY : 'every'; +UNTIL : 'until'; + // ********** time rules ********** - -AM : 'am' | 'a.m.' | 'a'; -PM : 'pm' | 'p.m.' | 'p'; + +AT : 'at' | '@'; +AFTER : 'after'; +PAST : 'past'; +AM : 'am' | 'a.m' DOT? | 'a'; +PM : 'pm' | 'p.m' DOT? | 'p'; T : 't'; MILITARY_HOUR_SUFFIX : 'h'; -MIDNIGHT : 'midnight' | 'mid-night'; -NOON : 'noon' | 'afternoon' | 'after-noon'; -MORNING : 'morning'; -NIGHT : 'night'; +MIDNIGHT : 'midnight' | 'mid-night'; +NOON : 'noon' | 'afternoon' | 'after-noon'; +MORNING : 'morning'; +EVENING : 'evening' | 'eve'; +NIGHT : 'night'; UTC : 'utc' | 'gmt' | 'z'; EST : 'est' | 'edt' | 'et'; @@ -192,7 +213,7 @@ TWENTY : 'twenty'; THIRTY : 'thirty'; FIRST : 'first'; -SECOND : 'second'; +SECOND : 'second' | 'seconds' | 'sec' | 'secs'; THIRD : 'third'; FOURTH : 'fourth'; FIFTH : 'fifth'; @@ -229,24 +250,26 @@ DOT : '.'; PLUS : '+'; SINGLE_QUOTE : '\''; +FOR : 'for'; IN : 'in'; +AN : 'an'; THE : 'the'; OR : 'or'; -AT : 'at'; +AND : 'and'; +TO : 'to'; +THROUGH : 'through'; ON : 'on'; OF : 'of'; THIS : 'this'; THAT : 'that'; -LAST : 'last'; +LAST : 'last' | 'final'; NEXT : 'next'; -PAST : 'past'; COMING : 'coming'; UPCOMING : 'upcoming'; FROM : 'from'; NOW : 'now'; AGO : 'ago'; BEFORE : 'before'; -AFTER : 'after'; BEGINNING : 'beginning' | 'begining'; START : 'start'; END : 'end'; @@ -255,6 +278,56 @@ WHITE_SPACE : (DOT | SPACE)+ ; +// ********** holiday specific ********** + +FOOL : 'fool' | 'fools' | 'fool' SINGLE_QUOTE 's'; +BLACK : 'black'; +CHRISTMAS : ('christmas' | 'xmas' | 'x-mas') 'es'?; +COLUMBUS : 'columbus'; +EARTH : 'earth'; +EASTER : 'easter'; +FATHER : 'father' | 'fathers' | 'father' SINGLE_QUOTE 's'; +FLAG : 'flag'; +GOOD : 'good'; +GROUNDHOG : GROUND WHITE_SPACE? HOG SINGLE_QUOTE? 's'?; +HALLOWEEN : ('halloween' | 'haloween') 's'?; +INAUGURATION : 'inauguration' | 'inaugaration'; +INDEPENDENCE : 'independence' | 'independance'; +KWANZAA : ('kwanza' 'a'?) 's'?; +LABOR : 'labor'; +MLK : 'mlk' | 'martin' WHITE_SPACE 'luther' WHITE_SPACE 'king' SINGLE_QUOTE? 's'? ('jr' DOT? SINGLE_QUOTE? 's'?)?; +MEMORIAL : 'memorial'; +MOTHER : 'mother' SINGLE_QUOTE? 's'?; +NEW : 'new'; +PALM : 'palm'; +PATRIOT : 'patriot' SINGLE_QUOTE? 's'?; +PRESIDENT : 'president' SINGLE_QUOTE? 's'?; +PATRICK : ('patrick' | 'patty' | 'paddy') SINGLE_QUOTE? 's'?; +SAINT : 'saint'; +TAX : 'tax'; +THANKSGIVING : 'thanksgiving' 's'?; +ELECTION : 'election'; +VALENTINE : 'valentine' SINGLE_QUOTE? 's'?; +VETERAN : 'veteran' SINGLE_QUOTE? 's'?; +fragment GROUND : 'ground'; +fragment HOG : 'hog'; + +// ********** season specific ********** + +WINTER : 'winter' 's'?; +FALL : 'fall' 's'?; +AUTUMN : 'autumn' 's'?; +SPRING : 'spring' 's'?; +SUMMER : 'summer' 's'?; + +UNKNOWN + : UNKNOWN_CHAR + ; + +fragment UNKNOWN_CHAR + : ~(SPACE | DOT) + ; + fragment DIGIT : '0'..'9'; -fragment SPACE : ' ' | '\t' | '\n' | '\r' ; \ No newline at end of file +fragment SPACE : ' ' | '\t' | '\n' | '\r' | '\u00A0'; \ No newline at end of file diff --git a/src/main/antlr3/com/joestelmach/natty/generated/DateParser.g b/src/main/antlr3/com/joestelmach/natty/generated/DateParser.g index c17066ad..83b38eaf 100644 --- a/src/main/antlr3/com/joestelmach/natty/generated/DateParser.g +++ b/src/main/antlr3/com/joestelmach/natty/generated/DateParser.g @@ -1,15 +1,18 @@ parser grammar DateParser; options { - output=AST; tokenVocab=DateLexer; + output=AST; } +import NumericRules; + tokens { INT; MONTH_OF_YEAR; DAY_OF_MONTH; DAY_OF_WEEK; + DAY_OF_YEAR; YEAR_OF; DATE_TIME; DATE_TIME_ALTERNATIVE; @@ -21,32 +24,51 @@ tokens { EXPLICIT_SEEK; SPAN; EXPLICIT_TIME; + RELATIVE_TIME; HOURS_OF_DAY; MINUTES_OF_HOUR; SECONDS_OF_MINUTE; AM_PM; ZONE; ZONE_OFFSET; + RECURRENCE; + HOLIDAY; + SEASON; } @header { package com.joestelmach.natty.generated; } +@members { + private java.util.logging.Logger _logger = java.util.logging.Logger.getLogger("com.joestelmach.natty"); + + public void displayRecognitionError(String[] tokenNames, RecognitionException re) { + String message = getErrorHeader(re); + try { message += getErrorMessage(re, tokenNames); } catch(Exception e) {} + _logger.fine(message); + } +} + parse - : (date_time_alternative)=> date_time_alternative - | date_time -> ^(DATE_TIME_ALTERNATIVE date_time) + : empty ((recurrence)=>recurrence | date_time_alternative) ; - + +recurrence + : EVERY WHITE_SPACE date_time_alternative (WHITE_SPACE UNTIL WHITE_SPACE date_time)? + -> date_time_alternative ^(RECURRENCE date_time?) + ; + +empty + : + ; + date_time : ( - (date (date_time_separator time)?)=> - date (date_time_separator time)? - - | (date) => date - - | time (time_date_separator date)? - ) -> ^(DATE_TIME date? time?) + (date)=>date (date_time_separator explicit_time)? + | explicit_time (time_date_separator date)? + ) -> ^(DATE_TIME date? explicit_time?) + | relative_time -> ^(DATE_TIME relative_time?) ; date_time_separator @@ -61,63 +83,123 @@ time_date_separator ; date - : (formal_date)=> formal_date - | (relaxed_date)=> relaxed_date - | explicit_relative_date + : formal_date + | relaxed_date | relative_date + | explicit_relative_date | global_date_prefix WHITE_SPACE date -> ^(RELATIVE_DATE ^(SEEK global_date_prefix date)) ; date_time_alternative - // "next wed or thurs" , "next wed, thurs, or fri" - : (alternative_day_of_week_list)=> alternative_day_of_week_list + + // in 2 to 3 months, 4 and 7 months + : (((IN | FOR | NEXT) WHITE_SPACE)? spelled_or_int_optional_prefix conjunction)=> + ((IN | FOR | NEXT) WHITE_SPACE)? one=spelled_or_int_optional_prefix conjunction two=spelled_or_int_optional_prefix WHITE_SPACE relative_date_span + -> ^(DATE_TIME_ALTERNATIVE + ^(DATE_TIME ^(RELATIVE_DATE ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] $one relative_date_span))) + ^(DATE_TIME ^(RELATIVE_DATE ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] $two relative_date_span)))) + + // today or the day after that, feb 16th or 2 days after that, january fourth or the friday after + | (date conjunction global_date_prefix)=> + date conjunction global_date_prefix (WHITE_SPACE THAT)? (date_time_separator explicit_time)? + -> ^(DATE_TIME_ALTERNATIVE ^(DATE_TIME date explicit_time?) ^(DATE_TIME ^(RELATIVE_DATE ^(SEEK global_date_prefix date) explicit_time?))) + + // "next wed or thurs" , "next wed, thurs, or fri" + | (alternative_day_of_week_list)=> alternative_day_of_week_list -> ^(DATE_TIME_ALTERNATIVE alternative_day_of_week_list) - // month day or day time + // feb 16, 17, or 18 | (alternative_day_of_month_list)=> alternative_day_of_month_list -> ^(DATE_TIME_ALTERNATIVE alternative_day_of_month_list) - - // date and time OR date and time - | (date (WHITE_SPACE OR WHITE_SPACE date (date_time_separator time)?)+) => - date (WHITE_SPACE OR WHITE_SPACE date (date_time_separator time)?)+ - -> ^(DATE_TIME_ALTERNATIVE ^(DATE_TIME date time?)+) - - // date OR date time - | (date (WHITE_SPACE OR WHITE_SPACE date)+ (date_time_separator time)?) => - date (WHITE_SPACE OR WHITE_SPACE date)+ (date_time_separator time)? - -> ^(DATE_TIME_ALTERNATIVE ^(DATE_TIME date time?)+) - + // this wed. or next - | ((THIS WHITE_SPACE)? day_of_week WHITE_SPACE OR WHITE_SPACE alternative_direction)=> - (THIS WHITE_SPACE)? day_of_week WHITE_SPACE OR WHITE_SPACE alternative_direction (date_time_separator time)? + | ((THIS WHITE_SPACE)? day_of_week conjunction alternative_direction)=> + (THIS WHITE_SPACE)? day_of_week conjunction alternative_direction (date_time_separator explicit_time)? -> ^(DATE_TIME_ALTERNATIVE - ^(DATE_TIME ^(RELATIVE_DATE ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["0"] day_of_week)) time?) - ^(DATE_TIME ^(RELATIVE_DATE ^(SEEK alternative_direction day_of_week)) time?) + ^(DATE_TIME ^(RELATIVE_DATE ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["0"] day_of_week)) explicit_time?) + ^(DATE_TIME ^(RELATIVE_DATE ^(SEEK alternative_direction day_of_week)) explicit_time?) ) - - // today or the day after that, feb 16th or 2 days after that, january fourth or the friday after - | date WHITE_SPACE OR WHITE_SPACE global_date_prefix (WHITE_SPACE THAT)? (date_time_separator time)? - -> ^(DATE_TIME_ALTERNATIVE ^(DATE_TIME date time?) ^(DATE_TIME ^(RELATIVE_DATE ^(SEEK global_date_prefix date) time?))) + + // 1/2 or 1/4 or 1/6 at 6pm + // Aug 16 at 10am or Sept 28th at 11am + | (date_time conjunction date_time)=> + date_time (conjunction date_time)+ + -> ^(DATE_TIME_ALTERNATIVE date_time+) + + // first or last day of 2009 + | (explicit_day_of_year_part conjunction explicit_day_of_year_part WHITE_SPACE relaxed_year)=> + first=explicit_day_of_year_part conjunction second=explicit_day_of_year_part WHITE_SPACE relaxed_year + -> ^(DATE_TIME_ALTERNATIVE + ^(DATE_TIME ^(RELATIVE_DATE ^(EXPLICIT_SEEK relaxed_year) $first)) + ^(DATE_TIME ^(RELATIVE_DATE ^(EXPLICIT_SEEK relaxed_year) $second))) + + // for 3 days, for 7 months, for twenty seconds + | ((FOR | NEXT) WHITE_SPACE spelled_or_int_optional_prefix WHITE_SPACE)=> + (FOR | NEXT) WHITE_SPACE spelled_or_int_optional_prefix WHITE_SPACE + (relative_date_span -> + ^(DATE_TIME_ALTERNATIVE + ^(DATE_TIME ^(RELATIVE_DATE ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["0"] SPAN["day"]))) + ^(DATE_TIME ^(RELATIVE_DATE ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] spelled_or_int_optional_prefix relative_date_span)))) + | relative_time_span -> + ^(DATE_TIME_ALTERNATIVE + ^(DATE_TIME ^(RELATIVE_TIME ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["0"] SPAN["day"]))) + ^(DATE_TIME ^(RELATIVE_TIME ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] spelled_or_int_optional_prefix relative_time_span)))) + ) + + // last 3 days, last 7 months, past twenty seconds + | ((LAST | PAST) WHITE_SPACE spelled_or_int_optional_prefix WHITE_SPACE)=> + (LAST | PAST) WHITE_SPACE spelled_or_int_optional_prefix WHITE_SPACE + (relative_date_span -> + ^(DATE_TIME_ALTERNATIVE + ^(DATE_TIME ^(RELATIVE_DATE ^(SEEK DIRECTION["<"] SEEK_BY["by_day"] INT["0"] SPAN["day"]))) + ^(DATE_TIME ^(RELATIVE_DATE ^(SEEK DIRECTION["<"] SEEK_BY["by_day"] spelled_or_int_optional_prefix relative_date_span)))) + | relative_time_span -> + ^(DATE_TIME_ALTERNATIVE + ^(DATE_TIME ^(RELATIVE_TIME ^(SEEK DIRECTION["<"] SEEK_BY["by_day"] INT["0"] SPAN["day"]))) + ^(DATE_TIME ^(RELATIVE_TIME ^(SEEK DIRECTION["<"] SEEK_BY["by_day"] spelled_or_int_optional_prefix relative_time_span)))) + ) + + // single date_time + | date_time -> ^(DATE_TIME_ALTERNATIVE date_time) + ; + +conjunction + : COMMA? WHITE_SPACE (AND | OR | TO | THROUGH | DASH) WHITE_SPACE ; alternative_day_of_month_list - : ((relaxed_day_of_week? relaxed_month WHITE_SPACE relaxed_day_of_month (WHITE_SPACE OR WHITE_SPACE relaxed_day_of_month)+) (date_time_separator time)?) - -> ^(DATE_TIME ^(EXPLICIT_DATE relaxed_month relaxed_day_of_month) time?)+ + // mon may 15 or tues may 16 + : ((relaxed_day_of_week? relaxed_month WHITE_SPACE relaxed_day_of_month (conjunction relaxed_day_of_month)+) (date_time_separator explicit_time)?) + -> ^(DATE_TIME ^(EXPLICIT_DATE relaxed_month relaxed_day_of_month) explicit_time?)+ + + // first or last day of september + | (explicit_day_of_month_part conjunction explicit_day_of_month_part WHITE_SPACE relaxed_month)=> + first=explicit_day_of_month_part conjunction second=explicit_day_of_month_part WHITE_SPACE relaxed_month (date_time_separator explicit_time)? + -> ^(DATE_TIME ^(RELATIVE_DATE ^(EXPLICIT_SEEK relaxed_month) $first) explicit_time?) + ^(DATE_TIME ^(RELATIVE_DATE ^(EXPLICIT_SEEK relaxed_month) $second) explicit_time?) + + // first or last day of next september + | (explicit_day_of_month_part conjunction explicit_day_of_month_part WHITE_SPACE prefix WHITE_SPACE explicit_relative_month)=> + first=explicit_day_of_month_part conjunction second=explicit_day_of_month_part WHITE_SPACE prefix WHITE_SPACE explicit_relative_month (date_time_separator explicit_time)? + -> ^(DATE_TIME ^(RELATIVE_DATE ^(SEEK prefix explicit_relative_month) $first) explicit_time?) + ^(DATE_TIME ^(RELATIVE_DATE ^(SEEK prefix explicit_relative_month) $second) explicit_time?) + + // first or last day of 2 septembers from now + | (explicit_day_of_month_part conjunction explicit_day_of_month_part WHITE_SPACE spelled_or_int_optional_prefix WHITE_SPACE explicit_relative_month WHITE_SPACE relative_date_suffix)=> + first=explicit_day_of_month_part conjunction second=explicit_day_of_month_part WHITE_SPACE + spelled_or_int_optional_prefix WHITE_SPACE explicit_relative_month WHITE_SPACE relative_date_suffix (date_time_separator explicit_time)? + -> ^(DATE_TIME ^(RELATIVE_DATE ^(SEEK relative_date_suffix spelled_or_int_optional_prefix explicit_relative_month) $first) explicit_time?) + ^(DATE_TIME ^(RELATIVE_DATE ^(SEEK relative_date_suffix spelled_or_int_optional_prefix explicit_relative_month) $second) explicit_time?) ; alternative_day_of_week_list - : alternative_direction WHITE_SPACE day_of_week (day_of_week_list_separator day_of_week)+ (date_time_separator time)? - -> ^(DATE_TIME ^(RELATIVE_DATE ^(SEEK alternative_direction day_of_week)) time?)+ + : alternative_direction WHITE_SPACE day_of_week (day_of_week_list_separator day_of_week)+ (date_time_separator explicit_time)? + -> ^(DATE_TIME ^(RELATIVE_DATE ^(SEEK alternative_direction day_of_week)) explicit_time?)+ ; day_of_week_list_separator - : COMMA (WHITE_SPACE | WHITE_SPACE OR WHITE_SPACE) - | WHITE_SPACE OR WHITE_SPACE - ; - -date_list_separator - : (COMMA WHITE_SPACE?) | (WHITE_SPACE OR WHITE_SPACE) + : COMMA (WHITE_SPACE | conjunction) | conjunction ; alternative_direction @@ -139,7 +221,10 @@ global_date_prefix // 2 weeks before | spelled_or_int_optional_prefix WHITE_SPACE WEEK WHITE_SPACE prefix_direction -> prefix_direction SEEK_BY["by_week"] spelled_or_int_optional_prefix - + + | WEEK WHITE_SPACE prefix_direction + -> prefix_direction SEEK_BY["by_week"] INT["1"] + // 6 months before | spelled_or_int_optional_prefix WHITE_SPACE MONTH WHITE_SPACE prefix_direction -> prefix_direction SEEK_BY["by_month"] spelled_or_int_optional_prefix @@ -161,7 +246,7 @@ global_date_prefix ; prefix_direction - : AFTER -> DIRECTION[">"] + : (AFTER | FROM | ON) -> DIRECTION[">"] | BEFORE -> DIRECTION["<"] ; @@ -170,32 +255,18 @@ prefix_direction // relaxed date with a spelled-out or abbreviated month relaxed_date : ( - // this is a bit tricky since a time can be placed directly after a date, and a year - // can look like a time (four digits, no colon i.e. 0500) Since a year would be more - // common in this context, we choose to swallow the year as part of the date. - // The 31st of April in the year 2008 // RFC822 style: Fri, 21 Nov 1997 - (relaxed_day_of_week? relaxed_day_of_month_prefix? relaxed_day_of_month - WHITE_SPACE (OF WHITE_SPACE)? relaxed_month relaxed_year_prefix relaxed_year)=> - relaxed_day_of_week? relaxed_day_of_month_prefix? relaxed_day_of_month - WHITE_SPACE (OF WHITE_SPACE)? relaxed_month relaxed_year_prefix relaxed_year - - // above without the year restriction - | relaxed_day_of_week? relaxed_day_of_month_prefix? relaxed_day_of_month WHITE_SPACE (OF WHITE_SPACE)? relaxed_month - - // Jan 21, 1997 - // Sun, Nov 21 - | (relaxed_day_of_week? relaxed_month WHITE_SPACE relaxed_day_of_month relaxed_year_prefix relaxed_year)=> - relaxed_day_of_week? relaxed_month WHITE_SPACE relaxed_day_of_month relaxed_year_prefix relaxed_year - - // jan 1st, february 28th - | relaxed_day_of_week? relaxed_month WHITE_SPACE relaxed_day_of_month + relaxed_day_of_week? relaxed_day_of_month_prefix? relaxed_day_of_month + WHITE_SPACE (OF WHITE_SPACE)? relaxed_month (relaxed_year_prefix relaxed_year)? + + // Jan 21, 1997 Sun, Nov 21 + | relaxed_day_of_week? relaxed_month COMMA? WHITE_SPACE relaxed_day_of_month (relaxed_year_prefix relaxed_year)? ) -> ^(EXPLICIT_DATE relaxed_month relaxed_day_of_month relaxed_day_of_week? relaxed_year?) ; - + relaxed_day_of_week - : (prefix WHITE_SPACE)? day_of_week ((COMMA WHITE_SPACE?) | WHITE_SPACE) -> day_of_week + : (prefix WHITE_SPACE)? day_of_week COMMA? WHITE_SPACE? -> day_of_week ; relaxed_day_of_month_prefix @@ -225,8 +296,17 @@ relaxed_day_of_month -> ^(DAY_OF_MONTH spelled_first_to_thirty_first) ; +// TODO expand these to 366 +relaxed_day_of_year + : spelled_or_int_01_to_31_optional_prefix + -> ^(DAY_OF_YEAR spelled_or_int_01_to_31_optional_prefix) + + | spelled_first_to_thirty_first + -> ^(DAY_OF_YEAR spelled_first_to_thirty_first) + ; + relaxed_year - : SINGLE_QUOTE? int_00_to_99_mandatory_prefix + : SINGLE_QUOTE int_00_to_99_mandatory_prefix -> ^(YEAR_OF int_00_to_99_mandatory_prefix) | int_four_digits @@ -241,12 +321,21 @@ relaxed_year_prefix formal_date // year first: 1979-02-28, 1980/01/02, etc. full 4 digit year required to match - : relaxed_day_of_week? formal_year_four_digits formal_date_separator formal_month_of_year formal_date_separator formal_day_of_month - -> ^(EXPLICIT_DATE formal_month_of_year formal_day_of_month relaxed_day_of_week? formal_year_four_digits) + : relaxed_day_of_week? formal_year_four_digits formal_date_separator (formal_month_of_year | relaxed_month) formal_date_separator formal_day_of_month + -> ^(EXPLICIT_DATE formal_month_of_year? relaxed_month? formal_day_of_month relaxed_day_of_week? formal_year_four_digits) // year last: 1/02/1980, 2/28/79. 2 or 4 digit year is acceptable | relaxed_day_of_week? formal_month_of_year formal_date_separator formal_day_of_month (formal_date_separator formal_year)? -> ^(EXPLICIT_DATE formal_month_of_year formal_day_of_month relaxed_day_of_week? formal_year?) + + // 15-Apr-2014 + | formal_day_of_month formal_date_separator relaxed_month (formal_date_separator formal_year_four_digits)? + -> ^(EXPLICIT_DATE relaxed_month formal_day_of_month formal_year_four_digits?) + + | relaxed_month WHITE_SPACE relaxed_year + -> ^(EXPLICIT_DATE relaxed_month ^(DAY_OF_MONTH INT["1"]) relaxed_year?) + | formal_year_four_digits + -> ^(EXPLICIT_DATE formal_year_four_digits) ; formal_month_of_year @@ -275,8 +364,8 @@ formal_date_separator relative_date // next wed, last month - : relative_prefix WHITE_SPACE relative_target - -> ^(RELATIVE_DATE ^(SEEK relative_prefix relative_target)) + : relative_date_prefix WHITE_SPACE relative_target + -> ^(RELATIVE_DATE ^(SEEK relative_date_prefix relative_target)) // this month, this week | implicit_prefix WHITE_SPACE relative_target @@ -287,16 +376,32 @@ relative_date // relative target with no prefix has an implicit seek of 0 -> ^(RELATIVE_DATE ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["0"] day_of_week)) + // january, february + | relaxed_month + -> ^(RELATIVE_DATE ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["0"] relaxed_month)) + // one month from now - | spelled_or_int_optional_prefix WHITE_SPACE relative_target WHITE_SPACE relative_suffix - -> ^(RELATIVE_DATE ^(SEEK relative_suffix spelled_or_int_optional_prefix relative_target)) - + | spelled_or_int_optional_prefix WHITE_SPACE relative_target WHITE_SPACE relative_date_suffix + -> ^(RELATIVE_DATE ^(SEEK relative_date_suffix spelled_or_int_optional_prefix relative_target)) + + // a month from now + | relative_target WHITE_SPACE relative_date_suffix + -> ^(RELATIVE_DATE ^(SEEK relative_date_suffix INT["1"] relative_target)) + // the week after next | (THE WHITE_SPACE)? relative_date_span WHITE_SPACE AFTER WHITE_SPACE NEXT -> ^(RELATIVE_DATE ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["2"] relative_date_span)) // today, tomorrow | named_relative_date + + // next christmas, 2 thanksgivings ago + | holiday + -> ^(RELATIVE_DATE holiday) + + // next fall, 2 summers from now + | season + -> ^(RELATIVE_DATE season) ; // ********** explicit relative date rules ********** @@ -304,14 +409,14 @@ relative_date explicit_relative_date // 1st of three months ago, 10th of 3 octobers from now, the last monday in 2 novembers ago - : explicit_day_of_month_part WHITE_SPACE spelled_or_int_optional_prefix - WHITE_SPACE explicit_relative_month WHITE_SPACE relative_suffix - -> ^(RELATIVE_DATE - ^(SEEK relative_suffix spelled_or_int_optional_prefix explicit_relative_month) - explicit_day_of_month_part) + : (explicit_day_of_month_part WHITE_SPACE spelled_or_int_optional_prefix)=> + explicit_day_of_month_part WHITE_SPACE spelled_or_int_optional_prefix + WHITE_SPACE explicit_relative_month WHITE_SPACE relative_date_suffix + -> ^(RELATIVE_DATE ^(SEEK relative_date_suffix spelled_or_int_optional_prefix explicit_relative_month) explicit_day_of_month_part) // 10th of next month, 31st of last month, 10th of next october, 30th of this month, the last thursday of last november - | explicit_day_of_month_part WHITE_SPACE prefix WHITE_SPACE explicit_relative_month + | (explicit_day_of_month_part WHITE_SPACE prefix)=> + explicit_day_of_month_part WHITE_SPACE prefix WHITE_SPACE explicit_relative_month -> ^(RELATIVE_DATE ^(SEEK prefix explicit_relative_month) explicit_day_of_month_part) @@ -322,18 +427,32 @@ explicit_relative_date -> ^(RELATIVE_DATE ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["2"] SPAN["month"]) explicit_day_of_month_part) - + + // monday after next + | (explicit_day_of_week_part WHITE_SPACE AFTER WHITE_SPACE NEXT) + -> ^(RELATIVE_DATE + ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["2"] SPAN["week"]) + explicit_day_of_week_part) + + // saturday before last + | (explicit_day_of_week_part WHITE_SPACE BEFORE WHITE_SPACE LAST) + -> ^(RELATIVE_DATE + ^(SEEK DIRECTION["<"] SEEK_BY["by_day"] INT["2"] SPAN["week"]) + explicit_day_of_week_part) + // monday of last week, tuesday of next week - | explicit_day_of_week_part WHITE_SPACE prefix WHITE_SPACE WEEK + | (explicit_day_of_week_part WHITE_SPACE prefix WHITE_SPACE WEEK)=> + explicit_day_of_week_part WHITE_SPACE prefix WHITE_SPACE WEEK -> ^(RELATIVE_DATE ^(SEEK prefix SPAN["week"]) explicit_day_of_week_part) // monday of 2 weeks ago, tuesday of 3 weeks from now - | explicit_day_of_week_part WHITE_SPACE spelled_or_int_optional_prefix - WHITE_SPACE WEEK WHITE_SPACE relative_suffix + | (explicit_day_of_week_part WHITE_SPACE spelled_or_int_optional_prefix)=> + explicit_day_of_week_part WHITE_SPACE spelled_or_int_optional_prefix + WHITE_SPACE WEEK WHITE_SPACE relative_date_suffix -> ^(RELATIVE_DATE - ^(SEEK relative_suffix spelled_or_int_optional_prefix SPAN["week"]) + ^(SEEK relative_date_suffix spelled_or_int_optional_prefix SPAN["week"]) explicit_day_of_week_part) // monday of the week after next @@ -355,25 +474,29 @@ explicit_relative_date -> ^(RELATIVE_DATE ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["0"] relaxed_month) explicit_day_of_month_part) + + // the first day of 2009 + | explicit_day_of_year_part WHITE_SPACE relaxed_year + -> ^(RELATIVE_DATE ^(EXPLICIT_SEEK relaxed_year) explicit_day_of_year_part) ; explicit_day_of_month_part // first of, 10th of, 31st of, - : (THE WHITE_SPACE)? relaxed_day_of_month WHITE_SPACE (IN | OF) + : (THE WHITE_SPACE)? relaxed_day_of_month (WHITE_SPACE (IN | OF))? -> ^(EXPLICIT_SEEK relaxed_day_of_month) // the last thursday - | (THE WHITE_SPACE)? relative_occurrence_index WHITE_SPACE day_of_week WHITE_SPACE (IN | OF) + | (THE WHITE_SPACE)? relative_occurrence_index WHITE_SPACE day_of_week (WHITE_SPACE (IN | OF))? -> ^(EXPLICIT_SEEK relative_occurrence_index day_of_week) // in the start of, at the beginning of, the end of, last day of, first day of - | (((IN | AT) WHITE_SPACE)? THE WHITE_SPACE)? explicit_day_of_month_bound WHITE_SPACE (OF | IN) + | (((IN | AT) WHITE_SPACE)? THE WHITE_SPACE)? explicit_day_of_month_bound (WHITE_SPACE (OF | IN))? -> explicit_day_of_month_bound ; explicit_day_of_week_part // monday of, tuesday of - : (THE WHITE_SPACE)? relaxed_day_of_week (IN | OF) + : (THE WHITE_SPACE)? relaxed_day_of_week (IN | OF)? -> ^(EXPLICIT_SEEK relaxed_day_of_week) // in the end of, at the beginning of @@ -381,6 +504,31 @@ explicit_day_of_week_part -> explicit_day_of_week_bound ; +explicit_day_of_year_part + // last of, first of, 15th day of + : (THE WHITE_SPACE)? relaxed_day_of_year (WHITE_SPACE (IN | OF))? + -> ^(EXPLICIT_SEEK relaxed_day_of_year) + + // in the start of, at the beginning of, the end of, last day of, first day of + | (((IN | AT) WHITE_SPACE)? THE WHITE_SPACE)? explicit_day_of_year_bound (WHITE_SPACE (OF | IN))? + -> explicit_day_of_year_bound + ; + +// the lower or upper bound when talking about days in a year +explicit_day_of_year_bound + // beginning, start + : (BEGINNING | START) + -> ^(EXPLICIT_SEEK ^(DAY_OF_YEAR INT["1"])) + + // first day, 2nd day, etc + | (spelled_first_to_thirty_first WHITE_SPACE DAY) + -> ^(EXPLICIT_SEEK ^(DAY_OF_YEAR spelled_first_to_thirty_first)) + + // end, last day + | (END | (LAST WHITE_SPACE DAY)) + -> ^(EXPLICIT_SEEK ^(DAY_OF_YEAR INT["366"])) + ; + // the lower or upper bound when talking about days in a month explicit_day_of_month_bound // beginning, start @@ -422,35 +570,66 @@ relative_occurrence_index ; relative_target - : day_of_week + : day_of_week | relaxed_month | relative_date_span ; +relative_time_target + : relative_time_span + ; + +relative_time_span + : HOUR -> SPAN["hour"] + | MINUTE -> SPAN["minute"] + | SECOND -> SPAN["second"] + ; + implicit_prefix : THIS -> DIRECTION[">"] SEEK_BY["by_day"] INT["0"] ; -relative_prefix +relative_date_prefix : (THIS WHITE_SPACE)? LAST -> DIRECTION["<"] SEEK_BY["by_week"] INT["1"] | (THIS WHITE_SPACE)? NEXT -> DIRECTION[">"] SEEK_BY["by_week"] INT["1"] | (THIS WHITE_SPACE)? PAST -> DIRECTION["<"] SEEK_BY["by_day"] INT["1"] | (THIS WHITE_SPACE)? COMING -> DIRECTION[">"] SEEK_BY["by_day"] INT["1"] | (THIS WHITE_SPACE)? UPCOMING -> DIRECTION[">"] SEEK_BY["by_day"] INT["1"] + | IN WHITE_SPACE (AM | AN) -> DIRECTION[">"] SEEK_BY["by_day"] INT["1"] | (IN WHITE_SPACE)? spelled_or_int_optional_prefix -> DIRECTION[">"] SEEK_BY["by_day"] spelled_or_int_optional_prefix ; prefix - : relative_prefix + : relative_date_prefix | implicit_prefix ; -relative_suffix +relative_date_suffix + // from now, after today : (FROM | AFTER) WHITE_SPACE (NOW | TODAY) -> DIRECTION[">"] SEEK_BY["by_day"] | AGO -> DIRECTION["<"] SEEK_BY["by_day"] ; +relative_time_suffix + // from now, after today, before noon, after 4pm + : (FROM | AFTER) (WHITE_SPACE relative_time_suffix_anchor)? + -> DIRECTION[">"] SEEK_BY["by_day"] relative_time_suffix_anchor + + // before noon, before 3pm + | BEFORE (WHITE_SPACE relative_time_suffix_anchor)? + -> DIRECTION["<"] SEEK_BY["by_day"] relative_time_suffix_anchor + + | AGO + -> DIRECTION["<"] SEEK_BY["by_day"] + ; + +relative_time_suffix_anchor + : named_relative_time + | explicit_time + -> ^(EXPLICIT_SEEK explicit_time) + ; + relative_date_span : DAY -> SPAN["day"] | WEEK -> SPAN["week"] @@ -474,17 +653,165 @@ named_relative_date | YESTERDAY -> ^(RELATIVE_DATE ^(SEEK DIRECTION["<"] SEEK_BY["by_day"] INT["1"] SPAN["day"])) ; +named_relative_time + : NOW -> ^(RELATIVE_DATE ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["0"] SPAN["day"])) + ; + +// ********** holidays ********** + +holiday + : spelled_or_int_optional_prefix WHITE_SPACE holiday_name WHITE_SPACE relative_date_suffix + -> ^(SEEK relative_date_suffix spelled_or_int_optional_prefix holiday_name) + + | relative_date_prefix WHITE_SPACE holiday_name + -> ^(SEEK relative_date_prefix holiday_name) + + | holiday_name relaxed_year_prefix relaxed_year + -> ^(EXPLICIT_SEEK holiday_name relaxed_year) + + | holiday_name + -> ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["1"] holiday_name) + ; + +holiday_name + : APRIL WHITE_SPACE FOOL (WHITE_SPACE DAY)? + -> HOLIDAY["APRIL_FOOLS_DAY"] + + | BLACK WHITE_SPACE FRIDAY + -> HOLIDAY["BLACK_FRIDAY"] + + | CHRISTMAS WHITE_SPACE EVENING + -> HOLIDAY["CHRISTMAS_EVE"] + + | CHRISTMAS (WHITE_SPACE DAY)? + -> HOLIDAY["CHRISTMAS"] + + | COLUMBUS WHITE_SPACE DAY + -> HOLIDAY["COLUMBUS_DAY"] + + | EARTH WHITE_SPACE DAY + -> HOLIDAY["EARTH_DAY"] + + | EASTER (WHITE_SPACE (SUNDAY | DAY))? + -> HOLIDAY["EASTER"] + + | FATHER WHITE_SPACE DAY + -> HOLIDAY["FATHERS_DAY"] + + | FLAG WHITE_SPACE DAY + -> HOLIDAY["FLAG_DAY"] + + | GOOD WHITE_SPACE FRIDAY + -> HOLIDAY["GOOD_FRIDAY"] + + | GROUNDHOG WHITE_SPACE? DAY + -> HOLIDAY["GROUNDHOG_DAY"] + + | HALLOWEEN (WHITE_SPACE DAY)? + -> HOLIDAY["HALLOWEEN"] + + | INAUGURATION WHITE_SPACE DAY + -> HOLIDAY["INAUGURATION_DAY"] + + | INDEPENDENCE WHITE_SPACE DAY + -> HOLIDAY["INDEPENDENCE_DAY"] + + | KWANZAA (WHITE_SPACE DAY)? + -> HOLIDAY["KWANZAA"] + + | LABOR WHITE_SPACE DAY + -> HOLIDAY["LABOR_DAY"] + + | MLK WHITE_SPACE DAY + -> HOLIDAY["MLK_DAY"] + + | MEMORIAL WHITE_SPACE DAY + -> HOLIDAY["MEMORIAL_DAY"] + + | MOTHER WHITE_SPACE DAY + -> HOLIDAY["MOTHERS_DAY"] + + | NEW WHITE_SPACE YEAR WHITE_SPACE EVENING + -> HOLIDAY["NEW_YEARS_EVE"] + + | NEW WHITE_SPACE YEAR (WHITE_SPACE DAY)? + -> HOLIDAY["NEW_YEARS_DAY"] + + | PATRIOT WHITE_SPACE DAY + -> HOLIDAY["PATRIOT_DAY"] + + | PRESIDENT WHITE_SPACE DAY + -> HOLIDAY["PRESIDENTS_DAY"] + + | (SAINT | ST DOT?) WHITE_SPACE PATRICK WHITE_SPACE DAY + -> HOLIDAY["ST_PATRICKS_DAY"] + + | TAX WHITE_SPACE DAY + -> HOLIDAY["TAX_DAY"] + + | THANKSGIVING (WHITE_SPACE DAY)? + -> HOLIDAY["THANKSGIVING"] + + | ELECTION WHITE_SPACE DAY + -> HOLIDAY["ELECTION_DAY"] + + | VALENTINE WHITE_SPACE DAY + -> HOLIDAY["VALENTINES_DAY"] + + | VETERAN WHITE_SPACE DAY + -> HOLIDAY["VETERANS_DAY"] + ; + +season + : spelled_or_int_optional_prefix WHITE_SPACE season_name WHITE_SPACE relative_date_suffix + -> ^(SEEK relative_date_suffix spelled_or_int_optional_prefix season_name) + + | relative_date_prefix WHITE_SPACE season_name + -> ^(SEEK relative_date_prefix season_name) + + | season_name relaxed_year_prefix relaxed_year + -> ^(EXPLICIT_SEEK season_name relaxed_year) + + | season_name + -> ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["1"] season_name) + ; + +season_name + :WINTER + -> SEASON["WINTER"] + | SPRING + -> SEASON["SPRING"] + | SUMMER + -> SEASON["SUMMER"] + | (FALL | AUTUMN) + -> SEASON["FALL"] + ; + // ********** time rules ********** +relative_time + // 10 hours ago, 20 minutes before noon + : spelled_or_int_optional_prefix WHITE_SPACE relative_time_target WHITE_SPACE relative_time_suffix + -> ^(RELATIVE_TIME ^(SEEK relative_time_suffix spelled_or_int_optional_prefix relative_time_target)) + + // in 3 minutes + | IN WHITE_SPACE spelled_or_int_optional_prefix WHITE_SPACE relative_time_target + -> ^(RELATIVE_TIME ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] spelled_or_int_optional_prefix relative_time_target)) + + // next hour, last minute + | prefix WHITE_SPACE relative_time_target + -> ^(RELATIVE_TIME ^(SEEK prefix relative_time_target)) + ; + // a time with an hour, optional minutes, and optional meridian indicator -time +explicit_time : hours COLON? minutes (COLON? seconds)? (WHITE_SPACE? (meridian_indicator | (MILITARY_HOUR_SUFFIX | HOUR)))? (WHITE_SPACE? time_zone)? -> ^(EXPLICIT_TIME hours minutes seconds? meridian_indicator? time_zone?) | hours (WHITE_SPACE? meridian_indicator)? (WHITE_SPACE? time_zone)? -> ^(EXPLICIT_TIME hours ^(MINUTES_OF_HOUR INT["0"]) meridian_indicator? time_zone?) - | named_time (WHITE_SPACE time_zone)? + | (THIS WHITE_SPACE)? named_time (WHITE_SPACE time_zone)? -> ^(EXPLICIT_TIME named_time time_zone?) ; @@ -507,23 +834,34 @@ seconds meridian_indicator : AM -> AM_PM["am"] | PM -> AM_PM["pm"] + | (IN WHITE_SPACE THE WHITE_SPACE)? MORNING -> AM_PM["am"] + | (IN WHITE_SPACE THE WHITE_SPACE)? NOON -> AM_PM["pm"] + | (IN WHITE_SPACE THE WHITE_SPACE)? EVENING -> AM_PM["pm"] + | (AT WHITE_SPACE)? NIGHT -> AM_PM["pm"] + ; named_time - : (IN WHITE_SPACE THE WHITE_SPACE)? NOON -> ^(HOURS_OF_DAY INT["12"]) ^(MINUTES_OF_HOUR INT["0"]) AM_PM["pm"] - | (IN WHITE_SPACE THE WHITE_SPACE)? MORNING -> ^(HOURS_OF_DAY INT["8"]) ^(MINUTES_OF_HOUR INT["0"]) AM_PM["am"] - | (IN WHITE_SPACE THE WHITE_SPACE)? NIGHT -> ^(HOURS_OF_DAY INT["8"]) ^(MINUTES_OF_HOUR INT["0"]) AM_PM["pm"] - | MIDNIGHT -> ^(HOURS_OF_DAY INT["12"]) ^(MINUTES_OF_HOUR INT["0"]) AM_PM["am"] + : (IN WHITE_SPACE THE WHITE_SPACE)? NOON -> ^(HOURS_OF_DAY INT["12"]) ^(MINUTES_OF_HOUR INT["0"]) ^(SECONDS_OF_MINUTE INT["0"]) AM_PM["pm"] + | (IN WHITE_SPACE THE WHITE_SPACE)? MORNING -> ^(HOURS_OF_DAY INT["8"]) ^(MINUTES_OF_HOUR INT["0"]) ^(SECONDS_OF_MINUTE INT["0"]) AM_PM["am"] + | (IN WHITE_SPACE THE WHITE_SPACE)? NIGHT -> ^(HOURS_OF_DAY INT["8"]) ^(MINUTES_OF_HOUR INT["0"]) ^(SECONDS_OF_MINUTE INT["0"]) AM_PM["pm"] + | TONIGHT -> ^(HOURS_OF_DAY INT["8"]) ^(MINUTES_OF_HOUR INT["0"]) ^(SECONDS_OF_MINUTE INT["0"]) AM_PM["pm"] + | (AT WHITE_SPACE)? MIDNIGHT -> ^(HOURS_OF_DAY INT["12"]) ^(MINUTES_OF_HOUR INT["0"]) ^(SECONDS_OF_MINUTE INT["0"]) AM_PM["am"] + | (IN WHITE_SPACE THE WHITE_SPACE)? EVENING -> ^(HOURS_OF_DAY INT["7"]) ^(MINUTES_OF_HOUR INT["0"]) ^(SECONDS_OF_MINUTE INT["0"]) AM_PM["pm"] ; time_zone - : time_zone_abbreviation - | time_zone_offset + : time_zone_plus_offset + | time_zone_abbreviation ; +time_zone_plus_offset + : UTC? time_zone_offset -> ZONE_OFFSET[$time_zone_offset.text] + ; + + time_zone_offset : (PLUS | DASH) hours (COLON? minutes)? - -> ZONE_OFFSET[$time_zone_offset.text] ; time_zone_abbreviation @@ -535,172 +873,3 @@ time_zone_abbreviation | AKST -> ZONE["America/Anchorage"] | HAST -> ZONE["Pacific/Honolulu"] ; - -// ********** numeric rules ********** - -// a number between 00 and 59 inclusive, with a mandatory 0 prefix before numbers 0-9 -int_00_to_59_mandatory_prefix - : (INT_00 - | int_01_to_12 - | int_13_to_23 - | int_24_to_31 - | int_32_to_59) -> INT[$int_00_to_59_mandatory_prefix.text] - ; - -// a number between 00 and 99 inclusive, with a mandatory 0 prefix before numbers 0-9 -int_00_to_99_mandatory_prefix - : (int_00_to_59_mandatory_prefix | int_60_to_99) - -> INT[$int_00_to_99_mandatory_prefix.text] - ; - -// a number between 1 and 12 inclusive, with an optional 0 prefix before numbers 1-9 -int_01_to_12_optional_prefix - : (int_1_to_9 | int_01_to_12) -> INT[$int_01_to_12_optional_prefix.text] - ; - -// a number between 0 and 23 inclusive, with an optional 0 prefix before numbers 0-9 -int_00_to_23_optional_prefix - : (INT_00 - | INT_0 - | int_1_to_9 - | int_01_to_12 - | int_13_to_23) -> INT[$int_00_to_23_optional_prefix.text] - ; - -// a number between 1 and 31 inclusive, with an optional 0 prefix before numbers 1-9 -int_01_to_31_optional_prefix - : (int_01_to_12 - | int_1_to_9 - | int_13_to_23 - | int_24_to_31) -> INT[$int_01_to_31_optional_prefix.text] - ; - -// a number with exactly four digits -int_four_digits - : int_00_to_99_mandatory_prefix int_00_to_99_mandatory_prefix - -> INT[$int_four_digits.text] - ; - -// a number between one and thirty-one either spelled-out, or as an -// integer with an optional 0 prefix for numbers betwen 1 and 9 -spelled_or_int_01_to_31_optional_prefix - : int_01_to_31_optional_prefix - | spelled_one_to_thirty_one - ; - -// a number between 1 and 9999 either spelled-out, or as an -// integer with an optional 0 prefix for numbers betwen 1 and 9 -spelled_or_int_optional_prefix - : spelled_one_to_thirty_one // TODO expand this spelled range to at least ninety-nine - | ((int_01_to_31_optional_prefix | int_32_to_59 | int_60_to_99) - (int_01_to_31_optional_prefix | int_32_to_59 | int_60_to_99)? - -> INT[$spelled_or_int_optional_prefix.text]) - ; - -// a spelled number between one and thirty-one (one, two, etc.) -spelled_one_to_thirty_one - : ONE -> INT["1"] - | TWO -> INT["2"] - | THREE -> INT["3"] - | FOUR -> INT["4"] - | FIVE -> INT["5"] - | SIX -> INT["6"] - | SEVEN -> INT["7"] - | EIGHT -> INT["8"] - | NINE -> INT["9"] - | TEN -> INT["10"] - | ELEVEN -> INT["11"] - | TWELVE -> INT["12"] - | THIRTEEN -> INT["13"] - | FOURTEEN -> INT["14"] - | FIFTEEN -> INT["15"] - | SIXTEEN -> INT["16"] - | SEVENTEEN -> INT["17"] - | EIGHTEEN -> INT["18"] - | NINETEEN -> INT["19"] - | TWENTY -> INT["20"] - | TWENTY (DASH | WHITE_SPACE)? ONE -> INT["21"] - | TWENTY (DASH | WHITE_SPACE)? TWO -> INT["22"] - | TWENTY (DASH | WHITE_SPACE)? THREE -> INT["23"] - | TWENTY (DASH | WHITE_SPACE)? FOUR -> INT["24"] - | TWENTY (DASH | WHITE_SPACE)? FIVE -> INT["25"] - | TWENTY (DASH | WHITE_SPACE)? SIX -> INT["26"] - | TWENTY (DASH | WHITE_SPACE)? SEVEN -> INT["27"] - | TWENTY (DASH | WHITE_SPACE)? EIGHT -> INT["28"] - | TWENTY (DASH | WHITE_SPACE)? NINE -> INT["29"] - | THIRTY -> INT["30"] - | THIRTY (DASH | WHITE_SPACE)? ONE -> INT["31"] - ; - -// a spelled number in sequence between first and thirty-first -spelled_first_to_thirty_first - : (FIRST | INT_1 ST) -> INT["1"] - | (SECOND | INT_2 ND) -> INT["2"] - | (THIRD | INT_3 RD) -> INT["3"] - | (FOURTH | INT_4 TH) -> INT["4"] - | (FIFTH | INT_5 TH) -> INT["5"] - | (SIXTH | INT_6 TH) -> INT["6"] - | (SEVENTH | INT_7 TH) -> INT["7"] - | (EIGHTH | INT_8 TH) -> INT["8"] - | (NINTH | INT_9 TH) -> INT["9"] - | (TENTH | INT_10 TH) -> INT["10"] - | (ELEVENTH | INT_11 TH) -> INT["11"] - | (TWELFTH | INT_12 TH) -> INT["12"] - | (THIRTEENTH | INT_13 TH) -> INT["13"] - | (FOURTEENTH | INT_14 TH) -> INT["14"] - | (FIFTEENTH | INT_15 TH) -> INT["15"] - | (SIXTEENTH | INT_16 TH) -> INT["16"] - | (SEVENTEENTH | INT_17 TH) -> INT["17"] - | (EIGHTEENTH | INT_18 TH) -> INT["18"] - | (NINETEENTH | INT_19 TH) -> INT["19"] - | (TWENTIETH | INT_20 TH) -> INT["20"] - | ((TWENTY (DASH | WHITE_SPACE)? FIRST) | INT_21 ST) -> INT["21"] - | ((TWENTY (DASH | WHITE_SPACE)? SECOND) | INT_22 ND) -> INT["22"] - | ((TWENTY (DASH | WHITE_SPACE)? THIRD) | INT_23 RD) -> INT["23"] - | ((TWENTY (DASH | WHITE_SPACE)? FOURTH) | INT_24 TH) -> INT["24"] - | ((TWENTY (DASH | WHITE_SPACE)? FIFTH) | INT_25 TH) -> INT["25"] - | ((TWENTY (DASH | WHITE_SPACE)? SIXTH) | INT_26 TH) -> INT["26"] - | ((TWENTY (DASH | WHITE_SPACE)? SEVENTH) | INT_27 TH) -> INT["27"] - | ((TWENTY (DASH | WHITE_SPACE)? EIGHTH) | INT_28 TH) -> INT["28"] - | ((TWENTY (DASH | WHITE_SPACE)? NINTH) | INT_29 TH) -> INT["29"] - | (THIRTIETH | INT_30 TH) -> INT["30"] - | ((THIRTY (DASH | WHITE_SPACE)? FIRST) | INT_31 ST) -> INT["31"] - ; - -int_60_to_99 - : INT_60 | INT_61 | INT_62 | INT_63 | INT_64 | INT_65 | INT_66 | INT_67 | INT_68 - | INT_69 | INT_70 | INT_71 | INT_72 | INT_73 | INT_74 | INT_75 | INT_76 | INT_77 - | INT_78 | INT_79 | INT_80 | INT_81 | INT_82 | INT_83 | INT_84 | INT_85 | INT_86 - | INT_87 | INT_88 | INT_89 | INT_90 | INT_91 | INT_92 | INT_93 | INT_94 | INT_95 - | INT_96 | INT_97 | INT_98 | INT_99 - ; - -int_32_to_59 - : INT_32 | INT_33 | INT_34 | INT_35 | INT_36 | INT_37 | INT_38 | INT_39 | INT_40 - | INT_41 | INT_42 | INT_43 | INT_44 | INT_45 | INT_46 | INT_47 | INT_48 | INT_49 - | INT_50 | INT_51 | INT_52 | INT_53 | INT_54 | INT_55 | INT_56 | INT_57 | INT_58 - | INT_59 - ; - -int_24_to_31 - : INT_24 | INT_25 | INT_26 | INT_27 | INT_28 | INT_29 | INT_30 | INT_31 - ; - -int_13_to_23 - : INT_13 | INT_14 | INT_15 | INT_16 | INT_17 | INT_18 | INT_19 | INT_20 | INT_21 - | INT_22 | INT_23 - ; - -int_01_to_12 - : INT_01 | INT_02 | INT_03 | INT_04 | INT_05 | INT_06 | INT_07 | INT_08 | INT_09 - | INT_10 | INT_11 | INT_12 - ; - -int_1_to_9 - : INT_1 | INT_2 | INT_3 | INT_4 | INT_5 | INT_6 | INT_7 | INT_8 | INT_9 - ; - -int_1_to_5 - : INT_1 | INT_2 | INT_3 | INT_4 | INT_5 - ; - \ No newline at end of file diff --git a/src/main/antlr3/com/joestelmach/natty/generated/DateWalker.g b/src/main/antlr3/com/joestelmach/natty/generated/DateWalker.g index 822f917b..847851d3 100644 --- a/src/main/antlr3/com/joestelmach/natty/generated/DateWalker.g +++ b/src/main/antlr3/com/joestelmach/natty/generated/DateWalker.g @@ -9,12 +9,35 @@ options { @members { private com.joestelmach.natty.WalkerState _walkerState = new com.joestelmach.natty.WalkerState(); + private java.util.logging.Logger _logger = java.util.logging.Logger.getLogger("com.joestelmach.natty"); + + public void displayRecognitionError(String[] tokenNames, RecognitionException re) { + String message = getErrorHeader(re); + try { message += getErrorMessage(re, tokenNames); } catch(Exception e) {} + _logger.fine(message); + } + + public void recover(IntStream input, RecognitionException re) { + reportError(re); + _walkerState.clearDateGroup(); + } public com.joestelmach.natty.WalkerState getState() { return _walkerState; } } +parse + : date_time_alternative recurrence? + ; + +recurrence + @init { + _walkerState.setRecurring(); + } + : ^(RECURRENCE date_time?{ _walkerState.captureDateTime(); }) + ; + date_time_alternative : ^(DATE_TIME_ALTERNATIVE date_time+) ; @@ -23,16 +46,17 @@ date_time @after { _walkerState.captureDateTime(); } - : ^(DATE_TIME date time?) + : ^(DATE_TIME date? time?) ; date : relative_date | explicit_date + | explicit_year_only_date ; relative_date - : ^(RELATIVE_DATE seek explicit_seek*) + : ^(RELATIVE_DATE seek? explicit_seek*) ; week_index @@ -45,37 +69,71 @@ explicit_date (^(DAY_OF_WEEK dow=INT))? (^(YEAR_OF year=INT))?) {_walkerState.setExplicitDate($month.text, $dom.text, $dow.text, $year.text);} ; - + +explicit_year_only_date + : ^(EXPLICIT_DATE ^(YEAR_OF year=INT)) + {_walkerState.setExplicitYearOnlyDate($year.text);} + ; + time + : explicit_time + | relative_time + ; + +explicit_time : ^(EXPLICIT_TIME ^(HOURS_OF_DAY hours=INT) ^(MINUTES_OF_HOUR minutes=INT) (^(SECONDS_OF_MINUTE seconds=INT))? AM_PM? (zone=ZONE | zone=ZONE_OFFSET)?) {_walkerState.setExplicitTime($hours.text, $minutes.text, $seconds.text, $AM_PM.text, $zone.text);} ; +relative_time + : ^(RELATIVE_TIME seek) + ; + seek - : ^(SEEK DIRECTION by=SEEK_BY amount=INT ^(DAY_OF_WEEK day=INT) date?) + : ^(SEEK DIRECTION by=SEEK_BY amount=INT ^(DAY_OF_WEEK day=INT) date?) {_walkerState.seekToDayOfWeek($DIRECTION.text, $by.text, $amount.text, $day.text);} | ^(SEEK DIRECTION SEEK_BY amount=INT ^(MONTH_OF_YEAR month=INT)) {_walkerState.seekToMonth($DIRECTION.text, $amount.text, $month.text);} - | ^(SEEK DIRECTION SEEK_BY INT SPAN) + | ^(SEEK DIRECTION SEEK_BY (explicit_seek | relative_date)? INT SPAN) {_walkerState.seekBySpan($DIRECTION.text, $INT.text, $SPAN.text);} | ^(SEEK DIRECTION SEEK_BY INT date) {_walkerState.seekBySpan($DIRECTION.text, $INT.text, $SEEK_BY.text);} + + | ^(SEEK DIRECTION SEEK_BY INT HOLIDAY) + {_walkerState.seekToHoliday($HOLIDAY.text, $DIRECTION.text, $INT.text);} + + | ^(SEEK DIRECTION SEEK_BY INT SEASON) + {_walkerState.seekToSeason($SEASON.text, $DIRECTION.text, $INT.text);} ; explicit_seek - : ^(EXPLICIT_SEEK ^(DAY_OF_MONTH month=INT)) + : ^(EXPLICIT_SEEK ^(MONTH_OF_YEAR day=INT)) + {_walkerState.seekToMonth(">", "0", $day.text);} + + | ^(EXPLICIT_SEEK ^(DAY_OF_MONTH month=INT)) {_walkerState.seekToDayOfMonth($month.text);} | ^(EXPLICIT_SEEK ^(DAY_OF_WEEK day=INT)) {_walkerState.seekToDayOfWeek(">", "by_week", "0", $day.text);} + | ^(EXPLICIT_SEEK ^(DAY_OF_YEAR day=INT)) + {_walkerState.seekToDayOfYear($day.text);} + | ^(EXPLICIT_SEEK ^(YEAR_OF year=INT)) {_walkerState.seekToYear($year.text);} + | ^(EXPLICIT_SEEK HOLIDAY ^(YEAR_OF year=INT)) + {_walkerState.seekToHolidayYear($HOLIDAY.text, $year.text);} + + | ^(EXPLICIT_SEEK SEASON ^(YEAR_OF year=INT)) + {_walkerState.seekToSeasonYear($SEASON.text, $year.text);} + | ^(EXPLICIT_SEEK index=INT ^(DAY_OF_WEEK day=INT)) {_walkerState.setDayOfWeekIndex($index.text, $day.text);} - ; + + | ^(EXPLICIT_SEEK explicit_time) + ; \ No newline at end of file diff --git a/src/main/antlr3/com/joestelmach/natty/generated/TreeRewrite.g b/src/main/antlr3/com/joestelmach/natty/generated/TreeRewrite.g index bad47b16..1e460e03 100644 --- a/src/main/antlr3/com/joestelmach/natty/generated/TreeRewrite.g +++ b/src/main/antlr3/com/joestelmach/natty/generated/TreeRewrite.g @@ -9,7 +9,38 @@ options { @header { package com.joestelmach.natty.generated; } +@members { + private java.util.logging.Logger _logger = java.util.logging.Logger.getLogger("com.joestelmach.natty"); + + public void displayRecognitionError(String[] tokenNames, RecognitionException re) { + String message = getErrorHeader(re); + try { message += getErrorMessage(re, tokenNames); } catch(Exception e) {} + _logger.fine(message); + } +} + topdown : ^(SEEK DIRECTION SEEK_BY INT ^(DAY_OF_WEEK INT) ^(DAY_OF_WEEK dow=INT)) -> ^(SEEK DIRECTION SEEK_BY INT ^(DAY_OF_WEEK $dow)) + + | ^(SEEK DIRECTION SEEK_BY INT ^(DAY_OF_MONTH INT) ^(DAY_OF_MONTH dow=INT)) + -> ^(SEEK DIRECTION SEEK_BY INT ^(DAY_OF_MONTH $dow)) + + | ^(SEEK DIRECTION SEEK_BY INT ^(MONTH_OF_YEAR INT) ^(MONTH_OF_YEAR dow=INT)) + -> ^(SEEK DIRECTION SEEK_BY INT ^(MONTH_OF_YEAR $dow)) + + | ^(SEEK DIRECTION SEEK_BY INT ^(MONTH_OF_YEAR INT) amount=INT ^(MONTH_OF_YEAR dow=INT)) + -> ^(SEEK DIRECTION SEEK_BY $amount ^(MONTH_OF_YEAR $dow)) + + // ensure year seek happens before day of week seek + + | ^(RELATIVE_DATE ^(SEEK dir=DIRECTION seekby=SEEK_BY day=INT ^(MONTH_OF_YEAR month=INT)) + ^(EXPLICIT_SEEK amount=INT ^(DAY_OF_WEEK dow=INT)) + ^(EXPLICIT_SEEK ^(YEAR_OF year=INT)) + ) + + -> ^(RELATIVE_DATE ^(SEEK $dir $seekby $day ^(MONTH_OF_YEAR $month)) + ^(EXPLICIT_SEEK ^(YEAR_OF $year)) + ^(EXPLICIT_SEEK $amount ^(DAY_OF_WEEK $dow)) + ) ; \ No newline at end of file diff --git a/src/main/antlr3/com/joestelmach/natty/generated/imports/NumericRules.g b/src/main/antlr3/com/joestelmach/natty/generated/imports/NumericRules.g new file mode 100644 index 00000000..7d64a32d --- /dev/null +++ b/src/main/antlr3/com/joestelmach/natty/generated/imports/NumericRules.g @@ -0,0 +1,188 @@ +parser grammar NumericRules; + +options { + output=AST; +} + +tokens { + INT; +} + +// ********** numeric rules ********** + +// a number between 00 and 59 inclusive, with a mandatory 0 prefix before numbers 0-9 +int_00_to_59_mandatory_prefix + : (INT_00 + | int_01_to_12 + | int_13_to_23 + | int_24_to_31 + | int_32_to_59) -> INT[$int_00_to_59_mandatory_prefix.text] + ; + +// a number between 00 and 99 inclusive, with a mandatory 0 prefix before numbers 0-9 +int_00_to_99_mandatory_prefix + : (int_00_to_59_mandatory_prefix | int_60_to_99) + -> INT[$int_00_to_99_mandatory_prefix.text] + ; + +// a number between 1 and 12 inclusive, with an optional 0 prefix before numbers 1-9 +int_01_to_12_optional_prefix + : (int_1_to_9 | int_01_to_12) -> INT[$int_01_to_12_optional_prefix.text] + ; + +// a number between 0 and 23 inclusive, with an optional 0 prefix before numbers 0-9 +int_00_to_23_optional_prefix + : (INT_00 + | INT_0 + | int_1_to_9 + | int_01_to_12 + | int_13_to_23) -> INT[$int_00_to_23_optional_prefix.text] + ; + +// a number between 1 and 31 inclusive, with an optional 0 prefix before numbers 1-9 +int_01_to_31_optional_prefix + : (int_01_to_12 + | int_1_to_9 + | int_13_to_23 + | int_24_to_31) -> INT[$int_01_to_31_optional_prefix.text] + ; + +// a number with exactly four digits +int_four_digits + : int_00_to_99_mandatory_prefix int_00_to_99_mandatory_prefix + -> INT[$int_four_digits.text] + ; + +// a number between one and thirty-one either spelled-out, or as an +// integer with an optional 0 prefix for numbers betwen 1 and 9 +spelled_or_int_01_to_31_optional_prefix + : int_01_to_31_optional_prefix + | spelled_one_to_thirty_one + ; + +// a number between 1 and 9999 either spelled-out, or as an +// integer with an optional 0 prefix for numbers betwen 1 and 9 +spelled_or_int_optional_prefix + : spelled_one_to_thirty_one // TODO expand this spelled range to at least ninety-nine + | ((int_01_to_31_optional_prefix | int_32_to_59 | int_60_to_99) + (int_01_to_31_optional_prefix | int_32_to_59 | int_60_to_99)? + -> INT[$spelled_or_int_optional_prefix.text]) + ; + +// a spelled number between one and thirty-one (one, two, etc.) +spelled_one_to_thirty_one + : ONE -> INT["1"] + | TWO -> INT["2"] + | THREE -> INT["3"] + | FOUR -> INT["4"] + | FIVE -> INT["5"] + | SIX -> INT["6"] + | SEVEN -> INT["7"] + | EIGHT -> INT["8"] + | NINE -> INT["9"] + | TEN -> INT["10"] + | ELEVEN -> INT["11"] + | TWELVE -> INT["12"] + | THIRTEEN -> INT["13"] + | FOURTEEN -> INT["14"] + | FIFTEEN -> INT["15"] + | SIXTEEN -> INT["16"] + | SEVENTEEN -> INT["17"] + | EIGHTEEN -> INT["18"] + | NINETEEN -> INT["19"] + | (TWENTY WHITE_SPACE ONE)=> TWENTY WHITE_SPACE ONE -> INT["21"] + | TWENTY DASH? ONE -> INT["21"] + | (TWENTY WHITE_SPACE TWO)=> TWENTY WHITE_SPACE TWO -> INT["22"] + | TWENTY DASH? TWO -> INT["22"] + | (TWENTY WHITE_SPACE THREE)=> TWENTY WHITE_SPACE THREE -> INT["23"] + | TWENTY DASH? THREE -> INT["23"] + | (TWENTY WHITE_SPACE FOUR)=> TWENTY WHITE_SPACE FOUR -> INT["24"] + | TWENTY DASH? FOUR -> INT["24"] + | (TWENTY WHITE_SPACE FIVE)=> TWENTY WHITE_SPACE FIVE -> INT["25"] + | TWENTY DASH? FIVE -> INT["25"] + | (TWENTY WHITE_SPACE SIX)=> TWENTY WHITE_SPACE SIX -> INT["26"] + | TWENTY DASH? SIX -> INT["26"] + | (TWENTY WHITE_SPACE SEVEN)=> TWENTY WHITE_SPACE SEVEN -> INT["27"] + | TWENTY DASH? SEVEN -> INT["27"] + | (TWENTY WHITE_SPACE EIGHT)=> TWENTY WHITE_SPACE EIGHT -> INT["28"] + | TWENTY DASH? EIGHT -> INT["28"] + | (TWENTY WHITE_SPACE NINE)=> TWENTY WHITE_SPACE NINE -> INT["29"] + | TWENTY DASH? NINE -> INT["29"] + | TWENTY -> INT["20"] + | (THIRTY WHITE_SPACE ONE)=> THIRTY WHITE_SPACE ONE -> INT["31"] + | THIRTY DASH? ONE -> INT["31"] + | THIRTY -> INT["30"] + ; + +// a spelled number in sequence between first and thirty-first +spelled_first_to_thirty_first + : (FIRST | INT_1 ST) -> INT["1"] + | (SECOND | INT_2 ND) -> INT["2"] + | (THIRD | INT_3 RD) -> INT["3"] + | (FOURTH | INT_4 TH) -> INT["4"] + | (FIFTH | INT_5 TH) -> INT["5"] + | (SIXTH | INT_6 TH) -> INT["6"] + | (SEVENTH | INT_7 TH) -> INT["7"] + | (EIGHTH | INT_8 TH) -> INT["8"] + | (NINTH | INT_9 TH) -> INT["9"] + | (TENTH | INT_10 TH) -> INT["10"] + | (ELEVENTH | INT_11 TH) -> INT["11"] + | (TWELFTH | INT_12 TH) -> INT["12"] + | (THIRTEENTH | INT_13 TH) -> INT["13"] + | (FOURTEENTH | INT_14 TH) -> INT["14"] + | (FIFTEENTH | INT_15 TH) -> INT["15"] + | (SIXTEENTH | INT_16 TH) -> INT["16"] + | (SEVENTEENTH | INT_17 TH) -> INT["17"] + | (EIGHTEENTH | INT_18 TH) -> INT["18"] + | (NINETEENTH | INT_19 TH) -> INT["19"] + | (TWENTIETH | INT_20 TH) -> INT["20"] + | ((TWENTY (DASH | WHITE_SPACE)? FIRST) | INT_21 ST) -> INT["21"] + | ((TWENTY (DASH | WHITE_SPACE)? SECOND) | INT_22 ND) -> INT["22"] + | ((TWENTY (DASH | WHITE_SPACE)? THIRD) | INT_23 RD) -> INT["23"] + | ((TWENTY (DASH | WHITE_SPACE)? FOURTH) | INT_24 TH) -> INT["24"] + | ((TWENTY (DASH | WHITE_SPACE)? FIFTH) | INT_25 TH) -> INT["25"] + | ((TWENTY (DASH | WHITE_SPACE)? SIXTH) | INT_26 TH) -> INT["26"] + | ((TWENTY (DASH | WHITE_SPACE)? SEVENTH) | INT_27 TH) -> INT["27"] + | ((TWENTY (DASH | WHITE_SPACE)? EIGHTH) | INT_28 TH) -> INT["28"] + | ((TWENTY (DASH | WHITE_SPACE)? NINTH) | INT_29 TH) -> INT["29"] + | (THIRTIETH | INT_30 TH) -> INT["30"] + | ((THIRTY (DASH | WHITE_SPACE)? FIRST) | INT_31 ST) -> INT["31"] + ; + +int_60_to_99 + : INT_60 | INT_61 | INT_62 | INT_63 | INT_64 | INT_65 | INT_66 | INT_67 | INT_68 + | INT_69 | INT_70 | INT_71 | INT_72 | INT_73 | INT_74 | INT_75 | INT_76 | INT_77 + | INT_78 | INT_79 | INT_80 | INT_81 | INT_82 | INT_83 | INT_84 | INT_85 | INT_86 + | INT_87 | INT_88 | INT_89 | INT_90 | INT_91 | INT_92 | INT_93 | INT_94 | INT_95 + | INT_96 | INT_97 | INT_98 | INT_99 + ; + +int_32_to_59 + : INT_32 | INT_33 | INT_34 | INT_35 | INT_36 | INT_37 | INT_38 | INT_39 | INT_40 + | INT_41 | INT_42 | INT_43 | INT_44 | INT_45 | INT_46 | INT_47 | INT_48 | INT_49 + | INT_50 | INT_51 | INT_52 | INT_53 | INT_54 | INT_55 | INT_56 | INT_57 | INT_58 + | INT_59 + ; + +int_24_to_31 + : INT_24 | INT_25 | INT_26 | INT_27 | INT_28 | INT_29 | INT_30 | INT_31 + ; + +int_13_to_23 + : INT_13 | INT_14 | INT_15 | INT_16 | INT_17 | INT_18 | INT_19 | INT_20 | INT_21 + | INT_22 | INT_23 + ; + +int_01_to_12 + : INT_01 | INT_02 | INT_03 | INT_04 | INT_05 | INT_06 | INT_07 | INT_08 | INT_09 + | INT_10 | INT_11 | INT_12 + ; + +int_1_to_9 + : INT_1 | INT_2 | INT_3 | INT_4 | INT_5 | INT_6 | INT_7 | INT_8 | INT_9 + ; + +int_1_to_5 + : INT_1 | INT_2 | INT_3 | INT_4 | INT_5 + ; + \ No newline at end of file diff --git a/src/main/java/com/joestelmach/natty/CalendarSource.java b/src/main/java/com/joestelmach/natty/CalendarSource.java new file mode 100644 index 00000000..1ffd1a9a --- /dev/null +++ b/src/main/java/com/joestelmach/natty/CalendarSource.java @@ -0,0 +1,28 @@ +package com.joestelmach.natty; + +import java.util.Date; +import java.util.GregorianCalendar; + +/** + * Responsible for generating new Calendars that represent + * the current point in time. This is neccessary so we can + * manipulate what the software thinks is the 'current' + * time, which may be different than the system time + * + * @author Joe Stelmach + */ +public class CalendarSource { + private static ThreadLocal _baseDate = new ThreadLocal(); + + public static void setBaseDate(Date baseDate) { + _baseDate.set(baseDate); + } + + public static GregorianCalendar getCurrentCalendar() { + GregorianCalendar calendar = new GregorianCalendar(); + if(_baseDate.get() != null) { + calendar.setTime(_baseDate.get()); + } + return calendar; + } +} diff --git a/src/main/java/com/joestelmach/natty/DateGroup.java b/src/main/java/com/joestelmach/natty/DateGroup.java new file mode 100644 index 00000000..9ec6c4f3 --- /dev/null +++ b/src/main/java/com/joestelmach/natty/DateGroup.java @@ -0,0 +1,98 @@ +package com.joestelmach.natty; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import org.antlr.runtime.tree.Tree; + +/** + * + * @author Joe Stelmach + */ +public class DateGroup { + private List _dates; + private String _text; + private int _line; + private int _position; + private boolean _isRecurring; + private boolean _isTimeInferred; + private Date _recurringUntil; + private Map> _parseLocations; + private Tree _syntaxTree; + + public DateGroup() { + _dates = new ArrayList(); + _isTimeInferred = true; + } + + public List getDates() { + return _dates; + } + public void addDate(Date date) { + _dates.add(date); + } + + public String getText() { + return _text; + } + public void setText(String text) { + _text = text; + } + + public int getLine() { + return _line; + } + public void setLine(int line) { + _line = line; + } + + public int getPosition() { + return _position; + } + public void setPosition(int position) { + _position = position; + } + + public boolean isRecurring() { + return _isRecurring; + } + public void setRecurring(boolean isRecurring) { + _isRecurring = isRecurring; + } + + /** + * @return true if the time information in this date group has been inferred + * as opposed to being explicity defined in the _text input. + */ + public boolean isTimeInferred() { + return _isTimeInferred; + } + public void setIsTimeInferred(boolean isTimeInferred) { + this._isTimeInferred = isTimeInferred; + } + + public Date getRecursUntil() { + return _recurringUntil; + } + public void setRecurringUntil(Date recurringUntil) { + _recurringUntil = recurringUntil; + } + + public Map> getParseLocations() { + return _parseLocations; + } + public void setParseLocations(Map> parseLocations) { + _parseLocations = parseLocations; + } + + public Tree getSyntaxTree() { + return _syntaxTree; + } + + public void setSyntaxTree(Tree syntaxTree) { + _syntaxTree = syntaxTree; + } + +} diff --git a/src/main/java/com/joestelmach/natty/Holiday.java b/src/main/java/com/joestelmach/natty/Holiday.java new file mode 100644 index 00000000..fdb5414f --- /dev/null +++ b/src/main/java/com/joestelmach/natty/Holiday.java @@ -0,0 +1,57 @@ +package com.joestelmach.natty; + +import java.util.HashMap; +import java.util.Map; + +public enum Holiday { + APRIL_FOOLS_DAY("April Fool's Day"), + BLACK_FRIDAY("Black Friday"), + CHRISTMAS("Christmas Day"), + CHRISTMAS_EVE("Christmas Eve"), + COLUMBUS_DAY("Columbus Day (US-OPM)"), + EARTH_DAY("Earth Day"), + EASTER("Easter Sunday"), + FATHERS_DAY("Father's Day"), + FLAG_DAY("Flag Day"), + GOOD_FRIDAY("Good Friday"), + GROUNDHOG_DAY("Groundhog's Day"), + HALLOWEEN("Halloween"), + INDEPENDENCE_DAY("Independence Day"), + KWANZAA("Kwanzaa"), + LABOR_DAY("Labor Day"), + MLK_DAY("Martin Luther King Jr.'s Day"), + MEMORIAL_DAY("Memorial Day"), + MOTHERS_DAY("Mother's Day"), + NEW_YEARS_DAY("New Year's Day"), + NEW_YEARS_EVE("New Year's Eve"), + PATRIOT_DAY("Patriot Day"), + PRESIDENTS_DAY("President's Day"), + ST_PATRICKS_DAY("St. Patrick's Day"), + TAX_DAY("Tax Day"), + THANKSGIVING("Thanksgiving Day"), + ELECTION_DAY("US General Election"), + VALENTINES_DAY("Valentine's Day"), + VETERANS_DAY("Veteran's Day"); + + private String summary; + private static final Map lookup; + static { + lookup = new HashMap(); + for(Holiday h:values()) { + lookup.put(h.getSummary(), h); + } + } + + Holiday(String summary) { + this.summary = summary; + } + + public String getSummary() { + return summary; + } + + public static Holiday fromSummary(String summary) { + if(summary == null) return null; + return lookup.get(summary); + } +} diff --git a/src/main/java/com/joestelmach/natty/IcsSearcher.java b/src/main/java/com/joestelmach/natty/IcsSearcher.java new file mode 100644 index 00000000..b74d9805 --- /dev/null +++ b/src/main/java/com/joestelmach/natty/IcsSearcher.java @@ -0,0 +1,92 @@ +package com.joestelmach.natty; + +import java.io.IOException; +import java.io.InputStream; +import java.text.ParseException; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.TimeZone; +import java.util.logging.Level; +import java.util.logging.Logger; + +import net.fortuna.ical4j.data.CalendarBuilder; +import net.fortuna.ical4j.data.ParserException; +import net.fortuna.ical4j.model.Component; +import net.fortuna.ical4j.model.DateTime; +import net.fortuna.ical4j.model.Period; +import net.fortuna.ical4j.model.PeriodList; + +public class IcsSearcher { + private static final String GMT = "GMT"; + private static final String VEVENT = "VEVENT"; + private static final String SUMMARY = "SUMMARY"; + private static final Logger _logger = Logger.getLogger("com.joestelmach.natty"); + private net.fortuna.ical4j.model.Calendar _holidayCalendar; + private String _calendarFileName; + private TimeZone _timeZone; + + public IcsSearcher(String calendarFileName, TimeZone timeZone) { + _calendarFileName = calendarFileName; + _timeZone = timeZone; + } + + public Map findDates(int startYear, int endYear, String eventSummary) { + Map holidays = new HashMap(); + + if(_holidayCalendar == null) { + InputStream fin = WalkerState.class.getResourceAsStream(_calendarFileName); + try { + _holidayCalendar = new CalendarBuilder().build(fin); + + } catch (IOException e) { + _logger.severe("Couln't open " + _calendarFileName); + return holidays; + + } catch (ParserException e) { + _logger.severe("Couln't parse " + _calendarFileName); + return holidays; + } + } + + Period period = null; + try { + DateTime from = new DateTime(startYear + "0101T000000Z"); + DateTime to = new DateTime(endYear + "1231T000000Z");; + period = new Period(from, to); + + } catch (ParseException e) { + _logger.log(Level.SEVERE, "Invalid start or end year: " + startYear + ", " + endYear, e); + return holidays; + } + + for (Object component : _holidayCalendar.getComponents(VEVENT)) { + Component vevent = (Component) component; + String summary = vevent.getProperty(SUMMARY).getValue(); + if(summary.equals(eventSummary)) { + PeriodList list = vevent.calculateRecurrenceSet(period); + for(Object p : list) { + DateTime date = ((Period) p).getStart(); + + // this date is at the date of the holiday at 12 AM UTC + Calendar utcCal = CalendarSource.getCurrentCalendar(); + utcCal.setTimeZone(TimeZone.getTimeZone(GMT)); + utcCal.setTime(date); + + // use the year, month and day components of our UTC date to form a new local date + Calendar localCal = CalendarSource.getCurrentCalendar(); + localCal.setTimeZone(_timeZone); + localCal.set(Calendar.YEAR, utcCal.get(Calendar.YEAR)); + localCal.set(Calendar.MONTH, utcCal.get(Calendar.MONTH)); + localCal.set(Calendar.DAY_OF_MONTH, utcCal.get(Calendar.DAY_OF_MONTH)); + + holidays.put(localCal.get(Calendar.YEAR), localCal.getTime()); + } + } + } + + return holidays; + } + +} diff --git a/src/main/java/com/joestelmach/natty/NattyTokenSource.java b/src/main/java/com/joestelmach/natty/NattyTokenSource.java new file mode 100644 index 00000000..df4095a6 --- /dev/null +++ b/src/main/java/com/joestelmach/natty/NattyTokenSource.java @@ -0,0 +1,27 @@ +package com.joestelmach.natty; + +import java.util.List; + +import org.antlr.runtime.Token; +import org.antlr.runtime.TokenSource; + +public class NattyTokenSource implements TokenSource { + private List _tokens; + private int _index = 0; + + public NattyTokenSource(List tokens) { + _tokens = tokens; + } + + public Token nextToken() { + return _tokens.size() > _index ? _tokens.get(_index++) : null; + } + + public String getSourceName() { + return "natty"; + } + + public List getTokens() { + return _tokens; + } +} diff --git a/src/main/java/com/joestelmach/natty/ParseListener.java b/src/main/java/com/joestelmach/natty/ParseListener.java index ba17bff8..18d1095b 100644 --- a/src/main/java/com/joestelmach/natty/ParseListener.java +++ b/src/main/java/com/joestelmach/natty/ParseListener.java @@ -2,7 +2,6 @@ import java.util.ArrayList; import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Stack; @@ -18,52 +17,24 @@ */ public class ParseListener extends BlankDebugEventListener { - private static final Map INTERESTING_RULES; - - static { - INTERESTING_RULES = new LinkedHashMap(); - INTERESTING_RULES.put("global_date_prefix", "date prefix"); - INTERESTING_RULES.put("relative_date", "relative date"); - INTERESTING_RULES.put("relaxed_date", "relaxed date"); - INTERESTING_RULES.put("formal_date", "formal date"); - INTERESTING_RULES.put("explicit_date", "explicit date"); - INTERESTING_RULES.put("relaxed_day_of_month", "day"); - INTERESTING_RULES.put("relaxed_month", "month"); - INTERESTING_RULES.put("relaxed_year", "year"); - INTERESTING_RULES.put("formal_month_of_year", "month"); - INTERESTING_RULES.put("formal_day_of_month", "day"); - INTERESTING_RULES.put("formal_year", "year"); - INTERESTING_RULES.put("relative_prefix", "relative prefix"); - INTERESTING_RULES.put("implicit_prefix", "implicit prefix"); - INTERESTING_RULES.put("relative_suffix", "relative suffix"); - INTERESTING_RULES.put("relative_target", "relative target"); - INTERESTING_RULES.put("relative_date_span", "span"); - INTERESTING_RULES.put("relative_occurrence_index", "relative occurrence index"); - INTERESTING_RULES.put("named_relative_date", "named relative date"); - INTERESTING_RULES.put("day_of_week", "weekday"); - INTERESTING_RULES.put("date", "date"); - INTERESTING_RULES.put("date_time_alternative", "alternative"); - INTERESTING_RULES.put("alternative_direction", "direction"); - INTERESTING_RULES.put("hours", "hours"); - INTERESTING_RULES.put("minutes", "minutes"); - INTERESTING_RULES.put("meridian_indicator", "am/pm"); - INTERESTING_RULES.put("time_zone", "zone"); - INTERESTING_RULES.put("time", "time"); - } - private int backtracking = 0; private Map>> _ruleMap; - private List _locations; + private Map> _locations; + private ParseLocation _dateGroupLocation; public ParseListener() { _ruleMap = new HashMap>>(); - _locations = new ArrayList(); + _locations = new HashMap>(); } - public List getLocations() { + public Map> getLocations() { return _locations; } + public ParseLocation getDateGroupLocation() { + return _dateGroupLocation; + } + // don't add backtracking or cyclic DFA nodes public void enterDecision(int d) { backtracking++; @@ -89,22 +60,36 @@ public void exitRule(String filename, String ruleName) { if (backtracking > 0) return; List tokenList = _ruleMap.get(ruleName).pop(); - if(tokenList.size() > 0 && INTERESTING_RULES.keySet().contains(ruleName)) { - + + if(tokenList.size() > 0) { + boolean isAlternative = ruleName.equals("date_time_alternative"); StringBuilder builder = new StringBuilder(); for(Token token:tokenList) { builder.append(token.getText()); } String text = builder.toString(); + int line = tokenList.get(0).getLine(); int start = tokenList.get(0).getCharPositionInLine(); int end = start + text.length(); - + ParseLocation location = new ParseLocation(); - location.setRuleName(INTERESTING_RULES.get(ruleName)); + location.setRuleName(ruleName); location.setText(text); + location.setLine(line); location.setStart(start); location.setEnd(end); - _locations.add(location); + + if(isAlternative) { + _dateGroupLocation = location; + } + + List list = _locations.get(location.getRuleName()); + if(list == null) { + list = new ArrayList(); + _locations.put(location.getRuleName(), list); + } + + list.add(location); } } diff --git a/src/main/java/com/joestelmach/natty/ParseLocation.java b/src/main/java/com/joestelmach/natty/ParseLocation.java index 6eed1101..3636d024 100644 --- a/src/main/java/com/joestelmach/natty/ParseLocation.java +++ b/src/main/java/com/joestelmach/natty/ParseLocation.java @@ -8,13 +8,13 @@ public class ParseLocation { private String _ruleName; private String _text; + private int _line; private int _start; private int _end; public String getRuleName() { return _ruleName; } - public void setRuleName(String ruleName) { _ruleName = ruleName; } @@ -22,15 +22,20 @@ public void setRuleName(String ruleName) { public String getText() { return _text; } - public void setText(String text) { _text = text; } + public int getLine() { + return _line; + } + public void setLine(int line) { + _line = line; + } + public int getStart() { return _start; } - public void setStart(int start) { _start = start; } @@ -38,8 +43,11 @@ public void setStart(int start) { public int getEnd() { return _end; } - public void setEnd(int end) { _end = end; } + + public String toString() { + return _text; + } } diff --git a/src/main/java/com/joestelmach/natty/ParseResult.java b/src/main/java/com/joestelmach/natty/ParseResult.java deleted file mode 100644 index bca598b4..00000000 --- a/src/main/java/com/joestelmach/natty/ParseResult.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.joestelmach.natty; - -import java.util.Date; -import java.util.List; - -/** - * Represents the result of a parse - * - * @author Joe Stelmach - */ -public class ParseResult { - - private List _dateTimes; - private List _parseLocations; - private String _syntaxTree; - - public List getDates() { - return _dateTimes; - } - - public void setDateTimes(List dateTimes) { - _dateTimes = dateTimes; - } - - public List getParseLocations() { - return _parseLocations; - } - - public void setParseLocations(List parseLocations) { - _parseLocations = parseLocations; - } - - public String getSyntaxTree() { - return _syntaxTree; - } - - public void setSyntaxTree(String syntaxTree) { - _syntaxTree = syntaxTree; - } -} diff --git a/src/main/java/com/joestelmach/natty/Parser.java b/src/main/java/com/joestelmach/natty/Parser.java index 0456ab19..76c0ff81 100644 --- a/src/main/java/com/joestelmach/natty/Parser.java +++ b/src/main/java/com/joestelmach/natty/Parser.java @@ -2,13 +2,19 @@ import java.io.ByteArrayInputStream; import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; import java.util.TimeZone; +import java.util.logging.ConsoleHandler; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.logging.SimpleFormatter; import org.antlr.runtime.ANTLRInputStream; import org.antlr.runtime.CommonTokenStream; -import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.Token; +import org.antlr.runtime.TokenStream; import org.antlr.runtime.tree.CommonTree; import org.antlr.runtime.tree.CommonTreeNodeStream; import org.antlr.runtime.tree.Tree; @@ -16,7 +22,6 @@ import com.joestelmach.natty.generated.DateLexer; import com.joestelmach.natty.generated.DateParser; import com.joestelmach.natty.generated.DateWalker; -import com.joestelmach.natty.generated.DebugDateParser; import com.joestelmach.natty.generated.TreeRewrite; /** @@ -26,9 +31,7 @@ public class Parser { private TimeZone _defaultTimeZone; - private boolean _debug; - private ParseListener _debugListener; - private static final Logger _logger = Logger.getLogger(Parser.class.getName()); + private static final Logger _logger = Logger.getLogger("com.joestelmach.natty"); /** * Creates a new parser using the given time zone as the default @@ -42,70 +45,272 @@ public Parser(TimeZone defaultTimeZone) { * Creates a new parser with no explicit default time zone (default will be US/Eastern) */ public Parser() { - _defaultTimeZone = TimeZone.getTimeZone("America/New_York"); + _defaultTimeZone = TimeZone.getDefault(); } /** - * Enables the collection of parse information - * @param debug + * Parses the given input value for one or more groups of + * date alternatives + * + * @param value + * @return */ - public void setDebug(final boolean debug) { - _debug = debug; + public List parse(String value) { + + // lex the input value to obtain our global token stream + ANTLRInputStream input = null; + try { + input = new ANTLRNoCaseInputStream(new ByteArrayInputStream(value.trim().getBytes())); + + } catch (IOException e) { + _logger.log(Level.SEVERE, "could not lex input", e); + } + DateLexer lexer = new DateLexer(input); + + // collect all sub-token streams that may include date information + List streams = collectTokenStreams(new CommonTokenStream(lexer)); + + // and parse each of them + List groups = new ArrayList(); + for(TokenStream stream:streams) { + List tokens = ((NattyTokenSource) stream.getTokenSource()).getTokens(); + DateGroup group = singleParse(stream); + while((group == null || group.getDates().size() == 0) && tokens.size() > 0) { + if(group == null || group.getDates().size() == 0) { + + // if we're down to only two tokens in our token stream, we can't continue + if(tokens.size() <= 2) { + tokens.clear(); + } + + // otherwise, we have two options: + else { + + // 1. Continuously remove tokens from the end of the stream and re-parse. This will + // recover from the case of an extaneous token at the end of the token stream. + // For example: 'june 20th on' + List endRemovedTokens = new ArrayList(tokens); + while((group == null || group.getDates().isEmpty()) && endRemovedTokens.size() > 2) { + endRemovedTokens = endRemovedTokens.subList(0, endRemovedTokens.size() - 1); + cleanupGroup(endRemovedTokens); + TokenStream newStream = new CommonTokenStream(new NattyTokenSource(endRemovedTokens)); + group = singleParse(newStream); + } + + // 2. Continuously look for another possible starting point in the token + // stream and re-parse. + if(group == null || group.getDates().isEmpty()) { + tokens = tokens.subList(1, tokens.size()); + Iterator iter = tokens.iterator(); + while(iter.hasNext()) { + Token token = iter.next(); + if(!DateParser.FOLLOW_empty_in_parse186.member(token.getType())) { + iter.remove(); + } + else { + break; + } + } + cleanupGroup(tokens); + TokenStream newStream = new CommonTokenStream(new NattyTokenSource(tokens)); + group = singleParse(newStream); + } + } + } + } + // if a group with some date(s) was found, we add it + if(group != null && group.getDates().size() > 0) { + groups.add(group); + } + } + return groups; } /** - * Parses the input string for a list of date times, assuming no - * extraneous text is present. - * @param inputString + * Parses the token stream for a SINGLE date time alternative. This + * method assumes that the entire token stream represents date and or + * time information (no extraneous tokens) + * + * @param stream * @return */ - public ParseResult parse(final String inputString) { - ParseResult result = new ParseResult(); + private DateGroup singleParse(TokenStream stream) { + DateGroup group = null; + List tokens = ((NattyTokenSource) stream.getTokenSource()).getTokens(); + if(tokens.isEmpty()) return group; + + StringBuilder tokenString = new StringBuilder(); + for(Token token:tokens) { + tokenString.append(DateParser.tokenNames[token.getType()]); + tokenString.append(" "); + } + _logger.fine("sub-token stream: " + tokenString.toString()); + try { - // lex - ANTLRInputStream input = new ANTLRNoCaseInputStream( - new ByteArrayInputStream(inputString.trim().getBytes())); - DateLexer lexer = new DateLexer(input); - CommonTokenStream tokens = new CommonTokenStream(lexer); + // parse + ParseListener listener = new ParseListener(); + DateParser parser = new DateParser(stream, listener); + DateParser.parse_return parseReturn = parser.parse(); - // parse with debug - Tree tree = null; - if(_debug) { - _debugListener = new ParseListener(); - DebugDateParser parser = new DebugDateParser(tokens, _debugListener); - DebugDateParser.parse_return parseReturn = parser.parse(); - tree = (Tree) parseReturn.getTree(); - result.setParseLocations(_debugListener.getLocations()); - } - - // or parse without debug - else { - DateParser parser = new DateParser(tokens); - DateParser.parse_return parseReturn = parser.parse(); - tree = (Tree) parseReturn.getTree(); - } - - // rewrite the tree (temporary fix for http://www.antlr.org/jira/browse/ANTLR-427) - CommonTreeNodeStream nodes = new CommonTreeNodeStream(tree); - TreeRewrite s = new TreeRewrite(nodes); - tree = (CommonTree)s.downup(tree); - result.setSyntaxTree(tree.toStringTree()); + Tree tree = (Tree) parseReturn.getTree(); + _logger.fine("AST: " + tree.toStringTree()); - // and walk it - nodes = new CommonTreeNodeStream(tree); - nodes.setTokenStream(tokens); - DateWalker walker = new DateWalker(nodes); - walker.getState().setDefaultTimeZone(_defaultTimeZone); - walker.date_time_alternative(); - result.setDateTimes(walker.getState().getDateTimes()); + // we only continue if a meaningful syntax tree has been built + if(tree.getChildCount() > 0) { - } catch(IOException e) { - _logger.log(Level.SEVERE, "Could not read from input stream", e); + // rewrite the tree (temporary fix for http://www.antlr.org/jira/browse/ANTLR-427) + CommonTreeNodeStream nodes = new CommonTreeNodeStream(tree); + TreeRewrite s = new TreeRewrite(nodes); + tree = (CommonTree)s.downup(tree); + + // and walk it + nodes = new CommonTreeNodeStream(tree); + nodes.setTokenStream(stream); + DateWalker walker = new DateWalker(nodes); + walker.getState().setDefaultTimeZone(_defaultTimeZone); + walker.parse(); + + // run through the results and append the parse information + group = walker.getState().getDateGroup(); + ParseLocation location = listener.getDateGroupLocation(); + group.setLine(location.getLine()); + group.setText(location.getText()); + group.setPosition(location.getStart()); + group.setSyntaxTree(tree); + group.setParseLocations(listener.getLocations()); + } - } catch(RecognitionException e) { + } catch(Exception e) { _logger.log(Level.SEVERE, "Could not parse input", e); } - return result; + return group; + } + + /** + * Scans the given token global token stream for a list of sub-token + * streams representing those portions of the global stream that + * may contain date time information + * + * @param stream + * @return + */ + private List collectTokenStreams(TokenStream stream) { + + // walk through the token stream and build a collection + // of sub token streams that represent possible date locations + List currentGroup = null; + List groups = new ArrayList(); + Token currentToken; + StringBuilder tokenString = new StringBuilder(); + while((currentToken = stream.getTokenSource().nextToken()).getType() != DateLexer.EOF) { + if(_logger.getLevel() != null && _logger.getLevel().intValue() <= Level.FINE.intValue()) { + tokenString.append(DateParser.tokenNames[currentToken.getType()]); + tokenString.append(" "); + } + + // we're currently NOT collecting for a possible date group + if(currentGroup == null) { + // ignore white space in-between possible rules + if(currentToken.getType() != DateLexer.WHITE_SPACE) { + // if the token is a possible date start token, we start a new collection + if(DateParser.FOLLOW_empty_in_parse186.member(currentToken.getType()) || currentToken.getType() == DateLexer.UNKNOWN) { + currentGroup = new ArrayList(); + currentGroup.add(currentToken); + } + } + } + // we're currently collecting + else { + // preserve white space + if(currentToken.getType() == DateLexer.WHITE_SPACE) { + currentGroup.add(currentToken); + } + else { + // if this is an unknown token, we need to end the current group + if(currentToken.getType() == DateLexer.UNKNOWN) { + if(currentGroup.size() > 0) { + currentGroup.add(currentToken); + cleanupGroup(currentGroup); + groups.add(new CommonTokenStream(new NattyTokenSource(currentGroup))); + } + currentGroup = null; + } + // otherwise, the token is known and we're currently collecting for + // a group, we add it if it's not a dot + else if(currentToken.getType() != DateLexer.DOT) { + currentGroup.add(currentToken); + } + } + } + } + if(currentGroup != null) { + cleanupGroup(currentGroup); + groups.add(new CommonTokenStream(new NattyTokenSource(currentGroup))); + } + + _logger.fine("global token stream: " + tokenString.toString()); + + return groups; + } + + /** + * Removes unwanted tokens from the given token group + * @param group + */ + private void cleanupGroup(List group) { + + // remove contiguous white space + Iterator iter = group.iterator(); + Token previousToken = null; + while(iter.hasNext()) { + Token token = iter.next(); + if(previousToken != null && previousToken.getType() == DateParser.WHITE_SPACE) { + if(token.getType() == DateParser.WHITE_SPACE) { + iter.remove(); + } + } + previousToken = token; + } + + // remove leading white space + if(group.size() > 0) { + boolean skip = false; + Iterator it1 = group.iterator(); + while(it1.hasNext()) { + Token tk = it1.next(); + if(tk.getType() == DateParser.WHITE_SPACE) { + it1.remove(); + skip = false; + } else if(tk.getType() == DateParser.UNKNOWN) { + it1.remove(); + skip = true; + } else if(skip) { + it1.remove(); + } else if(!DateParser.FOLLOW_empty_in_parse186.member(tk.getType())) { + it1.remove(); + } else break; + } + } + + // and trailing white space + if(group.size() > 0) { + boolean skip = false; + while(group.size() > 0) { + Token lastToken = group.get(group.size() - 1); + if(lastToken.getType() == DateParser.WHITE_SPACE) { + group.remove(lastToken); + skip = false; + } + else if(lastToken.getType() == DateParser.UNKNOWN) { + group.remove(lastToken); + skip = true; + } + else if(skip) { + group.remove(lastToken); + } + else break; + } + } } } diff --git a/src/main/java/com/joestelmach/natty/Season.java b/src/main/java/com/joestelmach/natty/Season.java new file mode 100644 index 00000000..b0f5f8a9 --- /dev/null +++ b/src/main/java/com/joestelmach/natty/Season.java @@ -0,0 +1,33 @@ +package com.joestelmach.natty; + +import java.util.HashMap; +import java.util.Map; + +public enum Season { + WINTER("Winter Solstice"), + SPRING("Vernal Equinox"), + SUMMER("Summer Solstice"), + FALL("Autumnal Equinox"); + + private String summary; + private static final Map lookup; + static { + lookup = new HashMap(); + for(Season h:values()) { + lookup.put(h.getSummary(), h); + } + } + + Season(String summary) { + this.summary = summary; + } + + public String getSummary() { + return summary; + } + + public static Season fromSummary(String summary) { + if(summary == null) return null; + return lookup.get(summary); + } +} diff --git a/src/main/java/com/joestelmach/natty/WalkerState.java b/src/main/java/com/joestelmach/natty/WalkerState.java index a4d38b4b..7a945487 100644 --- a/src/main/java/com/joestelmach/natty/WalkerState.java +++ b/src/main/java/com/joestelmach/natty/WalkerState.java @@ -1,33 +1,62 @@ package com.joestelmach.natty; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.List; -import java.util.TimeZone; +import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; /** * @author Joe Stelmach */ public class WalkerState { + + private static final int TWO_DIGIT_YEAR_CENTURY_THRESHOLD = 20; + private static final String MONTH = "month"; + private static final String DAY = "day"; + private static final String YEAR = "year"; + private static final String WEEK = "week"; + private static final String HOUR = "hour"; + private static final String MINUTE = "minute"; + private static final String SECOND = "second"; + private static final String AM = "am"; + private static final String PM = "pm"; + private static final String DIR_LEFT = "<"; + private static final String DIR_RIGHT = ">"; + private static final String SEEK_PREFIX = "by_"; + private static final String SEEK_BY_DAY = "by_day"; + private static final String SEEK_BY_WEEK = "by_week"; + private static final String PLUS = "+"; + private static final String MINUS = "-"; + private static final String GMT = "GMT"; + private static final String VEVENT = "VEVENT"; + private static final String SUMMARY = "SUMMARY"; + private static final String HOLIDAY_ICS_FILE = "/holidays.ics"; + private static final String SEASON_ICS_FILE = "/seasons.ics"; + private static final Logger _logger = Logger.getLogger("com.joestelmach.natty"); + private GregorianCalendar _calendar; private TimeZone _defaultTimeZone; private int _currentYear; - private static final int TWO_DIGIT_YEAR_CENTURY_THRESHOLD = 20; - private List _currentDateTimes; - + private boolean _firstDateInvocationInGroup = true; + private boolean _timeGivenInGroup = false; + private boolean _dateGivenInGroup = false; + private boolean _updatePreviousDates = false; + private DateGroup _dateGroup; + + + private static final Logger log = Logger.getLogger(WalkerState.class.getName()); + /** * Creates a new WalkerState representing the start of * the next hour from the current time */ public WalkerState() { - resetCalender(); - _currentDateTimes = new ArrayList(); + resetCalendar(); + _dateGroup = new DateGroup(); } public void setDefaultTimeZone(final TimeZone zone) { _defaultTimeZone = zone; + resetCalendar(); } /** @@ -43,7 +72,7 @@ public void setDefaultTimeZone(final TimeZone zone) { * next (or previous,) week (or multiple of next or previous week depending * on the seek amount.) * - * @param amount the amount to seek. Must be guaranteed to parse as an integer + * @param seekAmount the amount to seek. Must be guaranteed to parse as an integer * * @param dayOfWeek the day of the week to seek to, represented as an integer from * 1 to 7 (1 being Sunday, 7 being Saturday.) Must be guaranteed to parse as an Integer @@ -51,19 +80,22 @@ public void setDefaultTimeZone(final TimeZone zone) { public void seekToDayOfWeek(String direction, String seekType, String seekAmount, String dayOfWeek) { int dayOfWeekInt = Integer.parseInt(dayOfWeek); int seekAmountInt = Integer.parseInt(seekAmount); - assert(direction.equals("<") || direction.equals(">")); - assert(seekType.equals("by_day") || seekType.equals("by_week")); + assert(direction.equals(DIR_LEFT) || direction.equals(DIR_RIGHT)); + assert(seekType.equals(SEEK_BY_DAY) || seekType.equals(SEEK_BY_WEEK)); assert(dayOfWeekInt >= 1 && dayOfWeekInt <= 7); - int sign = direction.equals(">") ? 1 : -1; - if(seekType.equals("by_week")) { + markDateInvocation(); + + int sign = direction.equals(DIR_RIGHT) ? 1 : -1; + if(seekType.equals(SEEK_BY_WEEK)) { // set our calendar to this weeks requested day of the week, // then add or subtract the week(s) _calendar.set(Calendar.DAY_OF_WEEK, dayOfWeekInt); _calendar.add(Calendar.DAY_OF_YEAR, seekAmountInt * 7 * sign); + } - else if(seekType.equals("by_day")) { + else if(seekType.equals(SEEK_BY_DAY)) { // find the closest day do { _calendar.roll(Calendar.DAY_OF_YEAR, sign); @@ -86,25 +118,41 @@ else if(seekType.equals("by_day")) { public void seekToDayOfMonth(String dayOfMonth) { int dayOfMonthInt = Integer.parseInt(dayOfMonth); assert(dayOfMonthInt >= 1 && dayOfMonthInt <= 31); + + markDateInvocation(); + dayOfMonthInt = Math.min(dayOfMonthInt, _calendar.getActualMaximum(Calendar.DAY_OF_MONTH)); _calendar.set(Calendar.DAY_OF_MONTH, dayOfMonthInt); } + /** + * Seeks to the given day within the current year + * @param dayOfYear the day of the year to seek to, represented as an integer + * from 1 to 366. Must be guaranteed to parse as an Integer. If this day is + * beyond the last day of the current year, the actual last day of the year + * will be used. + */ + public void seekToDayOfYear(String dayOfYear) { + int dayOfYearInt = Integer.parseInt(dayOfYear); + assert(dayOfYearInt >= 1 && dayOfYearInt <= 366); + + markDateInvocation(); + + dayOfYearInt = Math.min(dayOfYearInt, _calendar.getActualMaximum(Calendar.DAY_OF_YEAR)); + _calendar.set(Calendar.DAY_OF_YEAR, dayOfYearInt); + } + /** * * @param year */ public void seekToYear(String year) { int yearInt = Integer.parseInt(year); - assert(yearInt > 0 && yearInt < 9999); - - // two digit years require us to choose a reasonable century. - if(year.length() == 2) { - int century = (yearInt > ((_currentYear - 2000) + TWO_DIGIT_YEAR_CENTURY_THRESHOLD)) ? 1900 : 2000; - yearInt = yearInt + century; - } + assert(yearInt >= 0 && yearInt < 9999); - _calendar.set(Calendar.YEAR, yearInt); + markDateInvocation(); + + _calendar.set(Calendar.YEAR, getFullYear(yearInt)); } /** @@ -114,7 +162,7 @@ public void seekToYear(String year) { * '<' go backward * '>' go forward * - * @param amount the amount to seek. Must be guaranteed to parse as an integer + * @param seekAmount the amount to seek. Must be guaranteed to parse as an integer * * @param month the month to seek to. Must be guaranteed to parse as an integer * between 1 and 12 @@ -122,20 +170,24 @@ public void seekToYear(String year) { public void seekToMonth(String direction, String seekAmount, String month) { int seekAmountInt = Integer.parseInt(seekAmount); int monthInt = Integer.parseInt(month); - assert(direction.equals("<") || direction.equals(">")); + assert(direction.equals(DIR_LEFT) || direction.equals(DIR_RIGHT)); assert(monthInt >= 1 && monthInt <= 12); + markDateInvocation(); + // set the day to the first of month. This step is necessary because if we seek to the // current day of a month whose number of days is less than the current day, we will // pushed into the next month. _calendar.set(Calendar.DAY_OF_MONTH, 1); // seek to the appropriate year - int currentMonth = _calendar.get(Calendar.MONTH) + 1; - int sign = direction.equals(">") ? 1 : -1; - int numYearsToShift = seekAmountInt + - (currentMonth <= monthInt ? sign > 0 ? -1 : 0 : sign > 0 ? 0 : -1); - _calendar.add(Calendar.YEAR, (numYearsToShift * sign)); + if(seekAmountInt > 0) { + int currentMonth = _calendar.get(Calendar.MONTH) + 1; + int sign = direction.equals(DIR_RIGHT) ? 1 : -1; + int numYearsToShift = seekAmountInt + + (currentMonth <= monthInt ? sign > 0 ? -1 : 0 : sign > 0 ? 0 : -1); + _calendar.add(Calendar.YEAR, (numYearsToShift * sign)); + } // now set the month _calendar.set(Calendar.MONTH, monthInt -1); @@ -148,22 +200,32 @@ public void seekToMonth(String direction, String seekAmount, String month) { * '<' go backward * '>' go forward * - * @param amount the amount to seek. Must be guaranteed to parse as an integer + * @param seekAmount the amount to seek. Must be guaranteed to parse as an integer * * @param span */ public void seekBySpan(String direction, String seekAmount, String span) { - if(span.startsWith("by_")) span = span.substring(3); + if(span.startsWith(SEEK_PREFIX)) span = span.substring(3); int seekAmountInt = Integer.parseInt(seekAmount); - assert(direction.equals("<") || direction.equals(">")); - assert(span.equals("day") || span.equals("week") || span.equals("month") || span.equals("year")); + assert(direction.equals(DIR_LEFT) || direction.equals(DIR_RIGHT)); + assert(span.equals(DAY) || span.equals(WEEK) || span.equals(MONTH) || + span.equals(YEAR) || span.equals(HOUR) || span.equals(MINUTE) || + span.equals(SECOND)); - int sign = direction.equals(">") ? 1 : -1; + boolean isDateSeek = span.equals(DAY) || span.equals(WEEK) || + span.equals(MONTH) || span.equals(YEAR); + if(isDateSeek) markDateInvocation(); else markTimeInvocation(); + + + int sign = direction.equals(DIR_RIGHT) ? 1 : -1; int field = - span.equals("day") ? Calendar.DAY_OF_YEAR : - span.equals("week") ? Calendar.WEEK_OF_YEAR : - span.equals("month") ? Calendar.MONTH : - span.equals("year") ? Calendar.YEAR : + span.equals(DAY) ? Calendar.DAY_OF_YEAR : + span.equals(WEEK) ? Calendar.WEEK_OF_YEAR : + span.equals(MONTH) ? Calendar.MONTH : + span.equals(YEAR) ? Calendar.YEAR : + span.equals(HOUR) ? Calendar.HOUR: + span.equals(MINUTE) ? Calendar.MINUTE: + span.equals(SECOND) ? Calendar.SECOND: null; if(field > 0) _calendar.add(field, seekAmountInt * sign); } @@ -172,7 +234,6 @@ public void seekBySpan(String direction, String seekAmount, String span) { * * @param index * @param dayOfWeek - * @param month */ public void setDayOfWeekIndex(String index, String dayOfWeek) { int indexInt = Integer.parseInt(index); @@ -181,6 +242,8 @@ public void setDayOfWeekIndex(String index, String dayOfWeek) { int dayOfWeekInt = Integer.parseInt(dayOfWeek); assert(dayOfWeekInt >= 1 && dayOfWeekInt <= 7); + markDateInvocation(); + // seek to the first day of the current month _calendar.set(Calendar.DAY_OF_MONTH, 1); @@ -225,13 +288,17 @@ public void setExplicitDate(String month, String dayOfMonth, String dayOfWeek, S int dayOfMonthInt = Integer.parseInt(dayOfMonth); assert(dayOfMonthInt > 0 && dayOfMonthInt <= 31); + markDateInvocation(); + + _calendar.set(Calendar.MONTH, monthInt - 1); _calendar.set(Calendar.DAY_OF_MONTH, dayOfMonthInt); if(year != null) { seekToYear(year); } - + + // if no year is given, but a day of week is, we ensure that the resulting // date falls on the given day of week. else if(dayOfWeek != null) { @@ -242,7 +309,21 @@ else if(dayOfWeek != null) { } } } - + + /** + * + * @param year the year to set. If present, must be guaranteed to + * parse as an integer with 4 digits (xxxx) between 0 and 9999 + */ + public void setExplicitYearOnlyDate(String year) { +// debug("BEFORE year: %s, month: %s, day: %s\n", _calendar.get(Calendar.YEAR), _calendar.get(Calendar.MONTH), _calendar.get(Calendar.DAY_OF_MONTH)); + seekToYear(year); //set the year + //WARNING: Months needs to be set AFTER the YEAR is set - RB + _calendar.set(Calendar.MONTH, Calendar.JANUARY); + _calendar.set(Calendar.DAY_OF_MONTH, 1); + debug("AFTER year: %s, month: %s, day: %s\n", _calendar.get(Calendar.YEAR), _calendar.get(Calendar.MONTH), _calendar.get(Calendar.DAY_OF_MONTH)); + } + /** * Sets the the time of day * @@ -257,16 +338,20 @@ else if(dayOfWeek != null) { * * @param amPm the meridian indicator to use. Must be either 'am' or 'pm' * - * @param zone the time zone to use in one of two formats: + * @param zoneString the time zone to use in one of two formats: * - zoneinfo format (America/New_York, America/Los_Angeles, etc) * - GMT offset (+05:00, -0500, +5, etc) */ public void setExplicitTime(String hours, String minutes, String seconds, String amPm, String zoneString) { int hoursInt = Integer.parseInt(hours); int minutesInt = Integer.parseInt(minutes); - assert(amPm == null || amPm.equals("am") || amPm.equals("pm")); + assert(amPm == null || amPm.equals(AM) || amPm.equals(PM)); assert(hoursInt >= 0 && hoursInt <= 23); assert(minutesInt >= 0 && minutesInt < 60); + + debug("setExplicitTime: %s, %s %s, %s, %s", hours, minutes, seconds, amPm, zoneString); + + markTimeInvocation(); // reset milliseconds to 0 _calendar.set(Calendar.MILLISECOND, 0); @@ -274,8 +359,8 @@ public void setExplicitTime(String hours, String minutes, String seconds, String // if no explicit zone is given, we use our own TimeZone zone = null; if(zoneString != null) { - if(zoneString.startsWith("+") || zoneString.startsWith("-")) { - zoneString = "GMT" + zoneString; + if(zoneString.startsWith(PLUS) || zoneString.startsWith(MINUS)) { + zoneString = GMT + zoneString; } zone = TimeZone.getTimeZone(zoneString); } @@ -286,8 +371,8 @@ public void setExplicitTime(String hours, String minutes, String seconds, String // hours greater than 12 are in 24-hour time if(hoursInt <= 12) { int amPmInt = amPm == null ? - (hoursInt > 12 ? Calendar.PM : Calendar.AM) : - amPm.equals("pm") ? Calendar.PM : Calendar.AM; + (hoursInt >= 12 ? Calendar.PM : Calendar.AM) : + amPm.equals(PM) ? Calendar.PM : Calendar.AM; _calendar.set(Calendar.AM_PM, amPmInt); // calendar is whacky at 12 o'clock (must use 0) @@ -300,31 +385,243 @@ public void setExplicitTime(String hours, String minutes, String seconds, String assert(secondsInt >= 0 && secondsInt < 60); _calendar.set(Calendar.SECOND, secondsInt); } + else { + _calendar.set(Calendar.SECOND, 0); + } _calendar.set(Calendar.MINUTE, minutesInt); } + /** + * Seeks forward or backwards to a particular holiday based on the current date + * + * @param holidayString The holiday to seek to + * @param direction The direction to seek + * @param seekAmount The number of years to seek + */ + public void seekToHoliday(String holidayString, String direction, String seekAmount) { + Holiday holiday = Holiday.valueOf(holidayString); + assert(holiday != null); + + seekToIcsEvent(HOLIDAY_ICS_FILE, holiday.getSummary(), direction, seekAmount); + } + + /** + * Seeks to the given holiday within the given year + * + * @param holidayString + * @param yearString + */ + public void seekToHolidayYear(String holidayString, String yearString) { + Holiday holiday = Holiday.valueOf(holidayString); + assert(holiday != null); + + seekToIcsEventYear(HOLIDAY_ICS_FILE, yearString, holiday.getSummary()); + } + + /** + * Seeks forward or backwards to a particular season based on the current date + * + * @param seasonString The season to seek to + * @param direction The direction to seek + * @param seekAmount The number of years to seek + */ + public void seekToSeason(String seasonString, String direction, String seekAmount) { + Season season = Season.valueOf(seasonString); + assert(season!= null); + + seekToIcsEvent(SEASON_ICS_FILE, season.getSummary(), direction, seekAmount); + } + + /** + * Seeks to the given season within the given year + * + * @param seasonString + * @param yearString + */ + public void seekToSeasonYear(String seasonString, String yearString) { + Season season = Season.valueOf(seasonString); + assert(season != null); + + seekToIcsEventYear(SEASON_ICS_FILE, yearString, season.getSummary()); + } + + /** + * + */ + public void setRecurring() { + _dateGroup.setRecurring(true); + } + /** * */ public void captureDateTime() { + // if other dates have already been added to the date group, we'll + // update their date portion to match this one + if(_updatePreviousDates) { + List dates = _dateGroup.getDates(); + if (!dates.isEmpty()) { + for (Date date : dates) { + Calendar calendar = getCalendar(); + calendar.setTime(date); + for (int field : new int[] { Calendar.DAY_OF_MONTH, Calendar.MONTH, Calendar.YEAR }) { + calendar.set(field, _calendar.get(field)); + } + date.setTime(calendar.getTimeInMillis()); + } + } + _updatePreviousDates = false; + } + Date date = _calendar.getTime(); - _currentDateTimes.add(date); - resetCalender(); + if(_dateGroup.isRecurring()) { + _dateGroup.setRecurringUntil(date); + } + else { + _dateGroup.addDate(date); + } + _firstDateInvocationInGroup = true; } /** * @return the list of date times found */ - public List getDateTimes() { - return _currentDateTimes; + public DateGroup getDateGroup() { + return _dateGroup; + } + + /** + * Clears any date/times that have been captured + */ + public void clearDateGroup() { + _dateGroup = new DateGroup(); } /** * Resets the calendar */ - private void resetCalender() { - _calendar = new GregorianCalendar(); + private void resetCalendar() { + _calendar = getCalendar(); + if (_defaultTimeZone != null) { + _calendar.setTimeZone(_defaultTimeZone); + } _currentYear = _calendar.get(Calendar.YEAR); } + + private void seekToIcsEvent(String icsFileName, String eventSummary, String direction, String seekAmount) { + int seekAmountInt = Integer.parseInt(seekAmount); + assert(direction.equals(DIR_LEFT) || direction.equals(DIR_RIGHT)); + assert(seekAmountInt >= 0); + + markDateInvocation(); + + // get the current year + Calendar cal = getCalendar(); + cal.setTimeZone(_defaultTimeZone); + int currentYear = cal.get(Calendar.YEAR); + + // look up a suitable period of occurrences + boolean forwards = direction.equals(DIR_RIGHT); + int startYear = forwards ? currentYear : currentYear - seekAmountInt - 1; + int endYear = forwards ? currentYear + seekAmountInt + 1 : currentYear; + Map dates = getDatesFromIcs(icsFileName, eventSummary, + startYear, endYear); + + // grab the right one + boolean hasPassed = cal.getTime().after(dates.get(currentYear)); + int targetYear = currentYear + + (forwards ? seekAmountInt + (hasPassed ? 0 : -1) : + (seekAmountInt - (hasPassed ? 1 : 0)) * -1); + + cal.setTimeZone(_calendar.getTimeZone()); + cal.setTime(dates.get(targetYear)); + _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)); + } + + private void seekToIcsEventYear(String icsFileName, String yearString, String eventSummary) { + int yearInt = Integer.parseInt(yearString); + assert(yearInt >= 0); + + markDateInvocation(); + + int year = getFullYear(yearInt); + Map dates = getDatesFromIcs(icsFileName, eventSummary, year, year); + Date date = dates.get(year - (eventSummary.equals(Holiday.NEW_YEARS_EVE.getSummary()) ? 1 : 0)); + + if(date != null) { + Calendar cal = getCalendar(); + cal.setTimeZone(_calendar.getTimeZone()); + cal.setTime(date); + _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)); + } + } + + /** + * ensures that the first invocation of a date seeking + * rule is captured + */ + private void markDateInvocation() { + + _updatePreviousDates = !_dateGivenInGroup; + _dateGivenInGroup = true; + + if(_firstDateInvocationInGroup) { + // if a time has been given within the current date group, + // we capture the current time before resetting the calendar + if(_timeGivenInGroup) { + int hours = _calendar.get(Calendar.HOUR_OF_DAY); + int minutes = _calendar.get(Calendar.MINUTE); + int seconds = _calendar.get(Calendar.SECOND); + resetCalendar(); + _calendar.set(Calendar.HOUR_OF_DAY, hours); + _calendar.set(Calendar.MINUTE, minutes); + _calendar.set(Calendar.SECOND, seconds); + } + else { + resetCalendar(); + } + _firstDateInvocationInGroup = false; + } + } + + /** + * + */ + private void markTimeInvocation() { + _timeGivenInGroup = true; + _dateGroup.setIsTimeInferred(false); + } + + private Map getDatesFromIcs(String icsFileName, + String eventSummary, int startYear, int endYear) { + IcsSearcher searcher = new IcsSearcher(icsFileName, _defaultTimeZone); + return searcher.findDates(startYear, endYear, eventSummary); + } + + private int getFullYear(Integer year) { + int result = year; + + if(year.toString().length() <= 2) { + int century = (year > ((_currentYear - 2000) + TWO_DIGIT_YEAR_CENTURY_THRESHOLD)) ? 1900 : 2000; + result = year + century; + } + + return result; + } + + /** + * @return the current calendar + */ + protected GregorianCalendar getCalendar() { + return CalendarSource.getCurrentCalendar(); + } + + private void debug(String msg, Object... params) { + _logger.finest(String.format(msg, params)); + } } \ No newline at end of file diff --git a/src/main/resources/holidays.ics b/src/main/resources/holidays.ics new file mode 100644 index 00000000..0e50d77f --- /dev/null +++ b/src/main/resources/holidays.ics @@ -0,0 +1,3316 @@ +BEGIN:VCALENDAR +COMMENT:http://www.mozilla.org/projects/calendar/caldata/USHolidays.ics +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +VERSION:2.0 +BEGIN:VEVENT +CREATED:20080827T223540Z +LAST-MODIFIED:20090611T212419Z +UID:b8388cae-2125-4614-9801-5fec40a07408 +SUMMARY:Easter Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000423 +DTEND;VALUE=DATE:20000424 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T223613Z +LAST-MODIFIED:20090611T212419Z +UID:27198947-51d4-475c-9116-6dd475ab4729 +SUMMARY:Easter Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20010415 +DTEND;VALUE=DATE:20010416 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T223633Z +LAST-MODIFIED:20090611T212419Z +UID:adebaeec-b7da-4d1e-baff-73e54a5bbe5d +SUMMARY:Easter Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20020331 +DTEND;VALUE=DATE:20020401 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T223650Z +LAST-MODIFIED:20090611T212419Z +UID:3378f42f-00f3-42be-888e-8368d5e140b2 +SUMMARY:Easter Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20030420 +DTEND;VALUE=DATE:20030421 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T223704Z +LAST-MODIFIED:20090611T212419Z +UID:18eae8ac-65e1-4cc0-8bf3-0c287882fbd9 +SUMMARY:Easter Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20040411 +DTEND;VALUE=DATE:20040412 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T223726Z +LAST-MODIFIED:20090611T212419Z +UID:d0a68adf-3eca-4585-a7a7-853b5d5b03e4 +SUMMARY:Easter Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20050327 +DTEND;VALUE=DATE:20050328 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T223741Z +LAST-MODIFIED:20090611T212419Z +UID:daf76dce-468b-4f99-8d42-01487f8fafa9 +SUMMARY:Easter Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20060416 +DTEND;VALUE=DATE:20060417 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T223757Z +LAST-MODIFIED:20090611T212419Z +UID:fa7a1db3-1c5f-472d-9ee4-988d0d0287eb +SUMMARY:Easter Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20070408 +DTEND;VALUE=DATE:20070409 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T223818Z +LAST-MODIFIED:20090611T212419Z +UID:69230f5f-f2d2-428d-a90c-a7e1c7deea5e +SUMMARY:Easter Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20090412 +DTEND;VALUE=DATE:20090413 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T223828Z +LAST-MODIFIED:20090611T212419Z +UID:0bc494ff-69b5-4dca-8699-c4fe5ea25a13 +SUMMARY:Easter Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20100404 +DTEND;VALUE=DATE:20100405 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T223934Z +LAST-MODIFIED:20090611T212419Z +UID:44a937c0-f0d8-4458-947c-8a33db7217ef +SUMMARY:Easter Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20120408 +DTEND;VALUE=DATE:20120409 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T223946Z +LAST-MODIFIED:20090611T212419Z +UID:734e6d03-ab3c-4693-8c81-f73e3b576c27 +SUMMARY:Easter Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20130331 +DTEND;VALUE=DATE:20130401 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T224000Z +LAST-MODIFIED:20090611T212419Z +UID:c4d6a9e0-a5d6-41d4-9297-97e4fdcc85a5 +SUMMARY:Easter Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20140420 +DTEND;VALUE=DATE:20140421 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T224012Z +LAST-MODIFIED:20090611T212419Z +UID:b2cab02a-a164-44a4-af66-ccb1b3c253b9 +SUMMARY:Easter Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20150405 +DTEND;VALUE=DATE:20150406 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T224028Z +LAST-MODIFIED:20090611T212419Z +UID:763686f7-3ec4-4dfc-9a33-e4c0bae652fa +SUMMARY:Easter Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20160327 +DTEND;VALUE=DATE:20160328 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T224043Z +LAST-MODIFIED:20090611T212419Z +UID:31edb1e8-a064-4a8b-a041-eecd22a4d419 +SUMMARY:Easter Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20170416 +DTEND;VALUE=DATE:20170417 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T224101Z +LAST-MODIFIED:20090611T212419Z +UID:d869e442-0b4d-40c4-b34c-6780afce8c46 +SUMMARY:Easter Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20180401 +DTEND;VALUE=DATE:20180402 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T224121Z +LAST-MODIFIED:20090611T212419Z +UID:135738b2-b8d5-4e65-bf7c-736e22c6b8b1 +SUMMARY:Easter Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20190421 +DTEND;VALUE=DATE:20190422 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T224133Z +LAST-MODIFIED:20090611T212419Z +UID:7606494f-0019-46e6-9d47-ab9498569e5a +SUMMARY:Easter Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20200412 +DTEND;VALUE=DATE:20200413 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212419Z +UID:c5c154cc-1dd1-11b2-85c7-e3de2bfad30f +SUMMARY:Easter Sunday +STATUS:CONFIRMED +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20080323 +DTEND;VALUE=DATE:20080324 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T024422Z +LAST-MODIFIED:20090611T212419Z +UID:5e604c83-cacc-46fe-bc40-b4f50e2412e7 +SUMMARY:Ash Wednesday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000308 +DTEND;VALUE=DATE:20000309 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T024533Z +LAST-MODIFIED:20090611T212419Z +UID:847021ce-03b6-4f2d-abe5-2ba869066b5d +SUMMARY:Ash Wednesday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20010228 +DTEND;VALUE=DATE:20010301 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T024626Z +LAST-MODIFIED:20090611T212419Z +UID:0e313bdb-171d-40dd-9ddb-1dfb4a777bb5 +SUMMARY:Ash Wednesday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20020213 +DTEND;VALUE=DATE:20020214 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T024822Z +LAST-MODIFIED:20090611T212419Z +UID:1577ed41-e8c5-45f9-83e5-86079fe2510b +SUMMARY:Ash Wednesday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20030305 +DTEND;VALUE=DATE:20030306 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T024847Z +LAST-MODIFIED:20090611T212419Z +UID:fcbfe576-82e9-4f68-b1ad-5366ff6968ac +SUMMARY:Ash Wednesday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20040225 +DTEND;VALUE=DATE:20040226 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T024911Z +LAST-MODIFIED:20090611T212419Z +UID:a403894c-bb2c-4d02-8dca-91c359a54bfe +SUMMARY:Ash Wednesday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20050209 +DTEND;VALUE=DATE:20050210 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T024924Z +LAST-MODIFIED:20090611T212419Z +UID:dcc3c2f4-d214-49d8-bb3e-f13c6acf3295 +SUMMARY:Ash Wednesday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20060301 +DTEND;VALUE=DATE:20060302 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T024939Z +LAST-MODIFIED:20090611T212419Z +UID:b81a6c71-9a3d-49a5-b695-8ac5f1bb013b +SUMMARY:Ash Wednesday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20070221 +DTEND;VALUE=DATE:20070222 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T024951Z +LAST-MODIFIED:20090611T212419Z +UID:40090c1d-8669-4d7a-b63a-288006cfac65 +SUMMARY:Ash Wednesday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20080206 +DTEND;VALUE=DATE:20080207 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025003Z +LAST-MODIFIED:20090611T212419Z +UID:609ce29c-966c-40b4-81c4-8ccac74e98dd +SUMMARY:Ash Wednesday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20090225 +DTEND;VALUE=DATE:20090226 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025017Z +LAST-MODIFIED:20090611T212419Z +UID:f96c0bf5-1cf2-4d2e-9955-eb89f4e645d3 +SUMMARY:Ash Wednesday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20100217 +DTEND;VALUE=DATE:20100218 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025046Z +LAST-MODIFIED:20090611T212419Z +UID:a0b949a8-2a75-4617-a687-1e7dffc098f5 +SUMMARY:Ash Wednesday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20120222 +DTEND;VALUE=DATE:20120223 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025058Z +LAST-MODIFIED:20090611T212419Z +UID:8caed879-6158-477c-9901-e1531dc855e8 +SUMMARY:Ash Wednesday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20130213 +DTEND;VALUE=DATE:20130214 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025131Z +LAST-MODIFIED:20090611T212419Z +UID:69e06d74-9acc-4667-8eb0-7c33299d82c0 +SUMMARY:Ash Wednesday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20140305 +DTEND;VALUE=DATE:20140306 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025146Z +LAST-MODIFIED:20090611T212419Z +UID:a05d7fa6-4c65-4465-a32a-1b5c2d1189aa +SUMMARY:Ash Wednesday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20150218 +DTEND;VALUE=DATE:20150219 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025202Z +LAST-MODIFIED:20090611T212419Z +UID:ce5fe814-c4b3-4aab-8298-86f54e34ffe4 +SUMMARY:Ash Wednesday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20160210 +DTEND;VALUE=DATE:20160211 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025220Z +LAST-MODIFIED:20090611T212419Z +UID:746c3ee3-3439-41bd-9ee1-7944812a8a29 +SUMMARY:Ash Wednesday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20170301 +DTEND;VALUE=DATE:20170302 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025235Z +LAST-MODIFIED:20090611T212419Z +UID:6eff84ea-6763-46d3-9c64-b847376f3e34 +SUMMARY:Ash Wednesday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20180214 +DTEND;VALUE=DATE:20180215 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025254Z +LAST-MODIFIED:20090611T212419Z +UID:e5d798cf-474f-42c3-83b8-0d1186c2c3ad +SUMMARY:Ash Wednesday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20190306 +DTEND;VALUE=DATE:20190307 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025309Z +LAST-MODIFIED:20090611T212419Z +UID:9d17488f-12b8-474b-8790-d22e6786735e +SUMMARY:Ash Wednesday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20200226 +DTEND;VALUE=DATE:20200227 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025421Z +LAST-MODIFIED:20090611T212419Z +UID:1abe0e7a-fa55-42b8-b6cc-bf037ab5c7f4 +SUMMARY:Palm Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000416 +DTEND;VALUE=DATE:20000417 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025457Z +LAST-MODIFIED:20090611T212419Z +UID:ff8dd4a7-0593-4fb6-bcdc-7cd8db41a451 +SUMMARY:Palm Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20010408 +DTEND;VALUE=DATE:20010409 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025510Z +LAST-MODIFIED:20090611T212419Z +UID:e403c114-aa09-4dcd-b8cf-b342be833477 +SUMMARY:Palm Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20020324 +DTEND;VALUE=DATE:20020325 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025522Z +LAST-MODIFIED:20090611T212419Z +UID:8ebc922a-a220-456f-937b-a943e5df123b +SUMMARY:Palm Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20030413 +DTEND;VALUE=DATE:20030414 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025549Z +LAST-MODIFIED:20090611T212419Z +UID:e03d10c1-ce57-426f-9a08-cd49c25920a9 +SUMMARY:Palm Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20040404 +DTEND;VALUE=DATE:20040405 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025630Z +LAST-MODIFIED:20090611T212419Z +UID:8ed86db1-ea90-453a-a1cd-36878811826e +SUMMARY:Palm Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20070401 +DTEND;VALUE=DATE:20070402 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025646Z +LAST-MODIFIED:20090611T212419Z +UID:b45150e2-da4d-477b-aace-b255186d89b1 +SUMMARY:Palm Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20080316 +DTEND;VALUE=DATE:20080317 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025820Z +LAST-MODIFIED:20090611T212419Z +UID:96b2e223-1b4f-44e1-80a3-8b57ab7d2f12 +SUMMARY:Palm Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20090405 +DTEND;VALUE=DATE:20090406 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025837Z +LAST-MODIFIED:20090611T212419Z +UID:863b0192-787d-4b4b-8550-c7e2db820b0a +SUMMARY:Palm Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20100328 +DTEND;VALUE=DATE:20100329 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025908Z +LAST-MODIFIED:20090611T212419Z +UID:ac159587-0a97-4f98-b11c-2193fe61265a +SUMMARY:Palm Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20120401 +DTEND;VALUE=DATE:20120402 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025922Z +LAST-MODIFIED:20090611T212419Z +UID:4dcb5128-64b8-488c-b9ec-6b8c6184c0e9 +SUMMARY:Palm Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20130324 +DTEND;VALUE=DATE:20130325 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025939Z +LAST-MODIFIED:20090611T212419Z +UID:0b3db3ec-fa4c-4e71-bf0b-251fde472ef6 +SUMMARY:Palm Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20140413 +DTEND;VALUE=DATE:20140414 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025956Z +LAST-MODIFIED:20090611T212419Z +UID:b3ac2edc-39ac-4d09-b45b-9a380ecbb63e +SUMMARY:Palm Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20150329 +DTEND;VALUE=DATE:20150330 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030027Z +LAST-MODIFIED:20090611T212419Z +UID:7af33c57-7711-4581-86e5-221ac5c59ecd +SUMMARY:Palm Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20160320 +DTEND;VALUE=DATE:20160321 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030042Z +LAST-MODIFIED:20090611T212419Z +UID:b372c357-0224-493e-a95c-50f69a9ee376 +SUMMARY:Palm Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20170409 +DTEND;VALUE=DATE:20170410 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030101Z +LAST-MODIFIED:20090611T212419Z +UID:2ed9175e-e119-4569-9371-390d70acc0fa +SUMMARY:Palm Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20180325 +DTEND;VALUE=DATE:20180326 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030124Z +LAST-MODIFIED:20090611T212419Z +UID:334646f6-5fef-43a9-83bc-d2dd88c9ebe6 +SUMMARY:Palm Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20190414 +DTEND;VALUE=DATE:20190415 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030150Z +LAST-MODIFIED:20090611T212419Z +UID:7f9c6e82-610a-4485-b5fd-dde7f0dba4b8 +SUMMARY:Palm Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20200405 +DTEND;VALUE=DATE:20200406 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030214Z +LAST-MODIFIED:20090611T212419Z +UID:af35fdb4-b67e-4b43-887f-c23f58bf9847 +SUMMARY:Good Friday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000421 +DTEND;VALUE=DATE:20000422 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030234Z +LAST-MODIFIED:20090611T212419Z +UID:116e335b-4cae-40b1-abf5-4e2f7121a4c0 +SUMMARY:Good Friday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20010413 +DTEND;VALUE=DATE:20010414 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030250Z +LAST-MODIFIED:20090611T212420Z +UID:7700d32c-2728-439f-b3a2-e458ba1052d7 +SUMMARY:Good Friday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20020329 +DTEND;VALUE=DATE:20020330 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030320Z +LAST-MODIFIED:20090611T212420Z +UID:2710e06f-c089-40d4-945a-9f8fe125a59c +SUMMARY:Good Friday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20040409 +DTEND;VALUE=DATE:20040410 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030337Z +LAST-MODIFIED:20090611T212420Z +UID:1dfafc7f-17b6-4a8d-b1ef-492adb9bd9dc +SUMMARY:Good Friday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20050325 +DTEND;VALUE=DATE:20050326 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030352Z +LAST-MODIFIED:20090611T212420Z +UID:8371454e-8dda-4667-81e5-3680dc8cc542 +SUMMARY:Good Friday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20060414 +DTEND;VALUE=DATE:20060415 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030406Z +LAST-MODIFIED:20090611T212420Z +UID:93b64832-1179-4db9-a8e5-4d3375156294 +SUMMARY:Good Friday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20070406 +DTEND;VALUE=DATE:20070407 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030423Z +LAST-MODIFIED:20090611T212420Z +UID:ee7bc7d8-d5a8-43f1-b817-54b5458429fa +SUMMARY:Good Friday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20080321 +DTEND;VALUE=DATE:20080322 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030436Z +LAST-MODIFIED:20090611T212420Z +UID:98652109-bdda-4451-afd3-9955bfe918c7 +SUMMARY:Good Friday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20090410 +DTEND;VALUE=DATE:20090411 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030446Z +LAST-MODIFIED:20090611T212420Z +UID:7241d18c-6e4a-4a15-b262-a0415d05e5c0 +SUMMARY:Good Friday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20100402 +DTEND;VALUE=DATE:20100403 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030513Z +LAST-MODIFIED:20090611T212420Z +UID:be42b228-382d-4add-be00-c36fdb2508c7 +SUMMARY:Good Friday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20120406 +DTEND;VALUE=DATE:20120407 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030526Z +LAST-MODIFIED:20090611T212420Z +UID:db8d6551-0b2d-44c1-8c21-ba96030a445e +SUMMARY:Good Friday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20130329 +DTEND;VALUE=DATE:20130330 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030543Z +LAST-MODIFIED:20090611T212420Z +UID:c14a8a90-a69d-44fe-9e34-a28feed1a1c0 +SUMMARY:Good Friday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20140418 +DTEND;VALUE=DATE:20140419 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030602Z +LAST-MODIFIED:20090611T212420Z +UID:269d06c4-2eed-45de-af47-6668b7e58902 +SUMMARY:Good Friday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20150403 +DTEND;VALUE=DATE:20150404 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030616Z +LAST-MODIFIED:20090611T212420Z +UID:8e3f7823-a340-4b6e-8b7c-10a539f5db8f +SUMMARY:Good Friday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20160325 +DTEND;VALUE=DATE:20160326 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030630Z +LAST-MODIFIED:20090611T212420Z +UID:a6d17884-5c3e-4f4b-9383-7b83814ada9d +SUMMARY:Good Friday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20170414 +DTEND;VALUE=DATE:20170415 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030648Z +LAST-MODIFIED:20090611T212420Z +UID:19fb2499-97ae-457f-8d41-6ec76292c77f +SUMMARY:Good Friday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20180330 +DTEND;VALUE=DATE:20180331 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030704Z +LAST-MODIFIED:20090611T212420Z +UID:0a125549-300d-4bd9-af20-3fc28935946e +SUMMARY:Good Friday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20190419 +DTEND;VALUE=DATE:20190420 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030718Z +LAST-MODIFIED:20090611T212420Z +UID:50de218e-013d-4739-b8d4-f30f74a57168 +SUMMARY:Good Friday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20200410 +DTEND;VALUE=DATE:20200411 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030737Z +LAST-MODIFIED:20090611T212420Z +UID:af9df15d-af91-4e19-b7d2-e80bf8dfc378 +SUMMARY:Pentecost +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000611 +DTEND;VALUE=DATE:20000612 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030855Z +LAST-MODIFIED:20090611T212420Z +UID:b5eb3eae-875a-4b50-a10c-b8a487907eac +SUMMARY:Pentecost +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20010603 +DTEND;VALUE=DATE:20010604 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030907Z +LAST-MODIFIED:20090611T212420Z +UID:c5a9ee90-d177-4ca5-917e-245791f15312 +SUMMARY:Pentecost +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20020519 +DTEND;VALUE=DATE:20020520 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030942Z +LAST-MODIFIED:20090611T212420Z +UID:9bbe68fb-4d23-4d27-9171-26177baf984d +SUMMARY:Pentecost +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20030608 +DTEND;VALUE=DATE:20030609 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030959Z +LAST-MODIFIED:20090611T212420Z +UID:c4f15038-8df9-475b-bc85-9948f97cddc7 +SUMMARY:Pentecost +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20040530 +DTEND;VALUE=DATE:20040531 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T031013Z +LAST-MODIFIED:20090611T212420Z +UID:d54dd907-ba90-4607-88ee-b15d7b79736f +SUMMARY:Pentecost +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20050515 +DTEND;VALUE=DATE:20050516 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T031027Z +LAST-MODIFIED:20090611T212420Z +UID:810562a0-2467-4a43-860d-db69cbf92fdf +SUMMARY:Pentecost +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20060604 +DTEND;VALUE=DATE:20060605 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T031042Z +LAST-MODIFIED:20090611T212420Z +UID:8212e80d-6efa-4e79-bcd0-44fc831a1ecc +SUMMARY:Pentecost +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20070527 +DTEND;VALUE=DATE:20070528 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T031101Z +LAST-MODIFIED:20090611T212420Z +UID:da7b884d-0d0f-48d3-97f7-d13b21cea817 +SUMMARY:Pentecost +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20080511 +DTEND;VALUE=DATE:20080512 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T031112Z +LAST-MODIFIED:20090611T212420Z +UID:719c1eff-d26b-4316-86b5-464a4d24bf98 +SUMMARY:Pentecost +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20090531 +DTEND;VALUE=DATE:20090601 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T031123Z +LAST-MODIFIED:20090611T212420Z +UID:a61992d7-0747-46db-aa4c-49ddef65de55 +SUMMARY:Pentecost +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20100523 +DTEND;VALUE=DATE:20100524 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T031210Z +LAST-MODIFIED:20090611T212420Z +UID:09673ae2-e44c-4cac-90de-deaf49b6b875 +SUMMARY:Pentecost +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20120527 +DTEND;VALUE=DATE:20120528 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T031224Z +LAST-MODIFIED:20090611T212420Z +UID:1919a739-59f5-46c2-8cb0-b86726b1e72d +SUMMARY:Pentecost +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20130519 +DTEND;VALUE=DATE:20130520 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T031235Z +LAST-MODIFIED:20090611T212420Z +UID:0a4fae5e-4698-427f-bbdf-b6c08fd96053 +SUMMARY:Pentecost +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20140608 +DTEND;VALUE=DATE:20140609 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T031253Z +LAST-MODIFIED:20090611T212420Z +UID:3d5c4095-4429-4abe-8a7a-4bbf5236eaa4 +SUMMARY:Pentecost +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20150524 +DTEND;VALUE=DATE:20150525 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T031311Z +LAST-MODIFIED:20090611T212420Z +UID:1177a296-0723-4fcb-8d35-e79b1c021330 +SUMMARY:Pentecost +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20160515 +DTEND;VALUE=DATE:20160516 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T031323Z +LAST-MODIFIED:20090611T212420Z +UID:452c678d-0b42-4a89-a347-a0b8e12c4fd6 +SUMMARY:Pentecost +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20170604 +DTEND;VALUE=DATE:20170605 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T031341Z +LAST-MODIFIED:20090611T212420Z +UID:01fec43b-b371-4f56-97cb-ef454132a74a +SUMMARY:Pentecost +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20180520 +DTEND;VALUE=DATE:20180521 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T031401Z +LAST-MODIFIED:20090611T212420Z +UID:697b8004-261c-4c0b-a15e-06c1af95ab9b +SUMMARY:Pentecost +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20190609 +DTEND;VALUE=DATE:20190610 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T031412Z +LAST-MODIFIED:20090611T212420Z +UID:ac723797-16b2-4fd9-9d5e-2f55cc90dc22 +SUMMARY:Pentecost +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20200531 +DTEND;VALUE=DATE:20200601 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T031600Z +LAST-MODIFIED:20090611T212420Z +UID:bec77dd4-49e7-46b5-9ea4-7409cc5369e1 +SUMMARY:Trinity Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000618 +DTEND;VALUE=DATE:20000619 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T031731Z +LAST-MODIFIED:20090611T212420Z +UID:f395a03d-27e2-4214-88c2-20c638c7f99d +SUMMARY:Trinity Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20010610 +DTEND;VALUE=DATE:20010611 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T031843Z +LAST-MODIFIED:20090611T212420Z +UID:fb37469f-ba53-4fec-9c65-fb22c59470a8 +SUMMARY:Trinity Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20020526 +DTEND;VALUE=DATE:20020527 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030305Z +LAST-MODIFIED:20090611T212420Z +UID:a2eb4297-f3a3-414b-b53a-e7cdaad20ac9 +SUMMARY:Good Friday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20030418 +DTEND;VALUE=DATE:20030419 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T032035Z +LAST-MODIFIED:20090611T212420Z +UID:078f774d-1113-4bad-b7e5-6f4c48100fc4 +SUMMARY:Trinity Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20030615 +DTEND;VALUE=DATE:20030616 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T032128Z +LAST-MODIFIED:20090611T212420Z +UID:c2df343c-bf38-4e86-9129-010bd55de1b1 +SUMMARY:Trinity Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20040606 +DTEND;VALUE=DATE:20040607 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025605Z +LAST-MODIFIED:20090611T212420Z +UID:e4891762-ad19-402e-8195-828fb3386d22 +SUMMARY:Palm Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20050320 +DTEND;VALUE=DATE:20050321 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T032246Z +LAST-MODIFIED:20090611T212420Z +UID:f14c5274-2112-4470-a3f8-f8a5b4eb8199 +SUMMARY:Trinity Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20050522 +DTEND;VALUE=DATE:20050523 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025617Z +LAST-MODIFIED:20090611T212420Z +UID:33a3f736-bd5b-4a87-b983-612e01ea4dbe +SUMMARY:Palm Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20060409 +DTEND;VALUE=DATE:20060410 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T032352Z +LAST-MODIFIED:20090611T212421Z +UID:43eeeab3-5836-41cc-82b4-d91d922871c4 +SUMMARY:Trinity Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20060611 +DTEND;VALUE=DATE:20060612 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T032435Z +LAST-MODIFIED:20090611T212421Z +UID:72234667-e98f-4532-b4da-a2cdd2e56ce4 +SUMMARY:Trinity Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20070603 +DTEND;VALUE=DATE:20070604 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T032512Z +LAST-MODIFIED:20090611T212421Z +UID:a289a44f-5eb2-4f14-b458-e55dcbc00e9d +SUMMARY:Trinity Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20080518 +DTEND;VALUE=DATE:20080519 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T032617Z +LAST-MODIFIED:20090611T212421Z +UID:b5d1e103-7807-4049-ac80-df13ab666b5e +SUMMARY:Trinity Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20090607 +DTEND;VALUE=DATE:20090608 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T032717Z +LAST-MODIFIED:20090611T212421Z +UID:59ef3b5c-a65f-4a5f-81ec-25d201285670 +SUMMARY:Trinity Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20100530 +DTEND;VALUE=DATE:20100531 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T033236Z +LAST-MODIFIED:20090611T212421Z +UID:f931f051-1dec-4a6c-b78b-615474143fd1 +SUMMARY:Trinity Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20120603 +DTEND;VALUE=DATE:20120604 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T033315Z +LAST-MODIFIED:20090611T212421Z +UID:838f906b-f3c8-4b94-9cdb-ef698d0222b8 +SUMMARY:Trinity Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20130526 +DTEND;VALUE=DATE:20130527 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T033425Z +LAST-MODIFIED:20090611T212421Z +UID:de98efc2-cd59-41fd-af0d-d2dcb58b23fe +SUMMARY:Trinity Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20140615 +DTEND;VALUE=DATE:20140616 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T033511Z +LAST-MODIFIED:20090611T212421Z +UID:139b7040-5cfd-4db4-abf2-8251ae17368e +SUMMARY:Trinity Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20150531 +DTEND;VALUE=DATE:20150601 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T033614Z +LAST-MODIFIED:20090611T212421Z +UID:78d6d38a-a0ae-4b75-bcbb-0276d69a430e +SUMMARY:Trinity Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20160522 +DTEND;VALUE=DATE:20160523 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T033655Z +LAST-MODIFIED:20090611T212421Z +UID:83608056-f721-4564-88ed-3cc5d12b01fb +SUMMARY:Trinity Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20170611 +DTEND;VALUE=DATE:20170612 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T033759Z +LAST-MODIFIED:20090611T212421Z +UID:cc92b242-3a24-471c-b1be-b8918fb8b6de +SUMMARY:Trinity Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20180527 +DTEND;VALUE=DATE:20180528 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T033848Z +LAST-MODIFIED:20090611T212421Z +UID:006195b6-8021-4814-9635-f9f5bffa7a36 +SUMMARY:Trinity Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20190616 +DTEND;VALUE=DATE:20190617 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T033920Z +LAST-MODIFIED:20090611T212421Z +UID:b60896ca-b1eb-4ec1-a9cd-67c7403c6335 +SUMMARY:Trinity Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20200607 +DTEND;VALUE=DATE:20200608 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20081125T224733Z +LAST-MODIFIED:20090611T212421Z +UID:d41971bd-5c3f-4293-a154-a210d39b6667 +SUMMARY:Ascension Day +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000601 +DTEND;VALUE=DATE:20000602 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20081125T224836Z +LAST-MODIFIED:20090611T212421Z +UID:e6166189-6b80-44b8-9902-60e58f0cbe0c +SUMMARY:Ascension Day +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20010524 +DTEND;VALUE=DATE:20010525 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20081125T224858Z +LAST-MODIFIED:20090611T212421Z +UID:7f5a197b-441e-40e3-a31a-1c0cb5ffed19 +SUMMARY:Ascension Day +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20020509 +DTEND;VALUE=DATE:20020510 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20081125T224916Z +LAST-MODIFIED:20090611T212421Z +UID:192d84d7-7a76-4d98-a335-919203fa28e1 +SUMMARY:Ascension Day +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20030529 +DTEND;VALUE=DATE:20030530 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20081125T224929Z +LAST-MODIFIED:20090611T212421Z +UID:52cb0477-1dab-4881-bf56-64099c934453 +SUMMARY:Ascension Day +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20040520 +DTEND;VALUE=DATE:20040521 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20081125T224944Z +LAST-MODIFIED:20090611T212421Z +UID:17c27008-29e2-4050-adda-5757c94fb673 +SUMMARY:Ascension Day +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20050505 +DTEND;VALUE=DATE:20050506 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20081125T224959Z +LAST-MODIFIED:20090611T212421Z +UID:03f7a269-b863-4fb9-a02f-135803fa2972 +SUMMARY:Ascension Day +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20060525 +DTEND;VALUE=DATE:20060526 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20081125T225014Z +LAST-MODIFIED:20090611T212421Z +UID:fc9f7abd-c3fc-42b2-bd87-7528470f77f3 +SUMMARY:Ascension Day +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20070517 +DTEND;VALUE=DATE:20070518 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20081125T225025Z +LAST-MODIFIED:20090611T212421Z +UID:c1649dbc-9ad2-4257-a687-7dc706b08ea2 +SUMMARY:Ascension Day +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20080501 +DTEND;VALUE=DATE:20080502 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20081125T225039Z +LAST-MODIFIED:20090611T212421Z +UID:82c49077-6f81-42ed-84e1-53ebe66f981d +SUMMARY:Ascension Day +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20090521 +DTEND;VALUE=DATE:20090522 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20081125T225051Z +LAST-MODIFIED:20090611T212421Z +UID:5203b99c-f304-4d25-ba4e-1e776237186d +SUMMARY:Ascension Day +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20100513 +DTEND;VALUE=DATE:20100514 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20081125T225111Z +LAST-MODIFIED:20090611T212421Z +UID:18e27f82-2d27-42fa-b04c-315c6fadcd9d +SUMMARY:Ascension Day +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20120517 +DTEND;VALUE=DATE:20120518 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20081125T225120Z +LAST-MODIFIED:20090611T212421Z +UID:7726ca21-1f4d-4cc5-ade3-8c384f5d72a5 +SUMMARY:Ascension Day +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20130509 +DTEND;VALUE=DATE:20130510 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20081125T225133Z +LAST-MODIFIED:20090611T212421Z +UID:6d025a60-733c-4af4-a2ee-f1b904090666 +SUMMARY:Ascension Day +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20140529 +DTEND;VALUE=DATE:20140530 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20081125T225144Z +LAST-MODIFIED:20090611T212421Z +UID:cd092661-b37e-4ec9-b798-8f957c4278b4 +SUMMARY:Ascension Day +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20150514 +DTEND;VALUE=DATE:20150515 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20081125T225154Z +LAST-MODIFIED:20090611T212421Z +UID:ed1358ef-c818-4af3-9f8e-4d965d800a21 +SUMMARY:Ascension Day +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20160505 +DTEND;VALUE=DATE:20160506 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20081125T225204Z +LAST-MODIFIED:20090611T212421Z +UID:7c8351e3-e3fd-4c0b-a60c-f558aa31df55 +SUMMARY:Ascension Day +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20170525 +DTEND;VALUE=DATE:20170526 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20081125T225214Z +LAST-MODIFIED:20090611T212421Z +UID:db037aff-17bf-430b-b094-52e99879ba4e +SUMMARY:Ascension Day +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20180510 +DTEND;VALUE=DATE:20180511 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20081125T225224Z +LAST-MODIFIED:20090611T212421Z +UID:dddeea06-c165-474c-96aa-6c1b705c6b0a +SUMMARY:Ascension Day +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20190530 +DTEND;VALUE=DATE:20190531 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20081125T225238Z +LAST-MODIFIED:20090611T212421Z +UID:09a3f71c-83df-4649-b7d1-4792ae2fb523 +SUMMARY:Ascension Day +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20200521 +DTEND;VALUE=DATE:20200522 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025033Z +LAST-MODIFIED:20100911T002511Z +DTSTAMP:20100911T002511Z +UID:a02038e9-a094-4744-b09d-d7d2ef96bd28 +SUMMARY:Ash Wednesday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20110309 +DTEND;VALUE=DATE:20110310 +TRANSP:TRANSPARENT +SEQUENCE:1 +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T025855Z +LAST-MODIFIED:20100911T002525Z +DTSTAMP:20100911T002525Z +UID:844a3d56-4fab-4df1-92d3-8f4385d8efc9 +SUMMARY:Palm Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20110417 +DTEND;VALUE=DATE:20110418 +TRANSP:TRANSPARENT +SEQUENCE:1 +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T030501Z +LAST-MODIFIED:20100911T002555Z +DTSTAMP:20100911T002555Z +UID:6e05d4a1-8b7f-446d-a7fc-566b6837c927 +SUMMARY:Good Friday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20110422 +DTEND;VALUE=DATE:20110423 +TRANSP:TRANSPARENT +SEQUENCE:1 +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T223849Z +LAST-MODIFIED:20100911T002632Z +DTSTAMP:20100911T002632Z +UID:fa0cda04-283e-4b53-95b0-c9903a3f85e2 +SUMMARY:Easter Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20110424 +DTEND;VALUE=DATE:20110425 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:2 +SEQUENCE:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20081125T225101Z +LAST-MODIFIED:20100911T002740Z +DTSTAMP:20100911T002740Z +UID:5250764e-c50c-44ba-b3d6-32ee24ee49e1 +SUMMARY:Ascension Day +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20110602 +DTEND;VALUE=DATE:20110603 +TRANSP:TRANSPARENT +SEQUENCE:1 +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T031143Z +LAST-MODIFIED:20100911T002756Z +DTSTAMP:20100911T002756Z +UID:5e614422-74fe-47d6-bf89-dca8c4d7df3a +SUMMARY:Pentecost +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20110612 +DTEND;VALUE=DATE:20110613 +TRANSP:TRANSPARENT +SEQUENCE:1 +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T033004Z +LAST-MODIFIED:20100911T002812Z +DTSTAMP:20100911T002812Z +UID:001f88a2-a3b4-4dff-a577-c9860d01dcbd +SUMMARY:Trinity Sunday +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20110619 +DTEND;VALUE=DATE:20110620 +TRANSP:TRANSPARENT +SEQUENCE:1 +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212421Z +UID:9f3df592-1dd1-11b2-8b51-a8c49a575f97 +SUMMARY:President's Day +STATUS:CONFIRMED +RRULE:FREQ=YEARLY;BYDAY=3MO;BYMONTH=2 +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000221 +DTEND;VALUE=DATE:20000222 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212421Z +UID:e0d4c156-1dd1-11b2-b6a4-dac750e0163e +SUMMARY:Mother's Day +STATUS:CONFIRMED +RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=5 +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000514 +DTEND;VALUE=DATE:20000515 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212421Z +UID:c2783248-1dd1-11b2-affa-ee50c39d8083 +SUMMARY:Armed Forces Day +STATUS:CONFIRMED +RRULE:FREQ=YEARLY;BYDAY=3SA;BYMONTH=5 +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000520 +DTEND;VALUE=DATE:20000521 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212421Z +UID:d9706fa6-1dd1-11b2-a349-e97241bd4740 +SUMMARY:Memorial Day +STATUS:CONFIRMED +RRULE:FREQ=YEARLY;BYDAY=-1MO;BYMONTH=5 +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000529 +DTEND;VALUE=DATE:20000530 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212421Z +UID:46e6845c-1dd2-11b2-bd3d-d8755a1a171f +SUMMARY:Parents' Day +STATUS:CONFIRMED +RRULE:FREQ=YEARLY;BYDAY=4SU;BYMONTH=7 +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000723 +DTEND;VALUE=DATE:20000724 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212421Z +UID:0726ea42-1dd2-11b2-9fe6-dda3d063fb50 +SUMMARY:Labor Day +STATUS:CONFIRMED +RRULE:FREQ=YEARLY;BYDAY=1MO;BYMONTH=9 +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000904 +DTEND;VALUE=DATE:20000905 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212421Z +UID:7d4402f4-1dd2-11b2-9790-b6193cfa4349 +SUMMARY:Halloween +STATUS:CONFIRMED +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20001031 +DTEND;VALUE=DATE:20001101 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212421Z +UID:2c7ba10c-1dd2-11b2-b0b7-96f89f288199 +SUMMARY:Veteran's Day +STATUS:CONFIRMED +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20001111 +DTEND;VALUE=DATE:20001112 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212421Z +UID:4299a358-1dd2-11b2-a228-d062222b6f88 +SUMMARY:Thanksgiving Day +STATUS:CONFIRMED +RRULE:FREQ=YEARLY;BYDAY=4TH;BYMONTH=11 +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20001123 +DTEND;VALUE=DATE:20001124 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212421Z +UID:54f392fa-1dd2-11b2-8ecb-9ae0a7fcebd1 +SUMMARY:Christmas Day +STATUS:CONFIRMED +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20001225 +DTEND;VALUE=DATE:20001226 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212421Z +UID:b1f194fc-1dd1-11b2-a973-d219c68b95c4 +SUMMARY:New Year's Day +STATUS:CONFIRMED +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000101 +DTEND;VALUE=DATE:20000102 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212421Z +UID:a2cbca9c-1dd1-11b2-83e2-abab36f0506d +SUMMARY:New Year's Eve +STATUS:CONFIRMED +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20001231 +DTEND;VALUE=DATE:20010101 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212421Z +UID:13f83ed2-1dd2-11b2-a0de-ee8645423959 +SUMMARY:Daylight Savings Time ends +STATUS:CONFIRMED +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11 +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20071104 +DTEND;VALUE=DATE:20071105 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212422Z +UID:f669a974-1dd1-11b2-9fac-f44d91c657af +SUMMARY:Independence Day +STATUS:CONFIRMED +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000704 +DTEND;VALUE=DATE:20000705 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212422Z +UID:cb90487e-1dd1-11b2-9e06-d1a12cc963dc +SUMMARY:Father's Day +STATUS:CONFIRMED +RRULE:FREQ=YEARLY;BYDAY=3SU;BYMONTH=6 +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000618 +DTEND;VALUE=DATE:20000619 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212422Z +UID:e8022726-1dd1-11b2-aae6-9f73f16c0f01 +SUMMARY:Flag Day +STATUS:CONFIRMED +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000614 +DTEND;VALUE=DATE:20000615 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212422Z +UID:f1561106-1dd1-11b2-955f-ba14e5210f45 +SUMMARY:Earth Day +STATUS:CONFIRMED +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000422 +DTEND;VALUE=DATE:20000423 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212422Z +UID:e46086c2-1dd1-11b2-8a16-a2c99f1c525a +SUMMARY:Tax Day +STATUS:CONFIRMED +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000415 +DTEND;VALUE=DATE:20000416 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212422Z +UID:c981b858-1dd1-11b2-b69b-94d05f8bda66 +SUMMARY:April Fool's Day +STATUS:CONFIRMED +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000401 +DTEND;VALUE=DATE:20000402 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212422Z +UID:bd6cfe4c-1dd1-11b2-b3dc-ebaab9302c26 +SUMMARY:St. Patrick's Day +STATUS:CONFIRMED +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000317 +DTEND;VALUE=DATE:20000318 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212422Z +UID:aff9f3c8-1dd1-11b2-8593-c63469762eb1 +SUMMARY:George Washington's Birthday (actual) +STATUS:CONFIRMED +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000222 +DTEND;VALUE=DATE:20000223 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212422Z +UID:790b86e2-1dd2-11b2-9cb9-c905fa35c6f9 +SUMMARY:Valentine's Day +STATUS:CONFIRMED +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000214 +DTEND;VALUE=DATE:20000215 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212422Z +UID:4d5b843e-1dd2-11b2-a6c9-8ea942124d1b +SUMMARY:Abraham Lincoln's Birthday +STATUS:CONFIRMED +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000212 +DTEND;VALUE=DATE:20000213 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212422Z +UID:0c771532-1dd2-11b2-8dd9-8638db3aef63 +SUMMARY:Groundhog's Day +STATUS:CONFIRMED +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000202 +DTEND;VALUE=DATE:20000203 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T220831Z +LAST-MODIFIED:20090611T212422Z +UID:8a05b5d3-6b97-4fe1-b26e-b258a073c5e0 +SUMMARY:United Nations Day +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20001024 +DTEND;VALUE=DATE:20001025 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212422Z +UID:e462881a-20b8-4b70-ab3c-cf38016d3398 +SUMMARY:Daylight Savings Time begins +STATUS:CONFIRMED +RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3 +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20070311 +DTEND;VALUE=DATE:20070312 +CLASS:PUBLIC +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T221253Z +LAST-MODIFIED:20090611T212422Z +UID:1eb525d5-26cd-43f0-819e-acc3003b35ad +SUMMARY:Daylight Savings Time begins +RRULE:FREQ=YEARLY;UNTIL=20060402T000000;BYDAY=1SU;BYMONTH=4 +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000402 +DTEND;VALUE=DATE:20000403 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T221447Z +LAST-MODIFIED:20090611T212422Z +UID:05504c5b-5984-4eb0-a60a-c6d5cf083d82 +SUMMARY:Daylight Savings Time ends +RRULE:FREQ=YEARLY;UNTIL=20061029T000000;BYDAY=-1SU;BYMONTH=10 +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20001029 +DTEND;VALUE=DATE:20001030 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T214410Z +LAST-MODIFIED:20090611T212422Z +UID:088031d1-abea-47ed-978d-39c6319e8bf2 +SUMMARY:Inauguration Day +RRULE:FREQ=YEARLY;INTERVAL=4 +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20010120 +DTEND;VALUE=DATE:20010121 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080828T164802Z +LAST-MODIFIED:20090611T212422Z +UID:d3c521e1-ffab-414c-8cfa-99e76758d8c8 +SUMMARY:Constitution Day\, Citizenship Day +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000917 +DTEND;VALUE=DATE:20000918 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080828T165106Z +LAST-MODIFIED:20090611T212422Z +UID:a44b8b6e-7ae4-4120-b7ad-14e91691be2a +SUMMARY:National Day of Prayer +RRULE:FREQ=YEARLY;BYDAY=1TH;BYMONTH=5 +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000504 +DTEND;VALUE=DATE:20000505 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080828T165839Z +LAST-MODIFIED:20090611T212422Z +UID:e20f1b5c-8ac4-4bea-acf4-b4f2bfe95329 +SUMMARY:Arbor Day +RRULE:FREQ=YEARLY;BYDAY=-1FR;BYMONTH=4 +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000428 +DTEND;VALUE=DATE:20000429 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080830T011903Z +LAST-MODIFIED:20090611T212422Z +UID:feae8f6c-bdce-46c8-aa45-e2d1c0fb9cb0 +SUMMARY:US General Election +RRULE:FREQ=YEARLY;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8;BYMONTH=11 +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20001107 +DTEND;VALUE=DATE:20001108 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080830T021222Z +LAST-MODIFIED:20090611T212422Z +UID:60b5ca68-d0a6-4634-9254-16cc300bb12e +SUMMARY:Black Friday +RRULE:FREQ=YEARLY;BYDAY=FR;BYMONTHDAY=23,24,25,26,27,28,29;BYMONTH=11 +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20001124 +DTEND;VALUE=DATE:20001125 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212422Z +UID:dd9bfdf4-1dd1-11b2-ab6e-8da990a14639 +SUMMARY:Martin Luther King Jr.'s Day +STATUS:CONFIRMED +RRULE:FREQ=YEARLY;BYDAY=3MO;BYMONTH=1 +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000117 +DTEND;VALUE=DATE:20000118 +CLASS:PUBLIC +TRANSP:TRANSPARENT +DESCRIPTION:Traditionally on January 15th.\nObserved on 3rd Monday of January. +END:VEVENT +BEGIN:VEVENT +CREATED:20070315T171800Z +LAST-MODIFIED:20090611T212422Z +UID:175691b0-1dd2-11b2-9596-e9837a6245dd +SUMMARY:Columbus Day (observed) +STATUS:CONFIRMED +RRULE:FREQ=YEARLY;BYDAY=2MO;BYMONTH=10 +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20001009 +DTEND;VALUE=DATE:20001010 +CLASS:PUBLIC +TRANSP:TRANSPARENT +DESCRIPTION:Traditionally on October 12th.\nObserved on 2nd Monday of October. +END:VEVENT +BEGIN:VEVENT +CREATED:20080902T140125Z +LAST-MODIFIED:20090611T212422Z +UID:22896130-ef70-46ca-a071-a15f3a746b04 +SUMMARY:Administrative Professionals Day +RRULE:FREQ=YEARLY;BYDAY=WE;BYMONTHDAY=21,22,23,24,25,26,27;BYMONTH=4 +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000426 +DTEND;VALUE=DATE:20000427 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T212909Z +LAST-MODIFIED:20090611T212422Z +UID:a0486410-f87b-4e09-939a-77121b58b535 +SUMMARY:Christmas Eve +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20001224 +DTEND;VALUE=DATE:20001225 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20081126T134531Z +LAST-MODIFIED:20090611T212422Z +UID:8c320d1a-fc56-4282-962c-4c67763a4c58 +SUMMARY:Grandparents Day +RRULE:FREQ=YEARLY;BYDAY=SU;BYMONTHDAY=7,8,9,10,11,12,13;BYMONTH=9 +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000910 +DTEND;VALUE=DATE:20000911 +DESCRIPTION:First Sunday following Labor Day +TRANSP:TRANSPARENT +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T214111Z +LAST-MODIFIED:20090611T212422Z +UID:efab1245-c19c-4693-8391-eb5f40cc3743 +SUMMARY:Epiphany +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000106 +DTEND;VALUE=DATE:20000107 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080827T220608Z +LAST-MODIFIED:20090611T212422Z +UID:37f09326-5105-4092-8535-1b4cad9812ac +SUMMARY:Reformation Day +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20001029 +DTEND;VALUE=DATE:20001030 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20080829T034030Z +LAST-MODIFIED:20090611T212422Z +UID:af808fce-8a77-4d15-ad63-c096737303d2 +SUMMARY:All Saints Day +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20001101 +DTEND;VALUE=DATE:20001102 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20081118T135517Z +LAST-MODIFIED:20090611T212422Z +UID:6c6e277f-d344-4807-8d91-bea1689eac7c +SUMMARY:Immaculate Conception +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20001208 +DTEND;VALUE=DATE:20001209 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20081118T141534Z +LAST-MODIFIED:20090611T212422Z +UID:25db674e-6d6a-41ee-848b-2b79552f071a +SUMMARY:Assumption Day +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000815 +DTEND;VALUE=DATE:20000816 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20081126T151107Z +LAST-MODIFIED:20090611T212422Z +UID:c3a78810-ea6a-48bc-8178-56fe3fe61e23 +SUMMARY:Kwanzaa +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:19661226 +DTEND;VALUE=DATE:19670102 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20091128T184942Z +LAST-MODIFIED:20091128T185038Z +UID:cebdafa9-6b2a-4869-a751-5554b6da2e45 +SUMMARY:Pearl Harbor Remembrance Day +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20001207 +DTEND;VALUE=DATE:20001208 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100911T004419Z +LAST-MODIFIED:20100911T004455Z +DTSTAMP:20100911T004455Z +UID:1ee4d840-eb70-44f0-8f2a-db778c373ac5 +SUMMARY:Patriot Day +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20010911 +DTEND;VALUE=DATE:20010912 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100911T004608Z +LAST-MODIFIED:20100911T004647Z +DTSTAMP:20100911T004647Z +UID:e8c8b4f6-1e0a-4bbb-b077-0fcce9077e1d +SUMMARY:Women's Equality Day +RRULE:FREQ=YEARLY +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20000826 +DTEND;VALUE=DATE:20000827 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T225155Z +LAST-MODIFIED:20100625T230014Z +DTSTAMP:20100625T230014Z +UID:4c53aa44-f014-4334-8e39-5b2ff090445a +SUMMARY:New Year's Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20100101 +DTEND;VALUE=DATE:20100102 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T225222Z +LAST-MODIFIED:20100625T230022Z +DTSTAMP:20100625T230022Z +UID:5705d0cf-01b6-4511-9f36-0fc9739b21fc +SUMMARY:Birthday of Martin Luther King\, Jr. (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20100118 +DTEND;VALUE=DATE:20100119 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T225249Z +LAST-MODIFIED:20100625T230034Z +DTSTAMP:20100625T230034Z +UID:26751c39-998f-4d1c-bc77-454f21d94fd1 +SUMMARY:Washington's Birthday (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20100215 +DTEND;VALUE=DATE:20100216 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T225312Z +LAST-MODIFIED:20100625T230050Z +DTSTAMP:20100625T230050Z +UID:91cde8ea-009b-4a07-b835-7d33b1b7e4e1 +SUMMARY:Memorial Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20100531 +DTEND;VALUE=DATE:20100601 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T225342Z +LAST-MODIFIED:20100625T230108Z +DTSTAMP:20100625T230108Z +UID:63368446-395a-42ca-8108-f1bb7ee5a851 +SUMMARY:Independence Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20100705 +DTEND;VALUE=DATE:20100706 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T225406Z +LAST-MODIFIED:20100625T230123Z +DTSTAMP:20100625T230123Z +UID:6cbc2eb9-d818-40f0-8e91-7c7ca5e43e0f +SUMMARY:Labor Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20100906 +DTEND;VALUE=DATE:20100907 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T225422Z +LAST-MODIFIED:20100625T230138Z +DTSTAMP:20100625T230138Z +UID:26ab3aae-1f30-4eea-9a30-35aab72d9e67 +SUMMARY:Columbus Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20101011 +DTEND;VALUE=DATE:20101012 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T225442Z +LAST-MODIFIED:20100625T230149Z +DTSTAMP:20100625T230149Z +UID:c08f4482-8591-4940-8838-80381b0cf69b +SUMMARY:Veterans Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20101111 +DTEND;VALUE=DATE:20101112 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T225456Z +LAST-MODIFIED:20100625T230155Z +DTSTAMP:20100625T230155Z +UID:7262eefe-a1b1-4370-8262-c2f900150c0f +SUMMARY:Thanksgiving Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20101125 +DTEND;VALUE=DATE:20101126 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T225512Z +LAST-MODIFIED:20100625T230205Z +DTSTAMP:20100625T230205Z +UID:431576fe-17ac-48ea-941e-08d25998c7c2 +SUMMARY:Christmas Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20101224 +DTEND;VALUE=DATE:20101225 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T225649Z +LAST-MODIFIED:20100625T230212Z +DTSTAMP:20100625T230212Z +UID:ff38e9bf-6a04-4140-8e14-17f77bfabbdf +SUMMARY:New Year's Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20101231 +DTEND;VALUE=DATE:20110101 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T225710Z +LAST-MODIFIED:20100625T230225Z +DTSTAMP:20100625T230225Z +UID:c670bd38-0675-4746-8eca-cd06d5d0fb46 +SUMMARY:Birthday of Martin Luther King\, Jr. (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20110117 +DTEND;VALUE=DATE:20110118 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T225733Z +LAST-MODIFIED:20100625T230234Z +DTSTAMP:20100625T230234Z +UID:486ce67b-2a71-44ae-87d1-ec8d000a9584 +SUMMARY:Washington's Birthday (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20110221 +DTEND;VALUE=DATE:20110222 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T225753Z +LAST-MODIFIED:20100625T230259Z +DTSTAMP:20100625T230259Z +UID:aca86965-d147-47d7-8e88-3990efe7188a +SUMMARY:Memorial Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20110530 +DTEND;VALUE=DATE:20110531 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T225812Z +LAST-MODIFIED:20100625T230311Z +DTSTAMP:20100625T230311Z +UID:40f9466a-95bd-40fd-a0a5-df005fbee438 +SUMMARY:Independence Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20110704 +DTEND;VALUE=DATE:20110705 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T225826Z +LAST-MODIFIED:20100625T230320Z +DTSTAMP:20100625T230320Z +UID:86a93453-eb85-4836-8e35-f3d7403eb02c +SUMMARY:Labor Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20110905 +DTEND;VALUE=DATE:20110906 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T225845Z +LAST-MODIFIED:20100625T230329Z +DTSTAMP:20100625T230329Z +UID:8aed5642-2ab6-4d47-ae3e-d6bda178c530 +SUMMARY:Columbus Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20111010 +DTEND;VALUE=DATE:20111011 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T225901Z +LAST-MODIFIED:20100625T230342Z +DTSTAMP:20100625T230342Z +UID:095e361c-db8c-4936-80ff-6ac356e3a8af +SUMMARY:Veterans Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20111111 +DTEND;VALUE=DATE:20111112 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T225917Z +LAST-MODIFIED:20100625T230349Z +DTSTAMP:20100625T230349Z +UID:859d4dfe-1eae-4b3b-9d42-438d500ca455 +SUMMARY:Thanksgiving Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20111124 +DTEND;VALUE=DATE:20111125 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T225933Z +LAST-MODIFIED:20100625T230400Z +DTSTAMP:20100625T230400Z +UID:06a9583b-01fb-4ed4-926a-ac995e5dac6d +SUMMARY:Christmas Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20111226 +DTEND;VALUE=DATE:20111227 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:1 +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T230426Z +LAST-MODIFIED:20100625T230432Z +DTSTAMP:20100625T230432Z +UID:ec58e9d8-27fa-4f3a-8cb2-8073b860982f +SUMMARY:New Year's Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20120102 +DTEND;VALUE=DATE:20120103 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T230442Z +LAST-MODIFIED:20100625T230451Z +DTSTAMP:20100625T230451Z +UID:e3e1f541-ad97-4683-ab18-13c9d8620130 +SUMMARY:Birthday of Martin Luther King\, Jr. (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20120116 +DTEND;VALUE=DATE:20120117 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T230503Z +LAST-MODIFIED:20100625T230510Z +DTSTAMP:20100625T230510Z +UID:b2f98535-66c6-423f-846d-7be00b63d315 +SUMMARY:Washington's Birthday (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20120220 +DTEND;VALUE=DATE:20120221 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T230528Z +LAST-MODIFIED:20100625T230537Z +DTSTAMP:20100625T230537Z +UID:6b1d9233-40b7-406b-a98c-769b2681d9ad +SUMMARY:Memorial Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20120528 +DTEND;VALUE=DATE:20120529 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T230550Z +LAST-MODIFIED:20100625T230558Z +DTSTAMP:20100625T230558Z +UID:9c6685c4-cf34-406d-a52d-457432cd2e72 +SUMMARY:Independence Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20120704 +DTEND;VALUE=DATE:20120705 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T230608Z +LAST-MODIFIED:20100625T230616Z +DTSTAMP:20100625T230616Z +UID:64e8f36b-30ca-4a68-bb68-b6be1bac33e7 +SUMMARY:Labor Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20120903 +DTEND;VALUE=DATE:20120904 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T230625Z +LAST-MODIFIED:20100625T230633Z +DTSTAMP:20100625T230633Z +UID:0feaf063-8cc3-4dcc-80a4-3006dd3ee395 +SUMMARY:Columbus Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20121008 +DTEND;VALUE=DATE:20121009 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T230640Z +LAST-MODIFIED:20100625T230646Z +DTSTAMP:20100625T230646Z +UID:a3ab943c-efa6-403a-94d6-266351d1bb9a +SUMMARY:Veterans Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20121112 +DTEND;VALUE=DATE:20121113 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T230652Z +LAST-MODIFIED:20100625T230659Z +DTSTAMP:20100625T230659Z +UID:e29b795e-0f37-4ea4-84e6-0f277cf5a8ba +SUMMARY:Thanksgiving Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20121122 +DTEND;VALUE=DATE:20121123 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T230711Z +LAST-MODIFIED:20100625T230719Z +DTSTAMP:20100625T230719Z +UID:9986c0ee-983a-4538-ad4a-4688fe5b5636 +SUMMARY:Christmas Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20121225 +DTEND;VALUE=DATE:20121226 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T230745Z +LAST-MODIFIED:20100625T230752Z +DTSTAMP:20100625T230752Z +UID:0b3180aa-65a8-4732-bb4c-36cd3c79a650 +SUMMARY:New Year's Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20130101 +DTEND;VALUE=DATE:20130102 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T230800Z +LAST-MODIFIED:20100625T230806Z +DTSTAMP:20100625T230806Z +UID:19f0cc25-bec1-4e84-915b-a70bb532fcfb +SUMMARY:Birthday of Martin Luther King\, Jr. (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20130121 +DTEND;VALUE=DATE:20130122 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T230816Z +LAST-MODIFIED:20100625T230822Z +DTSTAMP:20100625T230822Z +UID:db225df5-a20c-4889-9511-bcf81acad6e4 +SUMMARY:Washington's Birthday (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20130218 +DTEND;VALUE=DATE:20130219 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T230833Z +LAST-MODIFIED:20100625T230839Z +DTSTAMP:20100625T230839Z +UID:9b12b9be-0f16-42ff-97a5-53a67cc46f2c +SUMMARY:Memorial Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20130527 +DTEND;VALUE=DATE:20130528 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T230849Z +LAST-MODIFIED:20100625T230856Z +DTSTAMP:20100625T230856Z +UID:9b6b81e1-e4f8-490b-9062-24726dbf27f2 +SUMMARY:Independence Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20130704 +DTEND;VALUE=DATE:20130705 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T230905Z +LAST-MODIFIED:20100625T230910Z +DTSTAMP:20100625T230910Z +UID:5b722439-56b8-4f26-a21d-c2c99898bf47 +SUMMARY:Labor Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20130902 +DTEND;VALUE=DATE:20130903 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T230922Z +LAST-MODIFIED:20100625T230927Z +DTSTAMP:20100625T230927Z +UID:e44229c1-89cd-4c96-8094-c09a56f1a028 +SUMMARY:Columbus Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20131014 +DTEND;VALUE=DATE:20131015 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T230935Z +LAST-MODIFIED:20100625T230941Z +DTSTAMP:20100625T230941Z +UID:fea08c25-1cbc-4d7b-ab30-87bf5b87c4e0 +SUMMARY:Veterans Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20131111 +DTEND;VALUE=DATE:20131112 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T230955Z +LAST-MODIFIED:20100625T231001Z +DTSTAMP:20100625T231001Z +UID:12eb5737-4e7f-4278-9f8f-36202257d434 +SUMMARY:Thanksgiving Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20131128 +DTEND;VALUE=DATE:20131129 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T231008Z +LAST-MODIFIED:20100625T231016Z +DTSTAMP:20100625T231016Z +UID:a2acacd4-e541-4334-b5ad-e2e215cf32fc +SUMMARY:Christmas Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20131225 +DTEND;VALUE=DATE:20131226 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232203Z +LAST-MODIFIED:20100625T232211Z +DTSTAMP:20100625T232211Z +UID:784620c2-befa-40af-9aa2-5232ceebf80f +SUMMARY:New Year's Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20140101 +DTEND;VALUE=DATE:20140102 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232227Z +LAST-MODIFIED:20100625T232233Z +DTSTAMP:20100625T232233Z +UID:7d90511b-c99b-4937-af69-1da4781923c8 +SUMMARY:Birthday of Martin Luther King\, Jr. (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20140120 +DTEND;VALUE=DATE:20140121 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232242Z +LAST-MODIFIED:20100625T232249Z +DTSTAMP:20100625T232249Z +UID:760534b9-7b1c-41b1-8797-6dd6583d7b98 +SUMMARY:Washington's Birthday (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20140217 +DTEND;VALUE=DATE:20140218 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232259Z +LAST-MODIFIED:20100625T232307Z +DTSTAMP:20100625T232307Z +UID:3a66f3a1-bce5-4dcf-a815-2d50057ba758 +SUMMARY:Memorial Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20140526 +DTEND;VALUE=DATE:20140527 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232324Z +LAST-MODIFIED:20100625T232333Z +DTSTAMP:20100625T232333Z +UID:8dec8541-cada-48c8-bae1-7b64e5bd96f1 +SUMMARY:Independence Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20140704 +DTEND;VALUE=DATE:20140705 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232344Z +LAST-MODIFIED:20100625T232352Z +DTSTAMP:20100625T232352Z +UID:f1a94187-1b1a-46eb-9eba-9f2e03efff7e +SUMMARY:Labor Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20140901 +DTEND;VALUE=DATE:20140902 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232358Z +LAST-MODIFIED:20100625T232405Z +DTSTAMP:20100625T232405Z +UID:aa078715-e039-42e8-bfca-ceb787c3eceb +SUMMARY:Columbus Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20141013 +DTEND;VALUE=DATE:20141014 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232416Z +LAST-MODIFIED:20100625T232424Z +DTSTAMP:20100625T232424Z +UID:5a0ec2c2-3b70-4440-928d-d3c435205736 +SUMMARY:Veterans Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20141111 +DTEND;VALUE=DATE:20141112 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232432Z +LAST-MODIFIED:20100625T232438Z +DTSTAMP:20100625T232438Z +UID:ec969553-a7c6-4ad1-bbf9-061bd48b91dc +SUMMARY:Thanksgiving Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20141127 +DTEND;VALUE=DATE:20141128 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232449Z +LAST-MODIFIED:20100625T232456Z +DTSTAMP:20100625T232456Z +UID:f01417f3-4570-4711-9abc-bf20a01cea0c +SUMMARY:Christmas Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20141225 +DTEND;VALUE=DATE:20141226 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232517Z +LAST-MODIFIED:20100625T232523Z +DTSTAMP:20100625T232523Z +UID:f37395e6-1056-4175-a457-399cdab4eda2 +SUMMARY:New Year's Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20150101 +DTEND;VALUE=DATE:20150102 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232529Z +LAST-MODIFIED:20100625T232536Z +DTSTAMP:20100625T232536Z +UID:3f07705a-a1fb-4915-8dbd-4e1513ade06b +SUMMARY:Birthday of Martin Luther King\, Jr. (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20150119 +DTEND;VALUE=DATE:20150120 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232547Z +LAST-MODIFIED:20100625T232553Z +DTSTAMP:20100625T232553Z +UID:2dff572d-fc88-4925-8817-960c9f913974 +SUMMARY:Washington's Birthday (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20150216 +DTEND;VALUE=DATE:20150217 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232604Z +LAST-MODIFIED:20100625T232611Z +DTSTAMP:20100625T232611Z +UID:62aa4902-1113-4e89-ab08-9a344ff2709f +SUMMARY:Memorial Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20150525 +DTEND;VALUE=DATE:20150526 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232621Z +LAST-MODIFIED:20100625T232628Z +DTSTAMP:20100625T232628Z +UID:ed53ac56-5535-4877-acf1-84919821da44 +SUMMARY:Independence Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20150703 +DTEND;VALUE=DATE:20150704 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232639Z +LAST-MODIFIED:20100625T232645Z +DTSTAMP:20100625T232645Z +UID:fdb30d77-208e-4b9c-acbb-877549b0c60e +SUMMARY:Labor Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20150907 +DTEND;VALUE=DATE:20150908 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232656Z +LAST-MODIFIED:20100625T232703Z +DTSTAMP:20100625T232703Z +UID:a0add000-7f34-4a12-8cfc-dbf98872b532 +SUMMARY:Columbus Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20151012 +DTEND;VALUE=DATE:20151013 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232710Z +LAST-MODIFIED:20100625T232716Z +DTSTAMP:20100625T232716Z +UID:57bbf2f3-e92c-4864-9171-80e1aefa9b25 +SUMMARY:Veterans Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20151111 +DTEND;VALUE=DATE:20151112 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232724Z +LAST-MODIFIED:20100625T232730Z +DTSTAMP:20100625T232730Z +UID:17357cbf-822b-4e16-97fe-9212a98262ea +SUMMARY:Thanksgiving Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20151126 +DTEND;VALUE=DATE:20151127 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232740Z +LAST-MODIFIED:20100625T232746Z +DTSTAMP:20100625T232746Z +UID:a5ae0bfb-a761-48a7-80b3-85c4dec3459d +SUMMARY:Christmas Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20151225 +DTEND;VALUE=DATE:20151226 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232806Z +LAST-MODIFIED:20100625T232813Z +DTSTAMP:20100625T232813Z +UID:e84cfee3-d9f8-4088-9709-995258432145 +SUMMARY:New Year's Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20160101 +DTEND;VALUE=DATE:20160102 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232819Z +LAST-MODIFIED:20100625T232826Z +DTSTAMP:20100625T232826Z +UID:2c19281f-8160-4886-ae2b-35f5a83d32ec +SUMMARY:Birthday of Martin Luther King\, Jr. (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20160118 +DTEND;VALUE=DATE:20160119 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232836Z +LAST-MODIFIED:20100625T232842Z +DTSTAMP:20100625T232842Z +UID:ccb48d24-c58f-478b-a941-1706c642cd39 +SUMMARY:Washington's Birthday (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20160215 +DTEND;VALUE=DATE:20160216 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232851Z +LAST-MODIFIED:20100625T232858Z +DTSTAMP:20100625T232858Z +UID:067c379d-cdfa-49f5-9de7-65be7b5b0158 +SUMMARY:Memorial Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20160530 +DTEND;VALUE=DATE:20160531 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232911Z +LAST-MODIFIED:20100625T232917Z +DTSTAMP:20100625T232917Z +UID:dddf90ce-a186-4bfb-aaf8-878e07450fcf +SUMMARY:Independence Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20160704 +DTEND;VALUE=DATE:20160705 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232925Z +LAST-MODIFIED:20100625T232931Z +DTSTAMP:20100625T232931Z +UID:c734ef2f-53a9-48f3-9da0-f70b70c0d881 +SUMMARY:Labor Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20160905 +DTEND;VALUE=DATE:20160906 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232938Z +LAST-MODIFIED:20100625T232944Z +DTSTAMP:20100625T232944Z +UID:c9d05e3a-bd7d-4a5d-94ba-020335a9b3aa +SUMMARY:Columbus Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20161010 +DTEND;VALUE=DATE:20161011 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T232953Z +LAST-MODIFIED:20100625T233000Z +DTSTAMP:20100625T233000Z +UID:819d893b-af2b-4423-aaff-c4286ee349ee +SUMMARY:Veterans Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20161111 +DTEND;VALUE=DATE:20161112 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233007Z +LAST-MODIFIED:20100625T233013Z +DTSTAMP:20100625T233013Z +UID:9af9e892-e3a8-4a2c-a130-4e25c92c2baa +SUMMARY:Thanksgiving Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20161124 +DTEND;VALUE=DATE:20161125 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233022Z +LAST-MODIFIED:20100625T233028Z +DTSTAMP:20100625T233028Z +UID:8b5ff666-fec7-487b-99c0-aa621548e9ea +SUMMARY:Christmas Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20161226 +DTEND;VALUE=DATE:20161227 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233045Z +LAST-MODIFIED:20100625T233052Z +DTSTAMP:20100625T233052Z +UID:9943b6be-9c4b-4fc3-b690-80114b559f26 +SUMMARY:New Year's Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20170102 +DTEND;VALUE=DATE:20170103 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233101Z +LAST-MODIFIED:20100625T233109Z +DTSTAMP:20100625T233109Z +UID:4c3b85b1-2144-470e-b986-418c087cb5a0 +SUMMARY:Birthday of Martin Luther King\, Jr. (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20170116 +DTEND;VALUE=DATE:20170117 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233117Z +LAST-MODIFIED:20100625T233123Z +DTSTAMP:20100625T233123Z +UID:c09d860f-6464-41c4-b2ef-25011d25cecf +SUMMARY:Washington's Birthday (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20170220 +DTEND;VALUE=DATE:20170221 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233131Z +LAST-MODIFIED:20100625T233138Z +DTSTAMP:20100625T233138Z +UID:ba4752c9-4032-4385-820d-2c4213e14fa5 +SUMMARY:Memorial Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20170529 +DTEND;VALUE=DATE:20170530 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233145Z +LAST-MODIFIED:20100625T233151Z +DTSTAMP:20100625T233151Z +UID:59e934c5-672e-4ccc-a686-9d9c495b3883 +SUMMARY:Independence Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20170704 +DTEND;VALUE=DATE:20170705 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233158Z +LAST-MODIFIED:20100625T233205Z +DTSTAMP:20100625T233205Z +UID:90340458-52db-4697-b2d7-8bfd51cfaaf7 +SUMMARY:Labor Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20170904 +DTEND;VALUE=DATE:20170905 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233212Z +LAST-MODIFIED:20100625T233218Z +DTSTAMP:20100625T233218Z +UID:70c9c1e3-31a2-4b0a-9712-69a184fbfb7c +SUMMARY:Columbus Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20171009 +DTEND;VALUE=DATE:20171010 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233225Z +LAST-MODIFIED:20100625T233232Z +DTSTAMP:20100625T233232Z +UID:e9f86e06-9f1c-44bc-b3db-43a220802fe9 +SUMMARY:Veterans Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20171110 +DTEND;VALUE=DATE:20171111 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233240Z +LAST-MODIFIED:20100625T233246Z +DTSTAMP:20100625T233246Z +UID:d82d8d11-0793-4a68-a876-83c7e29736d0 +SUMMARY:Thanksgiving Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20171123 +DTEND;VALUE=DATE:20171124 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233255Z +LAST-MODIFIED:20100625T233302Z +DTSTAMP:20100625T233302Z +UID:4526de1c-b74d-4643-b299-4c4c42ed82bb +SUMMARY:Christmas Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20171225 +DTEND;VALUE=DATE:20171226 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233318Z +LAST-MODIFIED:20100625T233324Z +DTSTAMP:20100625T233324Z +UID:885fb7d7-ea3f-4938-9e40-f612986566f3 +SUMMARY:New Year's Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20180101 +DTEND;VALUE=DATE:20180102 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233329Z +LAST-MODIFIED:20100625T233335Z +DTSTAMP:20100625T233335Z +UID:89c44a14-aee8-49ce-8528-0e7a8bbe6e06 +SUMMARY:Birthday of Martin Luther King\, Jr. (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20180115 +DTEND;VALUE=DATE:20180116 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233342Z +LAST-MODIFIED:20100625T233348Z +DTSTAMP:20100625T233348Z +UID:e65fa03d-c681-458c-a26b-4206e3297280 +SUMMARY:Washington's Birthday (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20180219 +DTEND;VALUE=DATE:20180220 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233358Z +LAST-MODIFIED:20100625T233404Z +DTSTAMP:20100625T233404Z +UID:f0a9e5e8-077c-424a-a668-970951c307d5 +SUMMARY:Memorial Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20180528 +DTEND;VALUE=DATE:20180529 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233413Z +LAST-MODIFIED:20100625T233419Z +DTSTAMP:20100625T233419Z +UID:f00cd4b3-3f19-4b46-9d69-e33790eda2ea +SUMMARY:Independence Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20180704 +DTEND;VALUE=DATE:20180705 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233426Z +LAST-MODIFIED:20100625T233432Z +DTSTAMP:20100625T233432Z +UID:d0cdf502-fcdd-48ee-8cdc-2a6bc947a906 +SUMMARY:Labor Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20180903 +DTEND;VALUE=DATE:20180904 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233441Z +LAST-MODIFIED:20100625T233447Z +DTSTAMP:20100625T233447Z +UID:4ca554db-e59d-4de8-b043-d28988ae0b39 +SUMMARY:Columbus Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20181008 +DTEND;VALUE=DATE:20181009 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233455Z +LAST-MODIFIED:20100625T233502Z +DTSTAMP:20100625T233502Z +UID:876c8ca9-48a3-405d-86a1-432a66fb0ceb +SUMMARY:Veterans Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20181112 +DTEND;VALUE=DATE:20181113 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233510Z +LAST-MODIFIED:20100625T233516Z +DTSTAMP:20100625T233516Z +UID:5adaf364-32e7-4f8e-bc69-ad93c1c3aa15 +SUMMARY:Thanksgiving Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20181122 +DTEND;VALUE=DATE:20181123 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233527Z +LAST-MODIFIED:20100625T233532Z +DTSTAMP:20100625T233532Z +UID:05f95ef4-6098-4337-8d15-8b57ff37f251 +SUMMARY:Christmas Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20181225 +DTEND;VALUE=DATE:20181226 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233551Z +LAST-MODIFIED:20100625T233558Z +DTSTAMP:20100625T233558Z +UID:43fc5a98-b57a-456b-a50b-8e0e1de018ff +SUMMARY:New Year's Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20190101 +DTEND;VALUE=DATE:20190102 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233603Z +LAST-MODIFIED:20100625T233610Z +DTSTAMP:20100625T233610Z +UID:e1f23ce9-56fe-47e0-8f0e-2d408847c5d6 +SUMMARY:Birthday of Martin Luther King\, Jr. (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20190121 +DTEND;VALUE=DATE:20190122 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233618Z +LAST-MODIFIED:20100625T233626Z +DTSTAMP:20100625T233626Z +UID:3e8909b9-74aa-41bb-b8e7-23beff8c362d +SUMMARY:Washington's Birthday (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20190218 +DTEND;VALUE=DATE:20190219 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233635Z +LAST-MODIFIED:20100625T233641Z +DTSTAMP:20100625T233641Z +UID:1aac6453-abbf-47de-bf5d-4bd0dfb9cd44 +SUMMARY:Memorial Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20190527 +DTEND;VALUE=DATE:20190528 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233650Z +LAST-MODIFIED:20100625T233700Z +DTSTAMP:20100625T233700Z +UID:dce26b68-5fd3-4b45-a8a6-cb68c8946935 +SUMMARY:Independence Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20190704 +DTEND;VALUE=DATE:20190705 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233710Z +LAST-MODIFIED:20100625T233716Z +DTSTAMP:20100625T233716Z +UID:199ec585-69d7-45df-9cf2-c1e83d570e3a +SUMMARY:Labor Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20190902 +DTEND;VALUE=DATE:20190903 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233723Z +LAST-MODIFIED:20100625T233728Z +DTSTAMP:20100625T233728Z +UID:61d39eb7-ae9a-4667-bbfa-6107df1e09e7 +SUMMARY:Columbus Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20191014 +DTEND;VALUE=DATE:20191015 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233735Z +LAST-MODIFIED:20100625T233741Z +DTSTAMP:20100625T233741Z +UID:290c9fe0-e39e-425a-95b4-831945c6b8f2 +SUMMARY:Veterans Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20191111 +DTEND;VALUE=DATE:20191112 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233747Z +LAST-MODIFIED:20100625T233754Z +DTSTAMP:20100625T233754Z +UID:b7680e77-a64f-4861-bb35-f2d917a9c437 +SUMMARY:Thanksgiving Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20191128 +DTEND;VALUE=DATE:20191129 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233801Z +LAST-MODIFIED:20100625T233808Z +DTSTAMP:20100625T233808Z +UID:b234249f-565e-4159-b4e5-b0314c48a191 +SUMMARY:Christmas Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20191225 +DTEND;VALUE=DATE:20191226 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233825Z +LAST-MODIFIED:20100625T233832Z +DTSTAMP:20100625T233832Z +UID:a48665de-2184-4468-9f8a-c21cf582455f +SUMMARY:New Year's Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20200101 +DTEND;VALUE=DATE:20200102 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233838Z +LAST-MODIFIED:20100625T233844Z +DTSTAMP:20100625T233844Z +UID:f03dfc3e-924e-4574-87e7-e30fa1166025 +SUMMARY:Birthday of Martin Luther King\, Jr. (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20200120 +DTEND;VALUE=DATE:20200121 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233852Z +LAST-MODIFIED:20100625T233858Z +DTSTAMP:20100625T233858Z +UID:95d5c62a-4a58-49cf-9c91-d66a0fae1acd +SUMMARY:Washington's Birthday (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20200217 +DTEND;VALUE=DATE:20200218 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233906Z +LAST-MODIFIED:20100625T233912Z +DTSTAMP:20100625T233912Z +UID:dd42517c-228b-44b3-a3ac-0bd4a7aadb75 +SUMMARY:Memorial Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20200525 +DTEND;VALUE=DATE:20200526 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233921Z +LAST-MODIFIED:20100625T233927Z +DTSTAMP:20100625T233927Z +UID:cb3e2de7-2c55-4882-820a-df01c8c6aae7 +SUMMARY:Independence Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20200703 +DTEND;VALUE=DATE:20200704 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233935Z +LAST-MODIFIED:20100625T233941Z +DTSTAMP:20100625T233941Z +UID:65eb7b18-064f-4939-a840-f11ef4bec981 +SUMMARY:Labor Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20200907 +DTEND;VALUE=DATE:20200908 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T233952Z +LAST-MODIFIED:20100625T233959Z +DTSTAMP:20100625T233959Z +UID:2d13e3c5-8a26-49be-bdbc-9a1e9715ae4b +SUMMARY:Columbus Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20201012 +DTEND;VALUE=DATE:20201013 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T234006Z +LAST-MODIFIED:20100625T234017Z +DTSTAMP:20100625T234017Z +UID:8ffd1252-87f4-481c-b46a-c2aa4cba74c7 +SUMMARY:Veterans Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20201111 +DTEND;VALUE=DATE:20201112 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T234023Z +LAST-MODIFIED:20100625T234029Z +DTSTAMP:20100625T234029Z +UID:557d2349-5c21-45c8-aad7-9c24a6aceb66 +SUMMARY:Thanksgiving Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20201126 +DTEND;VALUE=DATE:20201127 +TRANSP:TRANSPARENT +END:VEVENT +BEGIN:VEVENT +CREATED:20100625T234036Z +LAST-MODIFIED:20100625T234041Z +DTSTAMP:20100625T234041Z +UID:1c5f2dc5-7cea-48cd-8c36-905930f8eee9 +SUMMARY:Christmas Day (US-OPM) +CATEGORIES:Holidays +DTSTART;VALUE=DATE:20201225 +DTEND;VALUE=DATE:20201226 +TRANSP:TRANSPARENT +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/src/main/resources/ical4j.properties b/src/main/resources/ical4j.properties new file mode 100644 index 00000000..e69de29b diff --git a/src/main/resources/seasons.ics b/src/main/resources/seasons.ics new file mode 100644 index 00000000..d4671905 --- /dev/null +++ b/src/main/resources/seasons.ics @@ -0,0 +1,600 @@ +BEGIN:VCALENDAR +METHOD:PUBLISH +PRODID:-//Apple Inc.//iCal 3.0//EN +CALSCALE:GREGORIAN +X-WR-CALNAME:EarthSeasons +X-WR-CALDESC:This calendar contains all of the Vernal Equinox, Summer Solstice, Autumnal Equinox, and Winter Solstice times for the Northern Hemisphere through the year 2020. +X-WR-RELCALID:B35600F3-5ED9-4984-8182-D4BD6C1DF452 +VERSION:2.0 +X-WR-TIMEZONE:US/Central +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:E0E8F3C4-6877-11D7-AB01-000393AF7662 +DTSTART:20111222T053000Z +DTSTAMP:20070410T001744Z +SUMMARY:Winter Solstice +CREATED:20091115T181129Z +DTEND:20111222T053000Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:D5FCDAAB-6875-11D7-AB01-000393AF7662 +DTSTART:20070923T095100Z +DTSTAMP:20070409T234742Z +SUMMARY:Autumnal Equinox +CREATED:20091115T181129Z +DTEND:20070923T095100Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:8710C20F-6875-11D7-AB01-000393AF7662 +DTSTART:20061222T002200Z +DTSTAMP:20070409T234409Z +SUMMARY:Winter Solstice +CREATED:20091115T181128Z +DTEND:20061222T002200Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:BA88E98A-6878-11D7-AB01-000393AF7662 +DTSTART:20130922T204400Z +DTSTAMP:20070410T002413Z +SUMMARY:Autumnal Equinox +CREATED:20091115T181129Z +DTEND:20130922T204400Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:6 +TRANSP:OPAQUE +UID:47FF98BF-687D-11D7-AB01-000393AF7662 +DTSTART:20200620T214300Z +DTSTAMP:20070410T012436Z +SUMMARY:Summer Solstice +CREATED:20091115T181128Z +DTEND:20200620T214300Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:5 +TRANSP:OPAQUE +UID:64625F50-6879-11D7-AB01-000393AF7662 +DTSTART:20141221T230300Z +DTSTAMP:20070410T002809Z +SUMMARY:Winter Solstice +CREATED:20091115T181128Z +DTEND:20141221T230300Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:FA0F80CE-687B-11D7-AB01-000393AF7662 +DTSTART:20190621T155400Z +DTSTAMP:20070410T012152Z +SUMMARY:Summer Solstice +CREATED:20091115T181130Z +DTEND:20190621T155400Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:9D605B9C-6876-11D7-AB01-000393AF7662 +DTSTART:20090621T054500Z +DTSTAMP:20070410T000512Z +SUMMARY:Summer Solstice +CREATED:20091115T181128Z +DTEND:20090621T054500Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:589557A2-687B-11D7-AB01-000393AF7662 +DTSTART:20180320T161500Z +DTSTAMP:20070410T003950Z +SUMMARY:Vernal Equinox +CREATED:20091115T181128Z +DTEND:20180320T161500Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:425B72D2-6876-11D7-AB01-000393AF7662 +DTSTART:20080922T154400Z +DTSTAMP:20070409T235326Z +SUMMARY:Autumnal Equinox +CREATED:20091115T181128Z +DTEND:20080922T154400Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:A3E8121C-6879-11D7-AB01-000393AF7662 +DTSTART:20150621T163800Z +DTSTAMP:20070410T003013Z +SUMMARY:Summer Solstice +CREATED:20091115T181129Z +DTEND:20150621T163800Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:85CBA533-687D-11D7-AB01-000393AF7662 +DTSTART:20201221T100200Z +DTSTAMP:20070410T012543Z +SUMMARY:Winter Solstice +CREATED:20091115T181128Z +DTEND:20201221T100200Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:6 +TRANSP:OPAQUE +UID:1D64B1E2-687B-11D7-AB01-000393AF7662 +DTSTART:20170922T200200Z +DTSTAMP:20070410T003748Z +SUMMARY:Autumnal Equinox +CREATED:20091115T181127Z +DTEND:20170922T200200Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:6 +TRANSP:OPAQUE +UID:133688F3-6878-11D7-AB01-000393AF7662 +DTSTART:20120620T230900Z +DTSTAMP:20070410T001906Z +SUMMARY:Summer Solstice +CREATED:20091115T181127Z +DTEND:20120620T230900Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:36E26948-6877-11D7-AB01-000393AF7662 +DTSTART:20100923T030900Z +DTSTAMP:20070410T000928Z +SUMMARY:Autumnal Equinox +CREATED:20091115T181127Z +DTEND:20100923T030900Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:9954D2D2-687A-11D7-AB01-000393AF7662 +DTSTART:20160922T142100Z +DTSTAMP:20070410T003352Z +SUMMARY:Autumnal Equinox +CREATED:20091115T181128Z +DTEND:20160922T142100Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:FC378A9C-6876-11D7-AB01-000393AF7662 +DTSTART:20100320T173200Z +DTSTAMP:20070410T000719Z +SUMMARY:Vernal Equinox +CREATED:20091115T181131Z +DTEND:20100320T173200Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:C5377646-687A-11D7-AB01-000393AF7662 +DTSTART:20161221T104400Z +DTSTAMP:20070410T003431Z +SUMMARY:Winter Solstice +CREATED:20091115T181129Z +DTEND:20161221T104400Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:6 +TRANSP:OPAQUE +UID:6DDE0568-6878-11D7-AB01-000393AF7662 +DTSTART:20121221T111100Z +DTSTAMP:20070410T002007Z +SUMMARY:Winter Solstice +CREATED:20091115T181128Z +DTEND:20121221T111100Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:E17A7B6A-687A-11D7-AB01-000393AF7662 +DTSTART:20170320T102800Z +DTSTAMP:20070410T003505Z +SUMMARY:Vernal Equinox +CREATED:20091115T181129Z +DTEND:20170320T102800Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:FC332B42-6877-11D7-AB01-000393AF7662 +DTSTART:20120320T051400Z +DTSTAMP:20070410T001831Z +SUMMARY:Vernal Equinox +CREATED:20091115T181131Z +DTEND:20120320T051400Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:4B2C4C29-6878-11D7-AB01-000393AF7662 +DTSTART:20120922T144900Z +DTSTAMP:20070410T001937Z +SUMMARY:Autumnal Equinox +CREATED:20091115T181128Z +DTEND:20120922T144900Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:06A51795-687D-11D7-AB01-000393AF7662 +DTSTART:20191222T041900Z +DTSTAMP:20070410T012319Z +SUMMARY:Winter Solstice +CREATED:20091115T181127Z +DTEND:20191222T041900Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:6 +TRANSP:OPAQUE +UID:BD792A22-687B-11D7-AB01-000393AF7662 +DTSTART:20181221T222200Z +DTSTAMP:20070410T012026Z +SUMMARY:Winter Solstice +CREATED:20091115T181129Z +DTEND:20181221T222200Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:30C535A1-6879-11D7-AB01-000393AF7662 +DTSTART:20140923T022900Z +DTSTAMP:20070410T002747Z +SUMMARY:Autumnal Equinox +CREATED:20091115T181127Z +DTEND:20140923T022900Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:6CE97CB7-6875-11D7-AB01-000393AF7662 +DTSTART:20060923T040300Z +DTSTAMP:20070409T234337Z +SUMMARY:Autumnal Equinox +CREATED:20091115T181128Z +DTEND:20060923T040300Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:5 +TRANSP:OPAQUE +UID:78C7D1E4-6877-11D7-AB01-000393AF7662 +DTSTART:20110320T232100Z +DTSTAMP:20070410T001344Z +SUMMARY:Vernal Equinox +CREATED:20091115T181128Z +DTEND:20110320T232100Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:26176C76-687D-11D7-AB01-000393AF7662 +DTSTART:20200320T034900Z +DTSTAMP:20070410T012402Z +SUMMARY:Vernal Equinox +CREATED:20091115T181127Z +DTEND:20200320T034900Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:E3D02AE3-6879-11D7-AB01-000393AF7662 +DTSTART:20151222T044800Z +DTSTAMP:20070410T003123Z +SUMMARY:Winter Solstice +CREATED:20091115T181129Z +DTEND:20151222T044800Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:107525BC-6876-11D7-AB01-000393AF7662 +DTSTART:20080320T054800Z +DTSTAMP:20070409T235141Z +SUMMARY:Vernal Equinox +CREATED:20091115T181127Z +DTEND:20080320T054800Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:A3AA0842-6875-11D7-AB01-000393AF7662 +DTSTART:20070321T000700Z +DTSTAMP:20070409T234521Z +SUMMARY:Vernal Equinox +CREATED:20091115T181128Z +DTEND:20070321T000700Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:8 +TRANSP:OPAQUE +UID:F0810C5B-6875-11D7-AB01-000393AF7662 +DTSTART:20071222T060800Z +DTSTAMP:20070409T235017Z +SUMMARY:Winter Solstice +CREATED:20091115T181129Z +DTEND:20071222T060800Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:6 +TRANSP:OPAQUE +UID:87FB4C04-6878-11D7-AB01-000393AF7662 +DTSTART:20130320T110200Z +DTSTAMP:20070410T002203Z +SUMMARY:Vernal Equinox +CREATED:20091115T181128Z +DTEND:20130320T110200Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:19F07040-6877-11D7-AB01-000393AF7662 +DTSTART:20100621T112800Z +DTSTAMP:20070410T000828Z +SUMMARY:Summer Solstice +CREATED:20091115T181127Z +DTEND:20100621T112800Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:5 +TRANSP:OPAQUE +UID:7FA978FD-687A-11D7-AB01-000393AF7662 +DTSTART:20160620T223400Z +DTSTAMP:20070410T003330Z +SUMMARY:Summer Solstice +CREATED:20091115T181128Z +DTEND:20160620T223400Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:BC01DF70-6875-11D7-AB01-000393AF7662 +DTSTART:20070621T180600Z +DTSTAMP:20070409T234609Z +SUMMARY:Summer Solstice +CREATED:20091115T181129Z +DTEND:20070621T180600Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:C8B0CD4C-6877-11D7-AB01-000393AF7662 +DTSTART:20110923T090400Z +DTSTAMP:20070410T001522Z +SUMMARY:Autumnal Equinox +CREATED:20091115T181129Z +DTEND:20110923T090400Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:10 +TRANSP:OPAQUE +UID:308BD832-6875-11D7-AB01-000393AF7662 +DTSTART:20060621T122600Z +DTSTAMP:20070409T233509Z +SUMMARY:Summer Solstice +CREATED:20091115T181127Z +DTEND:20060621T122600Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:A6298608-6878-11D7-AB01-000393AF7662 +DTSTART:20130621T050400Z +DTSTAMP:20070410T002339Z +SUMMARY:Summer Solstice +CREATED:20091115T181129Z +DTEND:20130621T050400Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:677A3ED8-687D-11D7-AB01-000393AF7662 +DTSTART:20200922T133000Z +DTSTAMP:20070410T012509Z +SUMMARY:Autumnal Equinox +CREATED:20091115T181128Z +DTEND:20200922T133000Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:6 +TRANSP:OPAQUE +UID:E148E3E4-687B-11D7-AB01-000393AF7662 +DTSTART:20190320T215800Z +DTSTAMP:20070410T012131Z +SUMMARY:Vernal Equinox +CREATED:20091115T181129Z +DTEND:20190320T215800Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:F925736E-687A-11D7-AB01-000393AF7662 +DTSTART:20170621T042400Z +DTSTAMP:20070410T003541Z +SUMMARY:Summer Solstice +CREATED:20091115T181130Z +DTEND:20170621T042400Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:6 +TRANSP:OPAQUE +UID:C1D76BDB-6876-11D7-AB01-000393AF7662 +DTSTART:20090922T211800Z +DTSTAMP:20070410T000601Z +SUMMARY:Autumnal Equinox +CREATED:20091115T181129Z +DTEND:20090922T211800Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:F9E3A9BC-6878-11D7-AB01-000393AF7662 +DTSTART:20140320T165700Z +DTSTAMP:20070410T002627Z +SUMMARY:Vernal Equinox +CREATED:20091115T181130Z +DTEND:20140320T165700Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:6 +TRANSP:OPAQUE +UID:1719D691-6879-11D7-AB01-000393AF7662 +DTSTART:20140621T105100Z +DTSTAMP:20070410T002651Z +SUMMARY:Summer Solstice +CREATED:20091115T181127Z +DTEND:20140621T105100Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:D72CDF90-6876-11D7-AB01-000393AF7662 +DTSTART:20091221T174700Z +DTSTAMP:20070410T000630Z +SUMMARY:Winter Solstice +CREATED:20091115T181129Z +DTEND:20091221T174700Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:3BED6989-687B-11D7-AB01-000393AF7662 +DTSTART:20171221T162800Z +DTSTAMP:20070410T003822Z +SUMMARY:Winter Solstice +CREATED:20091115T181128Z +DTEND:20171221T162800Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:16 +TRANSP:OPAQUE +UID:990A4B78-687C-11D7-AB01-000393AF7662 +DTSTART:20190923T075000Z +DTSTAMP:20070410T012227Z +SUMMARY:Autumnal Equinox +CREATED:20091115T181128Z +DTEND:20190923T075000Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:A1932616-6877-11D7-AB01-000393AF7662 +DTSTART:20110621T171600Z +DTSTAMP:20070410T001444Z +SUMMARY:Summer Solstice +CREATED:20091115T181128Z +DTEND:20110621T171600Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:D858FB09-6878-11D7-AB01-000393AF7662 +DTSTART:20131221T171100Z +DTSTAMP:20070410T002508Z +SUMMARY:Winter Solstice +CREATED:20091115T181129Z +DTEND:20131221T171100Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:8 +TRANSP:OPAQUE +UID:C138E162-6879-11D7-AB01-000393AF7662 +DTSTART:20150923T082000Z +DTSTAMP:20070410T003042Z +SUMMARY:Autumnal Equinox +CREATED:20091115T181129Z +DTEND:20150923T082000Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:5 +TRANSP:OPAQUE +UID:4DA9C3D2-6877-11D7-AB01-000393AF7662 +DTSTART:20101221T233800Z +DTSTAMP:20070410T001021Z +SUMMARY:Winter Solstice +CREATED:20091115T181128Z +DTEND:20101221T233800Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:88E0847A-687B-11D7-AB01-000393AF7662 +DTSTART:20180621T100700Z +DTSTAMP:20070410T004035Z +SUMMARY:Summer Solstice +CREATED:20091115T181128Z +DTEND:20180621T100700Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:6270E19C-687A-11D7-AB01-000393AF7662 +DTSTART:20160320T043000Z +DTSTAMP:20070410T003259Z +SUMMARY:Vernal Equinox +CREATED:20091115T181128Z +DTEND:20160320T043000Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:65915B26-6876-11D7-AB01-000393AF7662 +DTSTART:20081221T120400Z +DTSTAMP:20070409T235630Z +SUMMARY:Winter Solstice +CREATED:20091115T181128Z +DTEND:20081221T120400Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:6 +TRANSP:OPAQUE +UID:87836E42-6876-11D7-AB01-000393AF7662 +DTSTART:20090320T114400Z +DTSTAMP:20070410T000434Z +SUMMARY:Vernal Equinox +CREATED:20091115T181128Z +DTEND:20090320T114400Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:7 +TRANSP:OPAQUE +UID:A5647333-687B-11D7-AB01-000393AF7662 +DTSTART:20180923T015400Z +DTSTAMP:20070410T004117Z +SUMMARY:Autumnal Equinox +CREATED:20091115T181129Z +DTEND:20180923T015400Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:6 +TRANSP:OPAQUE +UID:2B838FE8-6876-11D7-AB01-000393AF7662 +DTSTART:20080620T235900Z +DTSTAMP:20070409T235226Z +SUMMARY:Summer Solstice +CREATED:20091115T181127Z +DTEND:20080620T235900Z +END:VEVENT +BEGIN:VEVENT +SEQUENCE:6 +TRANSP:OPAQUE +UID:8758D02E-6879-11D7-AB01-000393AF7662 +DTSTART:20150320T224500Z +DTSTAMP:20070410T002935Z +SUMMARY:Vernal Equinox +CREATED:20091115T181128Z +DTEND:20150320T224500Z +END:VEVENT +END:VCALENDAR diff --git a/src/test/gunit/DateLexer.testsuite b/src/test/gunit/DateLexer.testsuite new file mode 100644 index 00000000..7ccea6a7 --- /dev/null +++ b/src/test/gunit/DateLexer.testsuite @@ -0,0 +1,437 @@ +gunit Date; +@header{package com.joestelmach.natty.generated;} + +JANUARY: +"january" OK +"januarys" OK +"jan" OK +"jan." OK + +FEBRUARY: +"february" OK +"februarys" OK +"feb" OK +"feb." OK + +MARCH: +"march" OK +"marches" OK +"mar" OK +"mar." OK + +APRIL: +"april" OK +"aprils" OK +"apr" OK +"apr." OK + +MAY: +"may" OK +"mays" OK + +JUNE: +"june" OK +"junes" OK +"jun" OK +"jun." OK + +JULY: +"july" OK +"julys" OK +"jul" OK +"jul." OK + +AUGUST: +"august" OK +"augusts" OK +"aug" OK +"aug." OK + +SEPTEMBER: +"september" OK +"septembers" OK +"sep" OK +"sep." OK +"sept" OK +"sept." OK + +OCTOBER: +"october" OK +"octobers" OK +"oct" OK +"oct." OK + +NOVEMBER: +"november" OK +"novembers" OK +"nov" OK +"nov." OK + +DECEMBER: +"december" OK +"decembers" OK +"dec" OK +"dec." OK + +SUNDAY: +"sunday" OK +"sundays" OK +"sun" OK +"sun." OK +"suns" OK +"suns." OK + +MONDAY: +"monday" OK +"mondays" OK +"mon" OK +"mon." OK +"mons" OK +"mons." OK + +TUESDAY: +"tuesday" OK +"tuesdays" OK +"tues" OK +"tues." OK +"tue" OK +"tue." OK + +WEDNESDAY: +"wednesday" OK +"wednesdays" OK +"wed" OK +"wed." OK +"weds" OK +"weds." OK + +THURSDAY: +"thursday" OK +"thursdays" OK +"thur" OK +"thur." OK +"thu" OK +"thu." OK +"thus" OK +"thus." OK +"thurs" OK +"thurs." OK + +FRIDAY: +"friday" OK +"fridays" OK +"fri" OK +"fri." OK +"fris" OK +"fris." OK + +SATURDAY: +"saturday" OK +"saturdays" OK +"sat" OK +"sat." OK +"sats" OK +"sats." OK +"weekend" OK + +HOUR: +"hour" OK +"hours" OK + +DAY: +"day" OK +"days" OK + +WEEK: +"week" OK +"weeks" OK + +MONTH: +"month" OK +"months" OK + +YEAR: +"year" OK +"years" OK + +TODAY:"today" OK + +NOW:"now" OK + +TOMORROW: +"tomorow" OK +"tomorrow" OK +"tommorow" OK +"tommorrow" OK + +YESTERDAY : "yesterday" OK + +// ********** time lexer rules ********** + +AM: +"am" OK +"a.m." OK +"a" OK + +PM: +"pm" OK +"p.m." OK +"p" OK + +T:"t" OK + +MILITARY_HOUR_SUFFIX:"h" OK + +MIDNIGHT: +"midnight" OK +"mid-night" OK + +NOON: +"noon" OK +"afternoon" OK +"after-noon" OK + +UTC: +"utc" OK +"gmt" OK +"z" OK + +EST: +"est" OK +"edt" OK +"et" OK + +PST: +"pst" OK +"pdt" OK +"pt" OK + +CST: +"cst" OK +"cdt" OK +"ct" OK + +MST: +"mst" OK +"mdt" OK +"mt" OK + +AKST: +"akst" OK +"akdt" OK +"akt" OK + +HAST: +"hast" OK +"hadt" OK +"hat" OK +"hst" OK + +// ********* numeric lexer rules ********** + +INT_00: "00" OK +INT_00: "0" FAIL +INT_01: "01" OK +INT_01: "1" FAIL +INT_02: "02" OK +INT_02: "2" FAIL +INT_03: "03" OK +INT_03: "3" FAIL +INT_04: "04" OK +INT_04: "4" FAIL +INT_05: "05" OK +INT_05: "6" FAIL +INT_06: "06" OK +INT_06: "6" FAIL +INT_07: "07" OK +INT_07: "7" FAIL +INT_08: "08" OK +INT_08: "8" FAIL +INT_09: "09" OK +INT_09: "9" FAIL +INT_0: "0" OK +INT_1: "1" OK +INT_2: "2" OK +INT_3: "3" OK +INT_4: "4" OK +INT_5: "5" OK +INT_6: "6" OK +INT_7: "7" OK +INT_8: "8" OK +INT_9: "9" OK +INT_10: "10" OK +INT_11: "11" OK +INT_12: "12" OK +INT_13: "13" OK +INT_14: "14" OK +INT_15: "15" OK +INT_16: "16" OK +INT_17: "17" OK +INT_18: "18" OK +INT_19: "19" OK +INT_20: "20" OK +INT_21: "21" OK +INT_22: "22" OK +INT_23: "23" OK +INT_24: "24" OK +INT_25: "25" OK +INT_26: "26" OK +INT_27: "27" OK +INT_28: "28" OK +INT_29: "29" OK +INT_30: "30" OK +INT_31: "31" OK +INT_32: "32" OK +INT_33: "33" OK +INT_34: "34" OK +INT_35: "35" OK +INT_36: "36" OK +INT_37: "37" OK +INT_38: "38" OK +INT_39: "39" OK +INT_40: "40" OK +INT_41: "41" OK +INT_42: "42" OK +INT_43: "43" OK +INT_44: "44" OK +INT_45: "45" OK +INT_46: "46" OK +INT_47: "47" OK +INT_48: "48" OK +INT_49: "49" OK +INT_50: "50" OK +INT_51: "51" OK +INT_52: "52" OK +INT_53: "53" OK +INT_54: "54" OK +INT_55: "55" OK +INT_56: "56" OK +INT_57: "57" OK +INT_58: "58" OK +INT_59: "59" OK +INT_60: "60" OK +INT_61: "61" OK +INT_62: "62" OK +INT_63: "63" OK +INT_64: "64" OK +INT_65: "65" OK +INT_66: "66" OK +INT_67: "67" OK +INT_68: "68" OK +INT_69: "69" OK +INT_70: "70" OK +INT_71: "71" OK +INT_72: "72" OK +INT_73: "73" OK +INT_74: "74" OK +INT_75: "75" OK +INT_76: "76" OK +INT_77: "77" OK +INT_78: "78" OK +INT_79: "79" OK +INT_80: "80" OK +INT_81: "81" OK +INT_82: "82" OK +INT_83: "83" OK +INT_84: "84" OK +INT_85: "85" OK +INT_86: "86" OK +INT_87: "87" OK +INT_88: "88" OK +INT_89: "89" OK +INT_90: "90" OK +INT_91: "91" OK +INT_92: "92" OK +INT_93: "93" OK +INT_94: "94" OK +INT_95: "95" OK +INT_96: "96" OK +INT_97: "97" OK +INT_98: "98" OK +INT_99: "99" OK + +ST: "st" OK +ND: "nd" OK +RD: "rd" OK +TH: "th" OK + +ONE: "one" OK +TWO: "two" OK +THREE: "three" OK +FOUR: "four" OK +FIVE: "five" OK +SIX: "six" OK +SEVEN: "seven" OK +EIGHT: "eight" OK +NINE: "nine" OK +TEN: "ten" OK +ELEVEN: "eleven" OK +TWELVE: "twelve" OK +THIRTEEN: "thirteen" OK +FOURTEEN: "fourteen" OK +FIFTEEN: "fifteen" OK +SIXTEEN: "sixteen" OK +SEVENTEEN: "seventeen" OK +EIGHTEEN: + "eighteen" OK + "eightteen" OK +NINETEEN: "nineteen" OK +TWENTY: "twenty" OK +THIRTY: "thirty" OK + +FIRST: "first" OK +SECOND: "second" OK +THIRD: "third" OK +FOURTH: "fourth" OK +FIFTH: "fifth" OK +SIXTH: "sixth" OK +SEVENTH: "seventh" OK +EIGHTH: "eighth" OK +NINTH: "ninth" OK +TENTH: "tenth" OK +ELEVENTH: "eleventh" OK +TWELFTH: "twelfth" OK +THIRTEENTH: "thirteenth" OK +FOURTEENTH: "fourteenth" OK +FIFTEENTH: "fifteenth" OK +SIXTEENTH: "sixteenth" OK +SEVENTEENTH: "seventeenth" OK +EIGHTEENTH: "eighteenth" OK +NINETEENTH: "nineteenth" OK +TWENTIETH: "twentieth" OK +THIRTIETH: "thirtieth" OK + +COLON: ":" OK +COMMA: "," OK +DASH: "-" OK +SLASH: "/" OK +DOT: "." OK +PLUS: "+" OK +SINGLE_QUOTE: "'" OK +IN: "in" OK +THE: "the" OK +AT: "at" OK +ON: "on" OK +OF: "of" OK +THIS: "this" OK +LAST: "last" OK +NEXT: "next" OK +PAST: "past" OK +COMING: "coming" OK +UPCOMING: "upcoming" OK +FROM: "from" OK +NOW: "now" OK +AGO: "ago" OK +BEFORE: "before" OK +AFTER: "after" OK + +WHITE_SPACE: +" " OK +" " OK +"\t" OK +"\t\t" OK +"\n" OK +"\n" OK +"\n\n" OK +"\r" OK +"\r\r" OK \ No newline at end of file diff --git a/src/test/gunit/DateParser.testsuite b/src/test/gunit/DateParser.testsuite deleted file mode 100644 index e6d1beff..00000000 --- a/src/test/gunit/DateParser.testsuite +++ /dev/null @@ -1,1757 +0,0 @@ -gunit Date; -@header{package com.joestelmach.natty.generated;} - -date_time: -" " FAIL -"seven years ago at 3pm" -> (DATE_TIME (RELATIVE_DATE (SEEK < by_day 7 year)) (EXPLICIT_TIME (HOURS_OF_DAY 3) (MINUTES_OF_HOUR 0) pm)) -"1st oct in the year '89 1300 hours" -> (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 89)) (EXPLICIT_TIME (HOURS_OF_DAY 13) (MINUTES_OF_HOUR 00))) -"1st oct in the year '89 at 1300 hours" -> (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 89)) (EXPLICIT_TIME (HOURS_OF_DAY 13) (MINUTES_OF_HOUR 00))) -"1st oct in the year '89, 13:00" -> (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 89)) (EXPLICIT_TIME (HOURS_OF_DAY 13) (MINUTES_OF_HOUR 00))) -"1st oct in the year '89,13:00" -> (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 89)) (EXPLICIT_TIME (HOURS_OF_DAY 13) (MINUTES_OF_HOUR 00))) -"1st oct in the year '89, at 13:00" -> (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 89)) (EXPLICIT_TIME (HOURS_OF_DAY 13) (MINUTES_OF_HOUR 00))) -"3am on oct 1st 2010" -> (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 2010)) (EXPLICIT_TIME (HOURS_OF_DAY 3) (MINUTES_OF_HOUR 0) am)) -"3am, october first 2010" -> (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 2010)) (EXPLICIT_TIME (HOURS_OF_DAY 3) (MINUTES_OF_HOUR 0) am)) -"3am,october first 2010" -> (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 2010)) (EXPLICIT_TIME (HOURS_OF_DAY 3) (MINUTES_OF_HOUR 0) am)) -"3am, on october first 2010" -> (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 2010)) (EXPLICIT_TIME (HOURS_OF_DAY 3) (MINUTES_OF_HOUR 0) am)) -"3am october first 2010" -> (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 2010)) (EXPLICIT_TIME (HOURS_OF_DAY 3) (MINUTES_OF_HOUR 0) am)) -"next wed. at 5pm" -> (DATE_TIME (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 4))) (EXPLICIT_TIME (HOURS_OF_DAY 5) (MINUTES_OF_HOUR 0) pm)) -"3 days after next wed" -> (DATE_TIME (RELATIVE_DATE (SEEK > by_day 3 (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 4)))))) -"the sunday after next wed" -> (DATE_TIME (RELATIVE_DATE (SEEK > by_day 1 (DAY_OF_WEEK 1) (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 4)))))) -"two days after today" -> (DATE_TIME (RELATIVE_DATE (SEEK > by_day 2 day))) -"two days from today" -> (DATE_TIME (RELATIVE_DATE (SEEK > by_day 2 day))) -"3 sundays after next wed" -> (DATE_TIME (RELATIVE_DATE (SEEK > by_day 3 (DAY_OF_WEEK 1) (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 4)))))) -"september 11, 2010" OK -"the day after next" -> (DATE_TIME (RELATIVE_DATE (SEEK > by_day 2 day))) -"the week after next" -> (DATE_TIME (RELATIVE_DATE (SEEK > by_day 2 week))) -"the month after next" -> (DATE_TIME (RELATIVE_DATE (SEEK > by_day 2 month))) -"the year after next" -> (DATE_TIME (RELATIVE_DATE (SEEK > by_day 2 year))) -"wed of the week after next" -> (DATE_TIME (RELATIVE_DATE (SEEK > by_day 2 week) (EXPLICIT_SEEK (DAY_OF_WEEK 4)))) -"the 28th of the month after next" -> (DATE_TIME (RELATIVE_DATE (SEEK > by_day 2 month) (EXPLICIT_SEEK (DAY_OF_MONTH 28)))) - -date_time_alternative: -"this wed. or next at 5pm" -> (DATE_TIME_ALTERNATIVE (DATE_TIME (RELATIVE_DATE (SEEK > by_day 0 (DAY_OF_WEEK 4))) (EXPLICIT_TIME (HOURS_OF_DAY 5) (MINUTES_OF_HOUR 0) pm)) (DATE_TIME (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 4))) (EXPLICIT_TIME (HOURS_OF_DAY 5) (MINUTES_OF_HOUR 0) pm))) -"feb 28th or 2 days after" -> (DATE_TIME_ALTERNATIVE (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 2) (DAY_OF_MONTH 28))) (DATE_TIME (RELATIVE_DATE (SEEK > by_day 2 (EXPLICIT_DATE (MONTH_OF_YEAR 2) (DAY_OF_MONTH 28)))))) -"january fourth or the friday after" -> (DATE_TIME_ALTERNATIVE (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 1) (DAY_OF_MONTH 4))) (DATE_TIME (RELATIVE_DATE (SEEK > by_day 1 (DAY_OF_WEEK 6) (EXPLICIT_DATE (MONTH_OF_YEAR 1) (DAY_OF_MONTH 4)))))) -"10/10/2008 or 10/12/2008" -> (DATE_TIME_ALTERNATIVE (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 10) (YEAR_OF 2008))) (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 12) (YEAR_OF 2008)))) -"next wed or thursday" -> (DATE_TIME_ALTERNATIVE (DATE_TIME (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 4)))) (DATE_TIME (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 4) (DAY_OF_WEEK 5))))) -"next wed, thurs, fri" -> (DATE_TIME_ALTERNATIVE (DATE_TIME (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 4)))) (DATE_TIME (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 4) (DAY_OF_WEEK 5)))) (DATE_TIME (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 4) (DAY_OF_WEEK 6))))) -"next wed, thurs, or fri at 6pm" -> (DATE_TIME_ALTERNATIVE (DATE_TIME (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 4))) (EXPLICIT_TIME (HOURS_OF_DAY 6) (MINUTES_OF_HOUR 0) pm)) (DATE_TIME (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 4) (DAY_OF_WEEK 5))) (EXPLICIT_TIME (HOURS_OF_DAY 6) (MINUTES_OF_HOUR 0) pm)) (DATE_TIME (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 4) (DAY_OF_WEEK 6))) (EXPLICIT_TIME (HOURS_OF_DAY 6) (MINUTES_OF_HOUR 0) pm))) -"10/10 or 12/30 or 10/15 at 5pm" -> (DATE_TIME_ALTERNATIVE (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 10)) (EXPLICIT_TIME (HOURS_OF_DAY 5) (MINUTES_OF_HOUR 0) pm)) (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 12) (DAY_OF_MONTH 30)) (EXPLICIT_TIME (HOURS_OF_DAY 5) (MINUTES_OF_HOUR 0) pm)) (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 15)) (EXPLICIT_TIME (HOURS_OF_DAY 5) (MINUTES_OF_HOUR 0) pm))) - -date_time_separator: -"," OK -", " OK -", at " OK -" " OK -" at " OK - -time_date_separator: -"," OK -", " OK -", on " OK -" " OK -" on " OK - -date: -"the day before yesterday" -> (RELATIVE_DATE (SEEK < by_day 1 (RELATIVE_DATE (SEEK < by_day 1 day)))) -"1st oct in the year '89" -> (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 89)) -"2009-10-10" -> (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 10) (YEAR_OF 2009)) -"seven years ago" ->(RELATIVE_DATE (SEEK < by_day 7 year)) -"next monday" -> (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 2))) -//"the day before the day before yesterday" -> - -global_date_prefix: -"the day after" -> "> by_day 1" -"day after" -> "> by_day 1" -"2 days after" -> "> by_day 2" -"three days before" -> "< by_day 3" -"six months after" -> "> by_month 6" -"3 weeks before" -> "< by_week 3" -"10 years after" -> "> by_year 10" -"the day before" -> "< by_day 1" -"day before" -> "< by_day 1" - -// ********** relaxed date tests ********** - -relaxed_date: -"oct 1, 1980" -> (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 1980)) -"oct. 1, 1980" -> (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 1980)) -"oct 1,1980" -> (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 1980)) -"1st oct in the year '89" -> (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 89)) -"thirty first of december 80" -> (EXPLICIT_DATE (MONTH_OF_YEAR 12) (DAY_OF_MONTH 31) (YEAR_OF 80)) -"the first of december in the year 1980" -> (EXPLICIT_DATE (MONTH_OF_YEAR 12) (DAY_OF_MONTH 1) (YEAR_OF 1980)) -"the 2 of february in the year 1980" -> (EXPLICIT_DATE (MONTH_OF_YEAR 2) (DAY_OF_MONTH 2) (YEAR_OF 1980)) -"the 2nd of february in the year 1980" -> (EXPLICIT_DATE (MONTH_OF_YEAR 2) (DAY_OF_MONTH 2) (YEAR_OF 1980)) -"the second of february in the year 1980" -> (EXPLICIT_DATE (MONTH_OF_YEAR 2) (DAY_OF_MONTH 2) (YEAR_OF 1980)) -"jan. 2nd" -> (EXPLICIT_DATE (MONTH_OF_YEAR 1) (DAY_OF_MONTH 2)) -"sun, nov 21 2010" -> (EXPLICIT_DATE (MONTH_OF_YEAR 11) (DAY_OF_MONTH 21) (DAY_OF_WEEK 1) (YEAR_OF 2010)) - -relaxed_month: -"january" -> (MONTH_OF_YEAR 1) -"jan" -> (MONTH_OF_YEAR 1) -"february" -> (MONTH_OF_YEAR 2) -"feb" -> (MONTH_OF_YEAR 2) -"march" -> (MONTH_OF_YEAR 3) -"mar" -> (MONTH_OF_YEAR 3) -"april" -> (MONTH_OF_YEAR 4) -"apr" -> (MONTH_OF_YEAR 4) -"may" -> (MONTH_OF_YEAR 5) -"june" -> (MONTH_OF_YEAR 6) -"jun" -> (MONTH_OF_YEAR 6) -"july" -> (MONTH_OF_YEAR 7) -"jul" -> (MONTH_OF_YEAR 7) -"august" -> (MONTH_OF_YEAR 8) -"aug" -> (MONTH_OF_YEAR 8) -"september" -> (MONTH_OF_YEAR 9) -"sep" -> (MONTH_OF_YEAR 9) -"sept" -> (MONTH_OF_YEAR 9) -"october" -> (MONTH_OF_YEAR 10) -"oct" -> (MONTH_OF_YEAR 10) -"november" -> (MONTH_OF_YEAR 11) -"nov" -> (MONTH_OF_YEAR 11) -"december" -> (MONTH_OF_YEAR 12) -"dec" -> (MONTH_OF_YEAR 12) -"jan." -> (MONTH_OF_YEAR 1) -"feb." -> (MONTH_OF_YEAR 2) -"mar." -> (MONTH_OF_YEAR 3) -"apr." -> (MONTH_OF_YEAR 4) -"jun." -> (MONTH_OF_YEAR 6) -"jul." -> (MONTH_OF_YEAR 7) -"aug." -> (MONTH_OF_YEAR 8) -"sep." -> (MONTH_OF_YEAR 9) -"sept." -> (MONTH_OF_YEAR 9) -"oct." -> (MONTH_OF_YEAR 10) -"nov." -> (MONTH_OF_YEAR 11) -"dec." -> (MONTH_OF_YEAR 12) - -relaxed_day_of_month: -"three" -> (DAY_OF_MONTH 3) -"third" -> (DAY_OF_MONTH 3) -"3rd" -> (DAY_OF_MONTH 3) -"3" -> (DAY_OF_MONTH 3) -"03" -> (DAY_OF_MONTH 03) -"21" -> (DAY_OF_MONTH 21) -"thirty one" -> (DAY_OF_MONTH 31) -"thirty-one" -> (DAY_OF_MONTH 31) -"thirty first" -> (DAY_OF_MONTH 31) -"thirty-first" -> (DAY_OF_MONTH 31) -"31st" -> (DAY_OF_MONTH 31) -"32" FAIL - -relaxed_year: -"'69" -> (YEAR_OF 69) -"79" -> (YEAR_OF 79) -"2079" -> (YEAR_OF 2079) -"999" FAIL -"999" FAIL -"'80" -> (YEAR_OF 80) -"1979" -> (YEAR_OF 1979) -"2004" -> (YEAR_OF 2004) - -relaxed_year_prefix: -", in the year " OK -" in the year " OK -"in the year " FAIL - -// ********** formal date tests ********** - -formal_date: -"2009-10-10" -> (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 10) (YEAR_OF 2009)) -"1980-1-2" -> (EXPLICIT_DATE (MONTH_OF_YEAR 1) (DAY_OF_MONTH 2) (YEAR_OF 1980)) -"12/12/12" -> (EXPLICIT_DATE (MONTH_OF_YEAR 12) (DAY_OF_MONTH 12) (YEAR_OF 12)) -"3/4" -> (EXPLICIT_DATE (MONTH_OF_YEAR 3) (DAY_OF_MONTH 4)) -"sun, 11/21/2010" -> (EXPLICIT_DATE (MONTH_OF_YEAR 11) (DAY_OF_MONTH 21) (DAY_OF_WEEK 1) (YEAR_OF 2010)) - -formal_month_of_year: -"00" FAIL -"0" FAIL -"01" -> (MONTH_OF_YEAR 01) -"1" -> (MONTH_OF_YEAR 1) -"02" -> (MONTH_OF_YEAR 02) -"2" -> (MONTH_OF_YEAR 2) -"03" -> (MONTH_OF_YEAR 03) -"3" -> (MONTH_OF_YEAR 3) -"04" -> (MONTH_OF_YEAR 04) -"4" -> (MONTH_OF_YEAR 4) -"05" -> (MONTH_OF_YEAR 05) -"5" -> (MONTH_OF_YEAR 5) -"06" -> (MONTH_OF_YEAR 06) -"6" -> (MONTH_OF_YEAR 6) -"07" -> (MONTH_OF_YEAR 07) -"7" -> (MONTH_OF_YEAR 7) -"08" -> (MONTH_OF_YEAR 08) -"8" -> (MONTH_OF_YEAR 8) -"09" -> (MONTH_OF_YEAR 09) -"9" -> (MONTH_OF_YEAR 9) -"10" -> (MONTH_OF_YEAR 10) -"11" -> (MONTH_OF_YEAR 11) -"12" -> (MONTH_OF_YEAR 12) -"13" FAIL - -formal_day_of_month: -"00" FAIL -"0" FAIL -"01" -> (DAY_OF_MONTH 01) -"1" -> (DAY_OF_MONTH 1) -"02" -> (DAY_OF_MONTH 02) -"2" -> (DAY_OF_MONTH 2) -"03" -> (DAY_OF_MONTH 03) -"3" -> (DAY_OF_MONTH 3) -"04" -> (DAY_OF_MONTH 04) -"4" -> (DAY_OF_MONTH 4) -"05" -> (DAY_OF_MONTH 05) -"5" -> (DAY_OF_MONTH 5) -"06" -> (DAY_OF_MONTH 06) -"6" -> (DAY_OF_MONTH 6) -"07" -> (DAY_OF_MONTH 07) -"7" -> (DAY_OF_MONTH 7) -"08" -> (DAY_OF_MONTH 08) -"8" -> (DAY_OF_MONTH 8) -"09" -> (DAY_OF_MONTH 09) -"9" -> (DAY_OF_MONTH 9) -"10" -> (DAY_OF_MONTH 10) -"11" -> (DAY_OF_MONTH 11) -"12" -> (DAY_OF_MONTH 12) -"13" -> (DAY_OF_MONTH 13) -"14" -> (DAY_OF_MONTH 14) -"15" -> (DAY_OF_MONTH 15) -"16" -> (DAY_OF_MONTH 16) -"17" -> (DAY_OF_MONTH 17) -"18" -> (DAY_OF_MONTH 18) -"19" -> (DAY_OF_MONTH 19) -"20" -> (DAY_OF_MONTH 20) -"21" -> (DAY_OF_MONTH 21) -"22" -> (DAY_OF_MONTH 22) -"23" -> (DAY_OF_MONTH 23) -"24" -> (DAY_OF_MONTH 24) -"25" -> (DAY_OF_MONTH 25) -"26" -> (DAY_OF_MONTH 26) -"27" -> (DAY_OF_MONTH 27) -"28" -> (DAY_OF_MONTH 28) -"29" -> (DAY_OF_MONTH 29) -"30" -> (DAY_OF_MONTH 30) -"31" -> (DAY_OF_MONTH 31) -"32" FAIL - -formal_year: -"1999" -> (YEAR_OF 1999) -"80" -> (YEAR_OF 80) -"0000" -> (YEAR_OF 0000) -"2010" -> (YEAR_OF 2010) -"03" -> (YEAR_OF 03) -"037" FAIL -"0" FAIL -"03700" FAIL - -formal_year_four_digits: -"1999" -> (YEAR_OF 1999) -"80" FAIL -"0000" -> (YEAR_OF 0000) -"2010" -> (YEAR_OF 2010) -"03" FAIL -"037" FAIL -"0" FAIL -"03700" FAIL - -formal_date_separator: -"-" OK -"/" OK - -// ********** relative date tests ********** - -relative_date: -"yesterday" -> (RELATIVE_DATE (SEEK < by_day 1 day)) -"tomorrow" ->(RELATIVE_DATE (SEEK > by_day 1 day)) -"in 3 days" ->(RELATIVE_DATE (SEEK > by_day 3 day)) -"3 days ago" ->(RELATIVE_DATE (SEEK < by_day 3 day)) -"in 3 weeks" ->(RELATIVE_DATE (SEEK > by_day 3 week)) -"four weeks ago" ->(RELATIVE_DATE (SEEK < by_day 4 week)) -"in 3 months" ->(RELATIVE_DATE (SEEK > by_day 3 month)) -"three months ago" ->(RELATIVE_DATE (SEEK < by_day 3 month)) -"in 3 years" ->(RELATIVE_DATE (SEEK > by_day 3 year)) -"seven years ago" ->(RELATIVE_DATE (SEEK < by_day 7 year)) -"60 years ago" ->(RELATIVE_DATE (SEEK < by_day 60 year)) -"32 days ago" ->(RELATIVE_DATE (SEEK < by_day 32 day)) -"next monday" -> (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 2))) -"next mon" -> (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 2))) -"4 mondays from now" -> (RELATIVE_DATE (SEEK > by_day 4 (DAY_OF_WEEK 2))) -"4 mondays from today" -> (RELATIVE_DATE (SEEK > by_day 4 (DAY_OF_WEEK 2))) -"next weekend" -> (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 7))) -"six mondays ago" -> (RELATIVE_DATE (SEEK < by_day 6 (DAY_OF_WEEK 2))) -"last monday" -> (RELATIVE_DATE (SEEK < by_week 1 (DAY_OF_WEEK 2))) -"last mon" -> (RELATIVE_DATE (SEEK < by_week 1 (DAY_OF_WEEK 2))) -"this past mon" -> (RELATIVE_DATE (SEEK < by_day 1 (DAY_OF_WEEK 2))) -"this coming mon" -> (RELATIVE_DATE (SEEK > by_day 1 (DAY_OF_WEEK 2))) -"this upcoming mon" -> (RELATIVE_DATE (SEEK > by_day 1 (DAY_OF_WEEK 2))) -"next thurs" -> (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 5))) -"next month" -> (RELATIVE_DATE (SEEK > by_week 1 month)) -"last month" -> (RELATIVE_DATE (SEEK < by_week 1 month)) -"next week" -> (RELATIVE_DATE (SEEK > by_week 1 week)) -"last week" -> (RELATIVE_DATE (SEEK < by_week 1 week)) -"next year" -> (RELATIVE_DATE (SEEK > by_week 1 year)) -"last year" -> (RELATIVE_DATE (SEEK < by_week 1 year)) - -relative_occurrence_index: -"1" -> "1" -"2" -> "2" -"3" -> "3" -"4" -> "4" -"5" -> "5" -"6" FAIL -"first" -> "1" -"second" -> "2" -"third" -> "3" -"fourth" -> "4" -"fifth" -> "5" -"sixth" FAIL -"last" -> "5" - -relative_target: -"sunday" -> (DAY_OF_WEEK 1) -"sundays" -> (DAY_OF_WEEK 1) -"sun" -> (DAY_OF_WEEK 1) -"monday" -> (DAY_OF_WEEK 2) -"mondays" -> (DAY_OF_WEEK 2) -"mon" -> (DAY_OF_WEEK 2) -"tuesday" -> (DAY_OF_WEEK 3) -"tuesdays" -> (DAY_OF_WEEK 3) -"tues" -> (DAY_OF_WEEK 3) -"tue" -> (DAY_OF_WEEK 3) -"wednesday" -> (DAY_OF_WEEK 4) -"wednesdays" -> (DAY_OF_WEEK 4) -"wed" -> (DAY_OF_WEEK 4) -"thursday" -> (DAY_OF_WEEK 5) -"thursdays" -> (DAY_OF_WEEK 5) -"thur" -> (DAY_OF_WEEK 5) -"thu" -> (DAY_OF_WEEK 5) -"friday" -> (DAY_OF_WEEK 6) -"fridays" -> (DAY_OF_WEEK 6) -"fri" -> (DAY_OF_WEEK 6) -"saturday" -> (DAY_OF_WEEK 7) -"saturdays" -> (DAY_OF_WEEK 7) -"sat" -> (DAY_OF_WEEK 7) -"day" -> "day" -"days" -> "day" -"week" -> "week" -"weeks" -> "week" -"month" -> "month" -"months" -> "month" -"year" -> "year" -"years" -> "year" - -implicit_prefix: -"this" -> "> by_day 0" - -relative_prefix: -"this last" -> "< by_week 1" -"last" -> "< by_week 1" -"this past" -> "< by_day 1" -"past" -> "< by_day 1" -"this next" -> "> by_week 1" -"next" -> "> by_week 1" -"this coming" -> "> by_day 1" -"coming" -> "> by_day 1" -"this upcoming" -> "> by_day 1" -"upcoming" -> "> by_day 1" -"in 3" -> "> by_day 3" -"in twenty" -> "> by_day 20" -"3" -> "> by_day 3" -"twenty-eight" -> "> by_day 28" - -relative_suffix: -"from now" -> "> by_day" -"ago" -> "< by_day" - -relative_date_span: -"day" -> "day" -"days" -> "day" -"week" -> "week" -"weeks" -> "week" -"month" -> "month" -"months" -> "month" -"year" -> "year" -"years" -> "year" - -// ********** explicit_relative date tests ********** -explicit_relative_date: -"1st of three months ago" -> (RELATIVE_DATE (SEEK < by_day 3 month) (EXPLICIT_SEEK (DAY_OF_MONTH 1))) -"10th of next month" -> (RELATIVE_DATE (SEEK > by_week 1 month) (EXPLICIT_SEEK (DAY_OF_MONTH 10))) -"28th of last month" -> (RELATIVE_DATE (SEEK < by_week 1 month) (EXPLICIT_SEEK (DAY_OF_MONTH 28))) -"10th of next october" -> (RELATIVE_DATE (SEEK > by_week 1 (MONTH_OF_YEAR 10)) (EXPLICIT_SEEK (DAY_OF_MONTH 10))) -"the 30th of this month" -> (RELATIVE_DATE (SEEK > by_day 0 month) (EXPLICIT_SEEK (DAY_OF_MONTH 30))) -"monday of last week" -> (RELATIVE_DATE (SEEK < by_week 1 week) (EXPLICIT_SEEK (DAY_OF_WEEK 2))) -"tuesday of next week" -> (RELATIVE_DATE (SEEK > by_week 1 week) (EXPLICIT_SEEK (DAY_OF_WEEK 3))) -"the monday of 2 weeks ago" -> (RELATIVE_DATE (SEEK < by_day 2 week) (EXPLICIT_SEEK (DAY_OF_WEEK 2))) -"tuesday of 3 weeks from now" -> (RELATIVE_DATE (SEEK > by_day 3 week) (EXPLICIT_SEEK (DAY_OF_WEEK 3))) -"monday of 3 weeks from now" -> (RELATIVE_DATE (SEEK > by_day 3 week) (EXPLICIT_SEEK (DAY_OF_WEEK 2))) -"the last sunday in november" -> (RELATIVE_DATE (SEEK > by_day 0 (MONTH_OF_YEAR 11)) (EXPLICIT_SEEK 5 (DAY_OF_WEEK 1))) -"the last day of february 1999" -> (RELATIVE_DATE (SEEK > by_day 0 (MONTH_OF_YEAR 2)) (EXPLICIT_SEEK (DAY_OF_MONTH 31)) (EXPLICIT_SEEK (YEAR_OF 1999))) -"the first wed. in january" -> (RELATIVE_DATE (SEEK > by_day 0 (MONTH_OF_YEAR 1)) (EXPLICIT_SEEK 1 (DAY_OF_WEEK 4))) -"the first wed. in january in the year 2004" -> (RELATIVE_DATE (SEEK > by_day 0 (MONTH_OF_YEAR 1)) (EXPLICIT_SEEK 1 (DAY_OF_WEEK 4)) (EXPLICIT_SEEK (YEAR_OF 2004))) -"3rd wed in next month" -> (RELATIVE_DATE (SEEK > by_week 1 month) (EXPLICIT_SEEK 3 (DAY_OF_WEEK 4))) -"last monday of last month" -> (RELATIVE_DATE (SEEK < by_week 1 month) (EXPLICIT_SEEK 5 (DAY_OF_WEEK 2))) -"the last sunday of next nov" -> (RELATIVE_DATE (SEEK > by_week 1 (MONTH_OF_YEAR 11)) (EXPLICIT_SEEK 5 (DAY_OF_WEEK 1))) -"the 3rd sunday of 2 novembers from now" -> (RELATIVE_DATE (SEEK > by_day 2 (MONTH_OF_YEAR 11)) (EXPLICIT_SEEK 3 (DAY_OF_WEEK 1))) -"the last monday in 2 novembers ago" -> (RELATIVE_DATE (SEEK < by_day 2 (MONTH_OF_YEAR 11)) (EXPLICIT_SEEK 5 (DAY_OF_WEEK 2))) -"the beginning of next week" -> (RELATIVE_DATE (SEEK > by_week 1 week) (EXPLICIT_SEEK (DAY_OF_WEEK 2))) -"the end of next week" -> (RELATIVE_DATE (SEEK > by_week 1 week) (EXPLICIT_SEEK (DAY_OF_WEEK 6))) -"the end of this week" -> (RELATIVE_DATE (SEEK > by_day 0 week) (EXPLICIT_SEEK (DAY_OF_WEEK 6))) -"the start of this week" -> (RELATIVE_DATE (SEEK > by_day 0 week) (EXPLICIT_SEEK (DAY_OF_WEEK 2))) -"start of 3 weeks from now" -> (RELATIVE_DATE (SEEK > by_day 3 week) (EXPLICIT_SEEK (DAY_OF_WEEK 2))) -"the end of 3 weeks ago" -> (RELATIVE_DATE (SEEK < by_day 3 week) (EXPLICIT_SEEK (DAY_OF_WEEK 6))) -"the first day of this week" -> (RELATIVE_DATE (SEEK > by_day 0 week) (EXPLICIT_SEEK (DAY_OF_WEEK 2))) -"the last day of next week" -> (RELATIVE_DATE (SEEK > by_week 1 week) (EXPLICIT_SEEK (DAY_OF_WEEK 6))) -"first day of next week" -> (RELATIVE_DATE (SEEK > by_week 1 week) (EXPLICIT_SEEK (DAY_OF_WEEK 2))) -"last day of last week" -> (RELATIVE_DATE (SEEK < by_week 1 week) (EXPLICIT_SEEK (DAY_OF_WEEK 6))) -"start of 3 months from now" -> (RELATIVE_DATE (SEEK > by_day 3 month) (EXPLICIT_SEEK (DAY_OF_MONTH 1))) -"beginning of next month" -> (RELATIVE_DATE (SEEK > by_week 1 month) (EXPLICIT_SEEK (DAY_OF_MONTH 1))) -"end of next month" -> (RELATIVE_DATE (SEEK > by_week 1 month) (EXPLICIT_SEEK (DAY_OF_MONTH 31))) -"last day of next month" -> (RELATIVE_DATE (SEEK > by_week 1 month) (EXPLICIT_SEEK (DAY_OF_MONTH 31))) -"first day of 3 months from now" -> (RELATIVE_DATE (SEEK > by_day 3 month) (EXPLICIT_SEEK (DAY_OF_MONTH 1))) -"end of next october" -> (RELATIVE_DATE (SEEK > by_week 1 (MONTH_OF_YEAR 10)) (EXPLICIT_SEEK (DAY_OF_MONTH 31))) -"first day of feb" -> (RELATIVE_DATE (SEEK > by_day 0 (MONTH_OF_YEAR 2)) (EXPLICIT_SEEK (DAY_OF_MONTH 1))) -"last day of three februarys from now" -> (RELATIVE_DATE (SEEK > by_day 3 (MONTH_OF_YEAR 2)) (EXPLICIT_SEEK (DAY_OF_MONTH 31))) -"in the end of next week" -> (RELATIVE_DATE (SEEK > by_week 1 week) (EXPLICIT_SEEK (DAY_OF_WEEK 6))) -"at the end of last week" -> (RELATIVE_DATE (SEEK < by_week 1 week) (EXPLICIT_SEEK (DAY_OF_WEEK 6))) -"at the end of 2 weeks" -> (RELATIVE_DATE (SEEK > by_day 2 week) (EXPLICIT_SEEK (DAY_OF_WEEK 6))) -"in the start of june" -> (RELATIVE_DATE (SEEK > by_day 0 (MONTH_OF_YEAR 6)) (EXPLICIT_SEEK (DAY_OF_MONTH 1))) -"at the end of next week" -> (RELATIVE_DATE (SEEK > by_week 1 week) (EXPLICIT_SEEK (DAY_OF_WEEK 6))) -"at the end of last month" -> (RELATIVE_DATE (SEEK < by_week 1 month) (EXPLICIT_SEEK (DAY_OF_MONTH 31))) -"the second day of april" -> (RELATIVE_DATE (SEEK > by_day 0 (MONTH_OF_YEAR 4)) (EXPLICIT_SEEK (DAY_OF_MONTH 2))) -"the thirtieth day of next april" -> (RELATIVE_DATE (SEEK > by_week 1 (MONTH_OF_YEAR 4)) (EXPLICIT_SEEK (DAY_OF_MONTH 30))) - -day_of_week: -"sunday" -> (DAY_OF_WEEK 1) -"sundays" -> (DAY_OF_WEEK 1) -"sun" -> (DAY_OF_WEEK 1) -"sun." -> (DAY_OF_WEEK 1) -"monday" -> (DAY_OF_WEEK 2) -"mondays" -> (DAY_OF_WEEK 2) -"mon" -> (DAY_OF_WEEK 2) -"mon." -> (DAY_OF_WEEK 2) -"tuesday" -> (DAY_OF_WEEK 3) -"tuesdays" -> (DAY_OF_WEEK 3) -"tues" -> (DAY_OF_WEEK 3) -"tues." -> (DAY_OF_WEEK 3) -"tue" -> (DAY_OF_WEEK 3) -"tue." -> (DAY_OF_WEEK 3) -"wednesday" -> (DAY_OF_WEEK 4) -"wednesdays" -> (DAY_OF_WEEK 4) -"wed" -> (DAY_OF_WEEK 4) -"wed." -> (DAY_OF_WEEK 4) -"thursday" -> (DAY_OF_WEEK 5) -"thursdays" -> (DAY_OF_WEEK 5) -"thur" -> (DAY_OF_WEEK 5) -"thur." -> (DAY_OF_WEEK 5) -"thu" -> (DAY_OF_WEEK 5) -"thu." -> (DAY_OF_WEEK 5) -"friday" -> (DAY_OF_WEEK 6) -"fridays" -> (DAY_OF_WEEK 6) -"fri" -> (DAY_OF_WEEK 6) -"fri." -> (DAY_OF_WEEK 6) -"saturday" -> (DAY_OF_WEEK 7) -"saturdays" -> (DAY_OF_WEEK 7) -"sat" -> (DAY_OF_WEEK 7) -"sat." -> (DAY_OF_WEEK 7) - -named_relative_date: -"today" -> (RELATIVE_DATE (SEEK > by_day 0 day)) -"now" -> (RELATIVE_DATE (SEEK > by_day 0 day)) -"tomorow" -> (RELATIVE_DATE (SEEK > by_day 1 day)) -"tomorrow" -> (RELATIVE_DATE (SEEK > by_day 1 day)) -"tommorow" -> (RELATIVE_DATE (SEEK > by_day 1 day)) -"tommorrow" -> (RELATIVE_DATE (SEEK > by_day 1 day)) -"yesterday" -> (RELATIVE_DATE (SEEK < by_day 1 day)) - -// ********** time tests ********** -time: -"0600h" -> (EXPLICIT_TIME (HOURS_OF_DAY 06) (MINUTES_OF_HOUR 00)) -"06:00h" -> (EXPLICIT_TIME (HOURS_OF_DAY 06) (MINUTES_OF_HOUR 00)) -"06:00 hours" -> (EXPLICIT_TIME (HOURS_OF_DAY 06) (MINUTES_OF_HOUR 00)) -"0000" -> (EXPLICIT_TIME (HOURS_OF_DAY 00) (MINUTES_OF_HOUR 00)) -"0700h" -> (EXPLICIT_TIME (HOURS_OF_DAY 07) (MINUTES_OF_HOUR 00)) -"6pm" -> (EXPLICIT_TIME (HOURS_OF_DAY 6) (MINUTES_OF_HOUR 0) pm) -"5:30 a.m." -> (EXPLICIT_TIME (HOURS_OF_DAY 5) (MINUTES_OF_HOUR 30) am) -"5" -> (EXPLICIT_TIME (HOURS_OF_DAY 5) (MINUTES_OF_HOUR 0)) -"12:59" -> (EXPLICIT_TIME (HOURS_OF_DAY 12) (MINUTES_OF_HOUR 59)) -"23:59" -> (EXPLICIT_TIME (HOURS_OF_DAY 23) (MINUTES_OF_HOUR 59)) -"00:00" -> (EXPLICIT_TIME (HOURS_OF_DAY 00) (MINUTES_OF_HOUR 00)) -"10:00am" -> (EXPLICIT_TIME (HOURS_OF_DAY 10) (MINUTES_OF_HOUR 00) am) -"10a" -> (EXPLICIT_TIME (HOURS_OF_DAY 10) (MINUTES_OF_HOUR 0) am) -"10am" -> (EXPLICIT_TIME (HOURS_OF_DAY 10) (MINUTES_OF_HOUR 0) am) -"10" -> (EXPLICIT_TIME (HOURS_OF_DAY 10) (MINUTES_OF_HOUR 0)) -"8p" -> (EXPLICIT_TIME (HOURS_OF_DAY 8) (MINUTES_OF_HOUR 0) pm) -"8pm" -> (EXPLICIT_TIME (HOURS_OF_DAY 8) (MINUTES_OF_HOUR 0) pm) -"8 pm" -> (EXPLICIT_TIME (HOURS_OF_DAY 8) (MINUTES_OF_HOUR 0) pm) -"noon" -> (EXPLICIT_TIME (HOURS_OF_DAY 12) (MINUTES_OF_HOUR 0) pm) -"afternoon" -> (EXPLICIT_TIME (HOURS_OF_DAY 12) (MINUTES_OF_HOUR 0) pm) -"midnight" -> (EXPLICIT_TIME (HOURS_OF_DAY 12) (MINUTES_OF_HOUR 0) am) -"mid-night" -> (EXPLICIT_TIME (HOURS_OF_DAY 12) (MINUTES_OF_HOUR 0) am) - -hours: -"-1" FAIL -"00" -> (HOURS_OF_DAY 00) -"01" -> (HOURS_OF_DAY 01) -"1" -> (HOURS_OF_DAY 1) -"02" -> (HOURS_OF_DAY 02) -"2" -> (HOURS_OF_DAY 2) -"03" -> (HOURS_OF_DAY 03) -"3" -> (HOURS_OF_DAY 3) -"04" -> (HOURS_OF_DAY 04) -"4" -> (HOURS_OF_DAY 4) -"05" -> (HOURS_OF_DAY 05) -"5" -> (HOURS_OF_DAY 5) -"06" -> (HOURS_OF_DAY 06) -"6" -> (HOURS_OF_DAY 6) -"07" -> (HOURS_OF_DAY 07) -"7" -> (HOURS_OF_DAY 7) -"08" -> (HOURS_OF_DAY 08) -"8" -> (HOURS_OF_DAY 8) -"09" -> (HOURS_OF_DAY 09) -"9" -> (HOURS_OF_DAY 9) -"10" -> (HOURS_OF_DAY 10) -"11" -> (HOURS_OF_DAY 11) -"12" -> (HOURS_OF_DAY 12) -"13" -> (HOURS_OF_DAY 13) -"14" -> (HOURS_OF_DAY 14) -"15" -> (HOURS_OF_DAY 15) -"16" -> (HOURS_OF_DAY 16) -"17" -> (HOURS_OF_DAY 17) -"18" -> (HOURS_OF_DAY 18) -"19" -> (HOURS_OF_DAY 19) -"20" -> (HOURS_OF_DAY 20) -"21" -> (HOURS_OF_DAY 21) -"22" -> (HOURS_OF_DAY 22) -"23" -> (HOURS_OF_DAY 23) -"24" FAIL - -minutes: -"-1" FAIL -"00" -> (MINUTES_OF_HOUR 00) -"0" FAIL -"01" -> (MINUTES_OF_HOUR 01) -"1" FAIL -"02" -> (MINUTES_OF_HOUR 02) -"2" FAIL -"03" -> (MINUTES_OF_HOUR 03) -"3" FAIL -"04" -> (MINUTES_OF_HOUR 04) -"4" FAIL -"05" -> (MINUTES_OF_HOUR 05) -"5" FAIL -"06" -> (MINUTES_OF_HOUR 06) -"6" FAIL -"07" -> (MINUTES_OF_HOUR 07) -"7" FAIL -"08" -> (MINUTES_OF_HOUR 08) -"8" FAIL -"09" -> (MINUTES_OF_HOUR 09) -"9" FAIL -"10" -> (MINUTES_OF_HOUR 10) -"11" -> (MINUTES_OF_HOUR 11) -"12" -> (MINUTES_OF_HOUR 12) -"13" -> (MINUTES_OF_HOUR 13) -"14" -> (MINUTES_OF_HOUR 14) -"15" -> (MINUTES_OF_HOUR 15) -"16" -> (MINUTES_OF_HOUR 16) -"17" -> (MINUTES_OF_HOUR 17) -"18" -> (MINUTES_OF_HOUR 18) -"19" -> (MINUTES_OF_HOUR 19) -"20" -> (MINUTES_OF_HOUR 20) -"21" -> (MINUTES_OF_HOUR 21) -"22" -> (MINUTES_OF_HOUR 22) -"23" -> (MINUTES_OF_HOUR 23) -"24" -> (MINUTES_OF_HOUR 24) -"25" -> (MINUTES_OF_HOUR 25) -"26" -> (MINUTES_OF_HOUR 26) -"27" -> (MINUTES_OF_HOUR 27) -"28" -> (MINUTES_OF_HOUR 28) -"29" -> (MINUTES_OF_HOUR 29) -"30" -> (MINUTES_OF_HOUR 30) -"31" -> (MINUTES_OF_HOUR 31) -"32" -> (MINUTES_OF_HOUR 32) -"33" -> (MINUTES_OF_HOUR 33) -"34" -> (MINUTES_OF_HOUR 34) -"35" -> (MINUTES_OF_HOUR 35) -"36" -> (MINUTES_OF_HOUR 36) -"37" -> (MINUTES_OF_HOUR 37) -"38" -> (MINUTES_OF_HOUR 38) -"39" -> (MINUTES_OF_HOUR 39) -"40" -> (MINUTES_OF_HOUR 40) -"41" -> (MINUTES_OF_HOUR 41) -"42" -> (MINUTES_OF_HOUR 42) -"43" -> (MINUTES_OF_HOUR 43) -"44" -> (MINUTES_OF_HOUR 44) -"45" -> (MINUTES_OF_HOUR 45) -"46" -> (MINUTES_OF_HOUR 46) -"47" -> (MINUTES_OF_HOUR 47) -"48" -> (MINUTES_OF_HOUR 48) -"49" -> (MINUTES_OF_HOUR 49) -"50" -> (MINUTES_OF_HOUR 50) -"51" -> (MINUTES_OF_HOUR 51) -"52" -> (MINUTES_OF_HOUR 52) -"53" -> (MINUTES_OF_HOUR 53) -"54" -> (MINUTES_OF_HOUR 54) -"55" -> (MINUTES_OF_HOUR 55) -"56" -> (MINUTES_OF_HOUR 56) -"57" -> (MINUTES_OF_HOUR 57) -"58" -> (MINUTES_OF_HOUR 58) -"59" -> (MINUTES_OF_HOUR 59) -"60" FAIL - -meridian_indicator: -"am" -> "am" -"a.m." -> "am" -"a" -> "am" -"pm" -> "pm" -"p.m." -> "pm" -"p" -> "pm" - -time_zone_abbreviation: -"est" -> "America/New_York" -"edt" -> "America/New_York" -"et" -> "America/New_York" -"pst" -> "America/Los_Angeles" -"pdt" -> "America/Los_Angeles" -"pt" -> "America/Los_Angeles" -"cst" -> "America/Chicago" -"cdt" -> "America/Chicago" -"ct" -> "America/Chicago" -"mst" -> "America/Denver" -"mdt" -> "America/Denver" -"mt" -> "America/Denver" -"akst" -> "America/Anchorage" -"akdt" -> "America/Anchorage" -"akt" -> "America/Anchorage" -"hast" -> "Pacific/Honolulu" -"hadt" -> "Pacific/Honolulu" -"hat" -> "Pacific/Honolulu" -"hst" -> "Pacific/Honolulu" - -// ********** numeric tests ********** - -int_00_to_23_optional_prefix: -"00" -> "00" -"0" -> "0" -"01" -> "01" -"1" -> "1" -"02" -> "02" -"2" -> "2" -"03" -> "03" -"3" -> "3" -"04" -> "04" -"4" -> "4" -"05" -> "05" -"5" -> "5" -"06" -> "06" -"6" -> "6" -"07" -> "07" -"7" -> "7" -"08" -> "08" -"8" -> "8" -"09" -> "09" -"9" -> "9" -"10" -> "10" -"11" -> "11" -"12" -> "12" -"13" -> "13" -"14" -> "14" -"15" -> "15" -"16" -> "16" -"17" -> "17" -"18" -> "18" -"19" -> "19" -"20" -> "20" -"21" -> "21" -"22" -> "22" -"23" -> "23" -"24" FAIL - -int_00_to_59_mandatory_prefix: -"00" -> "00" -"0" FAIL -"01" -> "01" -"1" FAIL -"02" -> "02" -"2" FAIL -"03" -> "03" -"3" FAIL -"04" -> "04" -"4" FAIL -"05" -> "05" -"5" FAIL -"06" -> "06" -"6" FAIL -"07" -> "07" -"7" FAIL -"08" -> "08" -"8" FAIL -"09" -> "09" -"9" FAIL -"10" -> "10" -"11" -> "11" -"12" -> "12" -"13" -> "13" -"14" -> "14" -"15" -> "15" -"16" -> "16" -"17" -> "17" -"18" -> "18" -"19" -> "19" -"20" -> "20" -"21" -> "21" -"22" -> "22" -"23" -> "23" -"24" -> "24" -"25" -> "25" -"26" -> "26" -"27" -> "27" -"28" -> "28" -"29" -> "29" -"30" -> "30" -"31" -> "31" -"32" -> "32" -"33" -> "33" -"34" -> "34" -"35" -> "35" -"36" -> "36" -"37" -> "37" -"38" -> "38" -"39" -> "39" -"40" -> "40" -"41" -> "41" -"42" -> "42" -"43" -> "43" -"44" -> "44" -"45" -> "45" -"46" -> "46" -"47" -> "47" -"48" -> "48" -"49" -> "49" -"50" -> "50" -"51" -> "51" -"52" -> "52" -"53" -> "53" -"54" -> "54" -"55" -> "55" -"56" -> "56" -"57" -> "57" -"58" -> "58" -"59" -> "59" -"60" FAIL - -int_00_to_99_mandatory_prefix: -"00" -> "00" -"0" FAIL -"01" -> "01" -"1" FAIL -"02" -> "02" -"2" FAIL -"03" -> "03" -"3" FAIL -"04" -> "04" -"4" FAIL -"05" -> "05" -"5" FAIL -"06" -> "06" -"6" FAIL -"07" -> "07" -"7" FAIL -"08" -> "08" -"8" FAIL -"09" -> "09" -"9" FAIL -"10" -> "10" -"11" -> "11" -"12" -> "12" -"13" -> "13" -"14" -> "14" -"15" -> "15" -"16" -> "16" -"17" -> "17" -"18" -> "18" -"19" -> "19" -"20" -> "20" -"21" -> "21" -"22" -> "22" -"23" -> "23" -"24" -> "24" -"25" -> "25" -"26" -> "26" -"27" -> "27" -"28" -> "28" -"29" -> "29" -"30" -> "30" -"31" -> "31" -"32" -> "32" -"33" -> "33" -"34" -> "34" -"35" -> "35" -"36" -> "36" -"37" -> "37" -"38" -> "38" -"39" -> "39" -"40" -> "40" -"41" -> "41" -"42" -> "42" -"43" -> "43" -"44" -> "44" -"45" -> "45" -"46" -> "46" -"47" -> "47" -"48" -> "48" -"49" -> "49" -"50" -> "50" -"51" -> "51" -"52" -> "52" -"53" -> "53" -"54" -> "54" -"55" -> "55" -"56" -> "56" -"57" -> "57" -"58" -> "58" -"59" -> "59" -"60" -> "60" -"61" -> "61" -"62" -> "62" -"63" -> "63" -"64" -> "64" -"65" -> "65" -"66" -> "66" -"67" -> "67" -"68" -> "68" -"69" -> "69" -"70" -> "70" -"71" -> "71" -"72" -> "72" -"73" -> "73" -"74" -> "74" -"75" -> "75" -"76" -> "76" -"77" -> "77" -"78" -> "78" -"79" -> "79" -"80" -> "80" -"81" -> "81" -"82" -> "82" -"83" -> "83" -"84" -> "84" -"85" -> "85" -"86" -> "86" -"87" -> "87" -"88" -> "88" -"89" -> "89" -"90" -> "90" -"91" -> "91" -"92" -> "92" -"93" -> "93" -"94" -> "94" -"95" -> "95" -"96" -> "96" -"97" -> "97" -"98" -> "98" -"99" -> "99" -"100" FAIL - -int_01_to_12_optional_prefix: -"00" FAIL -"0" FAIL -"01" -> "01" -"1" -> "1" -"02" -> "02" -"2" -> "2" -"03" -> "03" -"3" -> "3" -"04" -> "04" -"4" -> "4" -"05" -> "05" -"5" -> "5" -"06" -> "06" -"6" -> "6" -"07" -> "07" -"7" -> "7" -"08" -> "08" -"8" -> "8" -"09" -> "09" -"9" -> "9" -"10" -> "10" -"11" -> "11" -"12" -> "12" -"13" FAIL - -int_01_to_31_optional_prefix: -"00" FAIL -"0" FAIL -"01" -> "01" -"1" -> "1" -"02" -> "02" -"2" -> "2" -"03" -> "03" -"3" -> "3" -"04" -> "04" -"4" -> "4" -"05" -> "05" -"5" -> "5" -"06" -> "06" -"6" -> "6" -"07" -> "07" -"7" -> "7" -"08" -> "08" -"8" -> "8" -"09" -> "09" -"9" -> "9" -"10" -> "10" -"11" -> "11" -"12" -> "12" -"13" -> "13" -"14" -> "14" -"15" -> "15" -"16" -> "16" -"17" -> "17" -"18" -> "18" -"19" -> "19" -"20" -> "20" -"21" -> "21" -"22" -> "22" -"23" -> "23" -"24" -> "24" -"25" -> "25" -"26" -> "26" -"27" -> "27" -"28" -> "28" -"29" -> "29" -"30" -> "30" -"31" -> "31" -"32" FAIL - -int_four_digits: -"000" FAIL -"33" FAIL -"2" FAIL -"0000" -> "0000" -"0100" -> "0100" -"0020" -> "0020" -"0003" -> "0003" -"9999" -> "9999" -"5050" -> "5050" - -spelled_or_int_01_to_31_optional_prefix: -"zero" FAIL -"one" -> "1" -"two" -> "2" -"three" -> "3" -"four" -> "4" -"five" -> "5" -"six" -> "6" -"seven" -> "7" -"eight" -> "8" -"nine" -> "9" -"ten" -> "10" -"eleven" -> "11" -"twelve" -> "12" -"thirteen" -> "13" -"fourteen" -> "14" -"fifteen" -> "15" -"sixteen" -> "16" -"seventeen" -> "17" -"eighteen" -> "18" -"nineteen" -> "19" -"twenty" -> "20" -"twenty one" -> "21" -"twenty-one" -> "21" -"twenty two" -> "22" -"twenty-two" -> "22" -"twenty three" -> "23" -"twenty-three" -> "23" -"twenty four" -> "24" -"twenty-four" -> "24" -"twenty five" -> "25" -"twenty-five" -> "25" -"twenty six" -> "26" -"twenty-six" -> "26" -"twenty seven" -> "27" -"twenty-seven" -> "27" -"twenty-eight" -> "28" -"twenty nine" -> "29" -"twenty-nine" -> "29" -"thirty" -> "30" -"thirty one" -> "31" -"thirty-one" -> "31" -"00" FAIL -"0" FAIL -"01" -> "01" -"1" -> "1" -"02" -> "02" -"2" -> "2" -"03" -> "03" -"3" -> "3" -"04" -> "04" -"4" -> "4" -"05" -> "05" -"5" -> "5" -"06" -> "06" -"6" -> "6" -"07" -> "07" -"7" -> "7" -"08" -> "08" -"8" -> "8" -"09" -> "09" -"9" -> "9" -"10" -> "10" -"11" -> "11" -"12" -> "12" -"13" -> "13" -"14" -> "14" -"15" -> "15" -"16" -> "16" -"17" -> "17" -"18" -> "18" -"19" -> "19" -"20" -> "20" -"21" -> "21" -"22" -> "22" -"23" -> "23" -"24" -> "24" -"25" -> "25" -"26" -> "26" -"27" -> "27" -"28" -> "28" -"29" -> "29" -"30" -> "30" -"31" -> "31" - -spelled_or_int_optional_prefix: -"00" FAIL -"0" FAIL -"01" -> "01" -"60" -> "60" -"99" -> "99" -"1" -> "1" -"zero" FAIL -"one" -> "1" -"two" -> "2" - -spelled_one_to_thirty_one: -"zero" FAIL -"one" -> "1" -"two" -> "2" -"three" -> "3" -"four" -> "4" -"five" -> "5" -"six" -> "6" -"seven" -> "7" -"eight" -> "8" -"nine" -> "9" -"ten" -> "10" -"eleven" -> "11" -"twelve" -> "12" -"thirteen" -> "13" -"fourteen" -> "14" -"fifteen" -> "15" -"sixteen" -> "16" -"seventeen" -> "17" -"eighteen" -> "18" -"nineteen" -> "19" -"twenty" -> "20" -"twenty one" -> "21" -"twenty-one" -> "21" -"twenty two" -> "22" -"twenty-two" -> "22" -"twenty three" -> "23" -"twenty-three" -> "23" -"twenty four" -> "24" -"twenty-four" -> "24" -"twenty five" -> "25" -"twenty-five" -> "25" -"twenty six" -> "26" -"twenty-six" -> "26" -"twenty seven" -> "27" -"twenty-seven" -> "27" -"twenty-eight" -> "28" -"twenty nine" -> "29" -"twenty-nine" -> "29" -"thirty" -> "30" -"thirty one" -> "31" -"thirty-one" -> "31" -"thirty two" FAIL -"thirty-two" FAIL - -spelled_first_to_thirty_first: -"first" -> "1" -"1st" -> "1" -"second" -> "2" -"2nd" -> "2" -"third" -> "3" -"3rd" -> "3" -"fourth" -> "4" -"4th" -> "4" -"fifth" -> "5" -"5th" -> "5" -"sixth" -> "6" -"6th" -> "6" -"seventh" -> "7" -"7th" -> "7" -"eighth" -> "8" -"8th" -> "8" -"ninth" -> "9" -"9th" -> "9" -"tenth" -> "10" -"10th" -> "10" -"eleventh" -> "11" -"11th" -> "11" -"twelfth" -> "12" -"12th" -> "12" -"thirteenth" -> "13" -"13th" -> "13" -"fourteenth" -> "14" -"14th" -> "14" -"fifteenth" -> "15" -"15th" -> "15" -"sixteenth" -> "16" -"16th" -> "16" -"seventeenth" -> "17" -"17th" -> "17" -"eighteenth" -> "18" -"18th" -> "18" -"nineteenth" -> "19" -"19th" -> "19" -"twentieth" -> "20" -"20th" -> "20" -"twenty-first" -> "21" -"twenty first" -> "21" -"21st" -> "21" -"twenty-second" -> "22" -"twenty second" -> "22" -"22nd" -> "22" -"twenty-third" -> "23" -"twenty third" -> "23" -"23rd" -> "23" -"twenty-fourth" -> "24" -"twenty fourth" -> "24" -"24th" -> "24" -"twenty-fifth" -> "25" -"twenty fifth" -> "25" -"25th" -> "25" -"twenty-sixth" -> "26" -"twenty sixth" -> "26" -"26th" -> "26" -"twenty-seventh" -> "27" -"twenty seventh" -> "27" -"27th" -> "27" -"twenty-eighth" -> "28" -"twenty eighth" -> "28" -"28th" -> "28" -"twenty-ninth" -> "29" -"twenty ninth" -> "29" -"29th" -> "29" -"thirtieth" -> "30" -"30th" -> "30" -"thirty-first" -> "31" -"thirty first" -> "31" -"31st" -> "31" - -int_60_to_99: -"59" FAIL -"60" OK -"61" OK -"62" OK -"63" OK -"64" OK -"65" OK -"66" OK -"67" OK -"68" OK -"69" OK -"70" OK -"71" OK -"72" OK -"73" OK -"74" OK -"75" OK -"76" OK -"77" OK -"78" OK -"79" OK -"80" OK -"81" OK -"82" OK -"83" OK -"84" OK -"85" OK -"86" OK -"87" OK -"88" OK -"89" OK -"90" OK -"91" OK -"92" OK -"93" OK -"94" OK -"95" OK -"96" OK -"97" OK -"98" OK -"99" OK -"100" FAIL - - -int_32_to_59: -"31" FAIL -"32" OK -"33" OK -"34" OK -"35" OK -"36" OK -"37" OK -"38" OK -"39" OK -"40" OK -"41" OK -"42" OK -"43" OK -"44" OK -"45" OK -"46" OK -"47" OK -"48" OK -"49" OK -"50" OK -"51" OK -"52" OK -"53" OK -"54" OK -"55" OK -"56" OK -"57" OK -"58" OK -"59" OK -"60" FAIL - -int_24_to_31: -"23" FAIL -"24" OK -"25" OK -"26" OK -"27" OK -"28" OK -"29" OK -"30" OK -"31" OK -"32" FAIL - -int_13_to_23: -"12" FAIL -"13" OK -"14" OK -"15" OK -"16" OK -"17" OK -"18" OK -"19" OK -"20" OK -"21" OK -"22" OK -"23" OK -"24" FAIL - -int_01_to_12: -"0" FAIL -"00" FAIL -"1" FAIL -"01" OK -"2" FAIL -"02" OK -"3" FAIL -"03" OK -"4" FAIL -"04" OK -"5" FAIL -"05" OK -"6" FAIL -"06" OK -"7" FAIL -"07" OK -"8" FAIL -"08" OK -"9" FAIL -"09" OK -"10" OK -"11" OK -"12" OK -"13" FAIL - -int_1_to_9: -"0" FAIL -"00" FAIL -"1" OK -"01" FAIL -"2" OK -"02" FAIL -"3" OK -"03" FAIL -"4" OK -"04" FAIL -"5" OK -"05" FAIL -"6" OK -"06" FAIL -"7" OK -"07" FAIL -"8" OK -"08" FAIL -"9" OK -"09" FAIL -"10" FAIL - -int_1_to_5: -"0" FAIL -"00" FAIL -"1" OK -"01" FAIL -"2" OK -"02" FAIL -"3" OK -"03" FAIL -"4" OK -"04" FAIL -"5" OK -"05" FAIL -"6" FAIL -"06" FAIL - -// ********** date lexer rules ********** - -JANUARY: -"january" OK -"januarys" OK -"jan" OK -"jan." OK - -FEBRUARY: -"february" OK -"februarys" OK -"feb" OK -"feb." OK - -MARCH: -"march" OK -"marches" OK -"mar" OK -"mar." OK - -APRIL: -"april" OK -"aprils" OK -"apr" OK -"apr." OK - -MAY: -"may" OK -"mays" OK - -JUNE: -"june" OK -"junes" OK -"jun" OK -"jun." OK - -JULY: -"july" OK -"julys" OK -"jul" OK -"jul." OK - -AUGUST: -"august" OK -"augusts" OK -"aug" OK -"aug." OK - -SEPTEMBER: -"september" OK -"septembers" OK -"sep" OK -"sep." OK -"sept" OK -"sept." OK - -OCTOBER: -"october" OK -"octobers" OK -"oct" OK -"oct." OK - -NOVEMBER: -"november" OK -"novembers" OK -"nov" OK -"nov." OK - -DECEMBER: -"december" OK -"decembers" OK -"dec" OK -"dec." OK - -SUNDAY: -"sunday" OK -"sundays" OK -"sun" OK -"sun." OK -"suns" OK -"suns." OK - -MONDAY: -"monday" OK -"mondays" OK -"mon" OK -"mon." OK -"mons" OK -"mons." OK - -TUESDAY: -"tuesday" OK -"tuesdays" OK -"tues" OK -"tues." OK -"tue" OK -"tue." OK - -WEDNESDAY: -"wednesday" OK -"wednesdays" OK -"wed" OK -"wed." OK -"weds" OK -"weds." OK - -THURSDAY: -"thursday" OK -"thursdays" OK -"thur" OK -"thur." OK -"thu" OK -"thu." OK -"thus" OK -"thus." OK -"thurs" OK -"thurs." OK - -FRIDAY: -"friday" OK -"fridays" OK -"fri" OK -"fri." OK -"fris" OK -"fris." OK - -SATURDAY: -"saturday" OK -"saturdays" OK -"sat" OK -"sat." OK -"sats" OK -"sats." OK -"weekend" OK - -HOUR: -"hour" OK -"hours" OK - -DAY: -"day" OK -"days" OK - -WEEK: -"week" OK -"weeks" OK - -MONTH: -"month" OK -"months" OK - -YEAR: -"year" OK -"years" OK - -TODAY:"today" OK - -NOW:"now" OK - -TOMORROW: -"tomorow" OK -"tomorrow" OK -"tommorow" OK -"tommorrow" OK - -YESTERDAY : "yesterday" OK - -// ********** time lexer rules ********** - -AM: -"am" OK -"a.m." OK -"a" OK - -PM: -"pm" OK -"p.m." OK -"p" OK - -T:"t" OK - -MILITARY_HOUR_SUFFIX:"h" OK - -MIDNIGHT: -"midnight" OK -"mid-night" OK - -NOON: -"noon" OK -"afternoon" OK -"after-noon" OK - -UTC: -"utc" OK -"gmt" OK -"z" OK - -EST: -"est" OK -"edt" OK -"et" OK - -PST: -"pst" OK -"pdt" OK -"pt" OK - -CST: -"cst" OK -"cdt" OK -"ct" OK - -MST: -"mst" OK -"mdt" OK -"mt" OK - -AKST: -"akst" OK -"akdt" OK -"akt" OK - -HAST: -"hast" OK -"hadt" OK -"hat" OK -"hst" OK - -// ********* numeric lexer rules ********** - -INT_00: "00" OK -INT_00: "0" FAIL -INT_01: "01" OK -INT_01: "1" FAIL -INT_02: "02" OK -INT_02: "2" FAIL -INT_03: "03" OK -INT_03: "3" FAIL -INT_04: "04" OK -INT_04: "4" FAIL -INT_05: "05" OK -INT_05: "6" FAIL -INT_06: "06" OK -INT_06: "6" FAIL -INT_07: "07" OK -INT_07: "7" FAIL -INT_08: "08" OK -INT_08: "8" FAIL -INT_09: "09" OK -INT_09: "9" FAIL -INT_0: "0" OK -INT_1: "1" OK -INT_2: "2" OK -INT_3: "3" OK -INT_4: "4" OK -INT_5: "5" OK -INT_6: "6" OK -INT_7: "7" OK -INT_8: "8" OK -INT_9: "9" OK -INT_10: "10" OK -INT_11: "11" OK -INT_12: "12" OK -INT_13: "13" OK -INT_14: "14" OK -INT_15: "15" OK -INT_16: "16" OK -INT_17: "17" OK -INT_18: "18" OK -INT_19: "19" OK -INT_20: "20" OK -INT_21: "21" OK -INT_22: "22" OK -INT_23: "23" OK -INT_24: "24" OK -INT_25: "25" OK -INT_26: "26" OK -INT_27: "27" OK -INT_28: "28" OK -INT_29: "29" OK -INT_30: "30" OK -INT_31: "31" OK -INT_32: "32" OK -INT_33: "33" OK -INT_34: "34" OK -INT_35: "35" OK -INT_36: "36" OK -INT_37: "37" OK -INT_38: "38" OK -INT_39: "39" OK -INT_40: "40" OK -INT_41: "41" OK -INT_42: "42" OK -INT_43: "43" OK -INT_44: "44" OK -INT_45: "45" OK -INT_46: "46" OK -INT_47: "47" OK -INT_48: "48" OK -INT_49: "49" OK -INT_50: "50" OK -INT_51: "51" OK -INT_52: "52" OK -INT_53: "53" OK -INT_54: "54" OK -INT_55: "55" OK -INT_56: "56" OK -INT_57: "57" OK -INT_58: "58" OK -INT_59: "59" OK -INT_60: "60" OK -INT_61: "61" OK -INT_62: "62" OK -INT_63: "63" OK -INT_64: "64" OK -INT_65: "65" OK -INT_66: "66" OK -INT_67: "67" OK -INT_68: "68" OK -INT_69: "69" OK -INT_70: "70" OK -INT_71: "71" OK -INT_72: "72" OK -INT_73: "73" OK -INT_74: "74" OK -INT_75: "75" OK -INT_76: "76" OK -INT_77: "77" OK -INT_78: "78" OK -INT_79: "79" OK -INT_80: "80" OK -INT_81: "81" OK -INT_82: "82" OK -INT_83: "83" OK -INT_84: "84" OK -INT_85: "85" OK -INT_86: "86" OK -INT_87: "87" OK -INT_88: "88" OK -INT_89: "89" OK -INT_90: "90" OK -INT_91: "91" OK -INT_92: "92" OK -INT_93: "93" OK -INT_94: "94" OK -INT_95: "95" OK -INT_96: "96" OK -INT_97: "97" OK -INT_98: "98" OK -INT_99: "99" OK - -ST: "st" OK -ND: "nd" OK -RD: "rd" OK -TH: "th" OK - -ONE: "one" OK -TWO: "two" OK -THREE: "three" OK -FOUR: "four" OK -FIVE: "five" OK -SIX: "six" OK -SEVEN: "seven" OK -EIGHT: "eight" OK -NINE: "nine" OK -TEN: "ten" OK -ELEVEN: "eleven" OK -TWELVE: "twelve" OK -THIRTEEN: "thirteen" OK -FOURTEEN: "fourteen" OK -FIFTEEN: "fifteen" OK -SIXTEEN: "sixteen" OK -SEVENTEEN: "seventeen" OK -EIGHTEEN: - "eighteen" OK - "eightteen" OK -NINETEEN: "nineteen" OK -TWENTY: "twenty" OK -THIRTY: "thirty" OK - -FIRST: "first" OK -SECOND: "second" OK -THIRD: "third" OK -FOURTH: "fourth" OK -FIFTH: "fifth" OK -SIXTH: "sixth" OK -SEVENTH: "seventh" OK -EIGHTH: "eighth" OK -NINTH: "ninth" OK -TENTH: "tenth" OK -ELEVENTH: "eleventh" OK -TWELFTH: "twelfth" OK -THIRTEENTH: "thirteenth" OK -FOURTEENTH: "fourteenth" OK -FIFTEENTH: "fifteenth" OK -SIXTEENTH: "sixteenth" OK -SEVENTEENTH: "seventeenth" OK -EIGHTEENTH: "eighteenth" OK -NINETEENTH: "nineteenth" OK -TWENTIETH: "twentieth" OK -THIRTIETH: "thirtieth" OK - -COLON: ":" OK -COMMA: "," OK -DASH: "-" OK -SLASH: "/" OK -DOT: "." OK -PLUS: "+" OK -SINGLE_QUOTE: "'" OK -IN: "in" OK -THE: "the" OK -AT: "at" OK -ON: "on" OK -OF: "of" OK -THIS: "this" OK -LAST: "last" OK -NEXT: "next" OK -PAST: "past" OK -COMING: "coming" OK -UPCOMING: "upcoming" OK -FROM: "from" OK -NOW: "now" OK -AGO: "ago" OK -BEFORE: "before" OK -AFTER: "after" OK - -WHITE_SPACE: -" " OK -" " OK -"\t" OK -"\t\t" OK -"\n" OK -"\n" OK -"\n\n" OK -"\r" OK -"\r\r" OK \ No newline at end of file diff --git a/src/test/java/com/joestelmach/natty/AbstractTest.java b/src/test/java/com/joestelmach/natty/AbstractTest.java new file mode 100644 index 00000000..b5662939 --- /dev/null +++ b/src/test/java/com/joestelmach/natty/AbstractTest.java @@ -0,0 +1,156 @@ +package com.joestelmach.natty; + +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +import org.junit.Assert; +import org.junit.Before; + +/** + * + * @author Joe Stelmach + */ +public abstract class AbstractTest { + private static Calendar _calendar; + protected static Parser _parser; + + + public static void initCalendarAndParser() { + _calendar = Calendar.getInstance(); + _parser = new Parser(); + } + + /** + * Resets the calendar source time before each test + */ + @Before + public void before() { + CalendarSource.setBaseDate(null); + } + + /** + * Parses the given value into a collection of dates + * + * @param value + * @return + */ + protected List parseCollection(String value) { + return _parser.parse(value).get(0).getDates(); + } + + /** + * Parses the given value, asserting that one and only one date is produced. + * + * @param value + * @return + */ + protected Date parseSingleDate(String value) { + List dates = parseCollection(value); + Assert.assertEquals(1, dates.size()); + return dates.get(0); + } + + /** + * Asserts that the given string value parses down to the given + * month, day, and year values. + * + * @param value + * @param month + * @param day + * @param year + */ + protected void validateDate(String value, int month, int day, int year) { + Date date = parseSingleDate(value); + validateDate(date, month, day, year); + } + + /** + * Asserts that the given date contains the given attributes + * + * @param date + * @param month + * @param day + * @param year + */ + protected void validateDate(Date date, int month, int day, int year) { + _calendar.setTime(date); + Assert.assertEquals(month -1, _calendar.get(Calendar.MONTH)); + Assert.assertEquals(day, _calendar.get(Calendar.DAY_OF_MONTH)); + Assert.assertEquals(year, _calendar.get(Calendar.YEAR)); + } + + /** + * + * Asserts that the given string value parses down to the given + * hours, minutes, and seconds + * + * @param value + * @param hours + * @param minutes + * @param seconds + */ + protected void validateTime(String value, int hours, int minutes, int seconds) { + Date date = parseSingleDate(value); + validateTime(date, hours, minutes, seconds); + } + + /** + * + * Asserts that the given date contains the given time attributes + * + * @param date + * @param hours + * @param minutes + * @param seconds + */ + protected void validateTime(Date date, int hours, int minutes, int seconds) { + _calendar.setTime(date); + Assert.assertEquals(hours, _calendar.get(Calendar.HOUR_OF_DAY)); + Assert.assertEquals(minutes, _calendar.get(Calendar.MINUTE)); + Assert.assertEquals(seconds, _calendar.get(Calendar.SECOND)); + } + + /** + * + * Asserts that the given string value parses down to the given + * month, day, year, hours, minutes, and seconds + * + * @param value + * @param month + * @param day + * @param year + * @param hours + * @param minutes + * @param seconds + */ + protected void validateDateTime(String value, int month, int day, int year, + int hours, int minutes, int seconds) { + + Date date = parseSingleDate(value); + validateDateTime(date, month, day, year, hours, minutes, seconds); + } + + /** + * Asserts that the given date contains the given attributes + * + * @param date + * @param month + * @param day + * @param year + * @param hours + * @param minutes + * @param seconds + */ + protected void validateDateTime(Date date, int month, int day, int year, + int hours, int minutes, int seconds) { + + _calendar.setTime(date); + Assert.assertEquals("month", month -1, _calendar.get(Calendar.MONTH)); + Assert.assertEquals("day of the month", day, _calendar.get(Calendar.DAY_OF_MONTH)); + Assert.assertEquals("year", year, _calendar.get(Calendar.YEAR)); + Assert.assertEquals("hour", hours, _calendar.get(Calendar.HOUR_OF_DAY)); + Assert.assertEquals("minutes", minutes, _calendar.get(Calendar.MINUTE)); + Assert.assertEquals("seconds", seconds, _calendar.get(Calendar.SECOND)); + } +} diff --git a/src/test/java/com/joestelmach/natty/CPANTest.java b/src/test/java/com/joestelmach/natty/CPANTest.java new file mode 100644 index 00000000..490f3b60 --- /dev/null +++ b/src/test/java/com/joestelmach/natty/CPANTest.java @@ -0,0 +1,33 @@ +package com.joestelmach.natty; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.util.List; + +import junit.framework.Assert; + +import org.junit.Test; + +/** + * + * @author Joe Stelmach + */ +public class CPANTest { + + @Test + public void sanityCheck() throws Exception { + BufferedReader reader = new BufferedReader(new InputStreamReader( + CPANTest.class.getResourceAsStream("/cpan.txt"))); + String value = null; + while((value = reader.readLine()) != null) { + if(!value.trim().startsWith("#") && value.trim().length() > 0) { + Parser parser = new Parser(); + List groups = parser.parse(value); + Assert.assertEquals(1, groups.size()); + Assert.assertTrue(groups.get(0).getDates().size() > 0); + } + } + + reader.close(); + } +} diff --git a/src/test/java/com/joestelmach/natty/DateTest.java b/src/test/java/com/joestelmach/natty/DateTest.java new file mode 100644 index 00000000..5c2c91c6 --- /dev/null +++ b/src/test/java/com/joestelmach/natty/DateTest.java @@ -0,0 +1,322 @@ +package com.joestelmach.natty; + +import junit.framework.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.text.DateFormat; +import java.util.*; +import java.util.Map.Entry; +import java.util.logging.ConsoleHandler; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Runs the parser through the various date formats + * + * @author Joe Stelmach + */ +public class DateTest extends AbstractTest { + @BeforeClass + public static void oneTime() { + Locale.setDefault(Locale.US); + TimeZone.setDefault(TimeZone.getTimeZone("US/Eastern")); + initCalendarAndParser(); + } + + @Test + public void testFormal() { + validateDate("1978-01-28", 1, 28, 1978); + validateDate("2009-10-10", 10, 10, 2009); + validateDate("1980-1-2", 1, 2, 1980); + validateDate("12/12/12", 12, 12, 2012); + validateDate("3/4", 3, 4, Calendar.getInstance().get(Calendar.YEAR)); + validateDate("sun, 11/21/2010", 11, 21, 2010); + validateDate("in october 2006", 10, 1, 2006); + validateDate("feb 1979", 2, 1, 1979); + validateDate("jan '80", 1, 1, 1980); + validateDate("2006-Jun-16", 6, 16, 2006); + validateDate("28-Feb-2010", 2, 28, 2010); + validateDate("9-Apr", 4, 9, Calendar.getInstance().get(Calendar.YEAR)); + validateDate("jan 10, '00", 1, 10, 2000); + } + + @Test + public void testRelaxed() { + validateDate("oct 1, 1980", 10, 1, 1980); + validateDate("oct. 1, 1980", 10, 1, 1980); + validateDate("oct 1,1980", 10, 1, 1980); + validateDate("1st oct in the year '89", 10, 1, 1989); + validateDate("thirty first of december '80", 12, 31, 1980); + validateDate("the first of december in the year 1980", 12, 1, 1980); + validateDate("the 2 of february in the year 1980", 2, 2, 1980); + validateDate("the 2nd of february in the year 1980", 2, 2, 1980); + validateDate("the second of february in the year 1980", 2, 2, 1980); + validateDate("jan. 2nd", 1, 2, Calendar.getInstance().get(Calendar.YEAR)); + validateDate("sun, nov 21 2010", 11, 21, 2010); + validateDate("Second Monday in October 2017", 10, 9, 2017); + validateDate("2nd thursday in sept. '02", 9, 12, 2002); + } + + @Test + public void testExplicitRelative() throws Exception { + Date reference = DateFormat.getDateInstance(DateFormat.SHORT).parse("2/28/2011"); + CalendarSource.setBaseDate(reference); + + validateDate("final thursday in april", 4, 28, 2011); + validateDate("final thurs in sep", 9, 29, 2011); + validateDate("4th february ", 2, 4, 2011); + } + + @Test + public void testRelative() throws Exception { + Date reference = DateFormat.getDateInstance(DateFormat.SHORT).parse("2/28/2011"); + CalendarSource.setBaseDate(reference); + + validateDate("yesterday", 2, 27, 2011); + validateDate("tomorrow", 3, 1, 2011); + validateDate("in 3 days", 3, 3, 2011); + validateDate("3 days ago", 2, 25, 2011); + validateDate("in 3 weeks", 3, 21, 2011); + validateDate("four weeks ago", 1, 31, 2011); + validateDate("in 3 months", 5, 28, 2011); + validateDate("three months ago", 11, 28, 2010); + validateDate("in 3 years", 2, 28, 2014); + validateDate("seven years ago", 2, 28, 2004); + validateDate("60 years ago", 2, 28, 1951); + validateDate("32 days ago", 1, 27, 2011); + validateDate("next monday", 3, 7, 2011); + validateDate("next mon", 3, 7, 2011); + validateDate("4 mondays from now", 3, 28, 2011); + validateDate("4 mondays from today", 3, 28, 2011); + validateDate("next weekend", 3, 12, 2011); + validateDate("six mondays ago", 1, 17, 2011); + validateDate("last monday", 2, 21, 2011); + validateDate("last mon", 2, 21, 2011); + validateDate("this past mon", 2, 21, 2011); + validateDate("this coming mon", 3, 7, 2011); + validateDate("this upcoming mon", 3, 7, 2011); + validateDate("next thurs", 3, 10, 2011); + validateDate("next month", 3, 28, 2011); + validateDate("last month", 1, 28, 2011); + validateDate("next week", 3, 7, 2011); + validateDate("last week", 2, 21, 2011); + validateDate("next year", 2, 28, 2012); + validateDate("last year", 2, 28, 2010); + validateDate("tues this week", 3, 1, 2011); + validateDate("tuesday this week", 3, 1, 2011); + validateDate("tuesday next week", 3, 8, 2011); + validateDate("this september", 9, 1, 2011); + validateDate("in a september", 9, 1, 2011); + validateDate("in an october", 10, 1, 2011); + validateDate("september", 9, 1, 2011); + validateDate("last september", 9, 1, 2010); + validateDate("next september", 9, 1, 2011); + validateDate("in a year", 2, 28, 2012); + validateDate("in a week", 3, 7, 2011); + validateDate("the saturday after next", 3, 19, 2011); + validateDate("the monday after next", 3, 14, 2011); + validateDate("the monday after next monday", 3, 14, 2011); + validateDate("tuesday before last", 2, 15, 2011); + validateDate("a week from now", 3, 7, 2011); + validateDate("a month from today", 3, 28, 2011); + validateDate("a week after this friday", 3, 11, 2011); + validateDate("a week from this friday", 3, 11, 2011); + validateDate("two weeks from this friday", 3, 18, 2011); + validateDate("It's gonna snow! How about skiing tomorrow", 3, 1, 2011); + validateDate("A week on tuesday", 3, 8, 2011); + validateDate("A month ago", 1, 28, 2011); + validateDate("A week ago", 2, 21, 2011); + validateDate("A year ago", 2, 28, 2010); + } + + @Test + public void testRange() throws Exception { + Date reference = DateFormat.getDateInstance(DateFormat.SHORT).parse("1/02/2011"); + CalendarSource.setBaseDate(reference); + + List dates = parseCollection("monday to friday"); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 1, 3, 2011); + validateDate(dates.get(1), 1, 7, 2011); + + dates = parseCollection("1999-12-31 to tomorrow"); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 12, 31, 1999); + validateDate(dates.get(1), 1, 3, 2011); + + dates = parseCollection("now to 2010-01-01"); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 1, 2, 2011); + validateDate(dates.get(1), 1, 1, 2010); + + dates = parseCollection("jan 1 to 2"); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 1, 1, 2011); + validateDate(dates.get(1), 1, 2, 2011); + + dates = parseCollection("may 2nd to 5th"); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 5, 2, 2011); + validateDate(dates.get(1), 5, 5, 2011); + + dates = parseCollection("1/3 to 2/3"); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 1, 3, 2011); + validateDate(dates.get(1), 2, 3, 2011); + + dates = parseCollection("2/3 to in one week"); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 2, 3, 2011); + validateDate(dates.get(1), 1, 9, 2011); + + dates = parseCollection("first day of may to last day of may"); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 5, 1, 2011); + validateDate(dates.get(1), 5, 31, 2011); + + dates = parseCollection("feb 28th or 2 days after"); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 2, 28, 2011); + validateDate(dates.get(1), 3, 2, 2011); + + dates = parseCollection("tomorrow at 10 and monday at 11"); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 1, 3, 2011); + validateDate(dates.get(1), 1, 3, 2011); + + dates = parseCollection("tomorrow at 10 through tues at 11"); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 1, 3, 2011); + validateDate(dates.get(1), 1, 4, 2011); + + dates = parseCollection("first day of 2009 to last day of 2009"); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 1, 1, 2009); + validateDate(dates.get(1), 12, 31, 2009); + + dates = parseCollection("first to last day of 2008"); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 1, 1, 2008); + validateDate(dates.get(1), 12, 31, 2008); + + dates = parseCollection("first to last day of september"); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 9, 1, 2011); + validateDate(dates.get(1), 9, 30, 2011); + + dates = parseCollection("first to last day of this september"); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 9, 1, 2011); + validateDate(dates.get(1), 9, 30, 2011); + + dates = parseCollection("first to last day of 2 septembers ago"); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 9, 1, 2009); + validateDate(dates.get(1), 9, 30, 2009); + + dates = parseCollection("first to last day of 2 septembers from now"); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 9, 1, 2012); + validateDate(dates.get(1), 9, 30, 2012); + + dates = parseCollection("for 5 days"); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 1, 2, 2011); + validateDate(dates.get(1), 1, 7, 2011); + + dates = parseCollection("for ten months"); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 1, 2, 2011); + validateDate(dates.get(1), 11, 2, 2011); + + dates = parseCollection("for twenty-five years"); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 1, 2, 2011); + validateDate(dates.get(1), 1, 2, 2036); + + dates = parseCollection("I want to go shopping in Knoxville, TN in the next five to six months."); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 6, 2, 2011); + validateDate(dates.get(1), 7, 2, 2011); + + dates = parseCollection("I want to watch the fireworks in the next two to three months."); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 3, 2, 2011); + validateDate(dates.get(1), 4, 2, 2011); + + dates = parseCollection("september 7th something"); + Assert.assertEquals(1, dates.size()); + validateDate(dates.get(0), 9, 7, 2011); + + dates = parseCollection("september 7th something happened here"); + Assert.assertEquals(1, dates.size()); + validateDate(dates.get(0), 9, 7, 2011); + + dates = parseCollection("bla bla bla 2 and 4 month"); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 3, 2, 2011); + validateDate(dates.get(1), 5, 2, 2011); + } + + // https://github.com/joestelmach/natty/issues/38 + @Test + public void testRelativeDateDifferentTimezone() { + // Prepare + TimeZone.setDefault(TimeZone.getTimeZone("US/Eastern")); + Parser parser = new Parser(TimeZone.getTimeZone("US/Pacific")); + // 2012, June 3, Sunday, 1 a.m. in US/Eastern GMT -4 + // Same time as + // 2012, June 2, Saturday, 10 p.m. in US/Pacific GMT -7 + Calendar earlySunday = new GregorianCalendar(2012, 5, 3, 1, 0); + CalendarSource.setBaseDate(earlySunday.getTime()); + + // Run + Date result = parser.parse("Sunday at 10am").get(0).getDates().get(0); + + // Validate + // Result should be June 3, 2012 + validateDate(result, 6, 3, 2012); + + TimeZone.setDefault(TimeZone.getTimeZone("US/Eastern")); + } + + public static void main(String[] args) { + ConsoleHandler handler = new ConsoleHandler(); + handler.setLevel(Level.ALL); + Logger logger = Logger.getLogger("com.joestelmach.natty"); + logger.setLevel(Level.FINEST); + logger.addHandler(handler); + + String value = "Monday after next"; + + Parser parser = new Parser(); + List groups = parser.parse(value); + for(DateGroup group:groups) { + System.out.println(value); + System.out.println(group.getSyntaxTree().toStringTree()); + System.out.println("line: " + group.getLine() + ", column: " + group.getPosition()); + System.out.println(group.getText()); + System.out.println(group.getDates()); + System.out.println("is time inferred: " + group.isTimeInferred()); + System.out.println("is recurring: " + group.isRecurring()); + System.out.println("recurs until: " + group.getRecursUntil()); + + System.out.println("\n** Parse Locations **"); + for(Entry> entry:group.getParseLocations().entrySet()) { + for(ParseLocation loc:entry.getValue()) { + System.out.println(loc.getRuleName()); + } + } + + List conjunctionLocations = group.getParseLocations().get("conjunction"); + if(conjunctionLocations != null) { + System.out.print("\nconjunctions: "); + for(ParseLocation location:conjunctionLocations) { + System.out.print(location.getText() + " "); + } + } + System.out.print("\n\n"); + } + } +} diff --git a/src/test/java/com/joestelmach/natty/DateTimeTest.java b/src/test/java/com/joestelmach/natty/DateTimeTest.java new file mode 100644 index 00000000..b5a4d5b2 --- /dev/null +++ b/src/test/java/com/joestelmach/natty/DateTimeTest.java @@ -0,0 +1,180 @@ +package com.joestelmach.natty; + +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; + +import java.text.DateFormat; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.TimeZone; + +/** + * Runs the parser through the various datetime formats + * + * @author Joe Stelmach + */ +public class DateTimeTest extends AbstractTest { + @BeforeClass + public static void oneTime() { + Locale.setDefault(Locale.US); + TimeZone.setDefault(TimeZone.getTimeZone("US/Eastern")); + initCalendarAndParser(); + } + + @Test + public void testSpecific() throws Exception { + Date reference = DateFormat.getDateTimeInstance(DateFormat.SHORT, + DateFormat.SHORT).parse("5/19/2012 12:00 am"); + CalendarSource.setBaseDate(reference); + + validateDateTime("1st oct in the year '89 1300 hours", 10, 1, 1989, 13, 0, 0); + validateDateTime("1st oct in the year '89 at 1300 hours", 10, 1, 1989, 13, 0, 0); + validateDateTime("1st oct in the year '89, 13:00", 10, 1, 1989, 13, 0, 0); + validateDateTime("1st oct in the year '89,13:00", 10, 1, 1989, 13, 0, 0); + validateDateTime("1st oct in the year '89, at 13:00", 10, 1, 1989, 13, 0, 0); + validateDateTime("3am on oct 1st 2010", 10, 1, 2010, 3, 0, 0); + validateDateTime("3am, october first 2010", 10, 1, 2010, 3, 0, 0); + validateDateTime("3am,october first 2010", 10, 1, 2010, 3, 0, 0); + validateDateTime("3am, on october first 2010", 10, 1, 2010, 3, 0, 0); + validateDateTime("3am october first 2010", 10, 1, 2010, 3, 0, 0); + validateDateTime("2011-06-17T07:00:00Z", 6, 17, 2011, 3, 0, 0); + validateDateTime("April 20, 10am", 4, 20, 2012, 10, 0, 0); + validateDateTime("April 20 10", 4, 20, 2012, 10, 0, 0); + validateDateTime("April 20 at 10 am", 4, 20, 2012, 10, 0, 0); + validateDateTime("05-Aug-2013 14:10:56 UTC", 8, 5, 2013, 10, 10, 56); + validateDateTime("5/1/13 01:00:00-8", 5, 1, 2013, 5, 0, 0); + validateDateTime("5/1/13 01:00:00 UTC", 4, 30, 2013, 21, 0, 0); + validateDateTime("5/1/13 01:00:00 UTC+8", 4, 30, 2013, 13, 0, 0); + validateDateTime("5/1/13 01:00:00 GMT-1", 4, 30, 2013, 22, 0, 0); + } + + @Test + public void testRelative() throws Exception { + Date reference = DateFormat.getDateTimeInstance(DateFormat.SHORT, + DateFormat.SHORT).parse("2/24/2011 12:00 am"); + CalendarSource.setBaseDate(reference); + + validateDateTime("seven years ago at 3pm", 2, 24, 2004, 15, 0, 0); + validateDateTime("next wed. at 5pm", 3, 2, 2011, 17, 0, 0); + validateDateTime("3 days after next wed at 6a", 3, 5, 2011, 6, 0, 0); + validateDateTime("8pm on the sunday after next wed", 3, 6, 2011, 20, 0, 0); + validateDateTime("two days after today @ 6p", 2, 26, 2011, 18, 0, 0); + validateDateTime("two days from today @ 6p", 2, 26, 2011, 18, 0, 0); + validateDateTime("11:59 on 3 sundays after next wed", 3, 20, 2011, 11, 59, 0); + validateDateTime("the day after next 6pm", 2, 26, 2011, 18, 0, 0); + validateDateTime("the week after next 2a", 3, 10, 2011, 2, 0, 0); + validateDateTime("the month after next 0700", 4, 24, 2011, 7, 0, 0); + validateDateTime("the year after next @ midnight", 2, 24, 2013, 0, 0, 0); + validateDateTime("wed of the week after next in the evening", 3, 9, 2011, 19, 0, 0); + validateDateTime("the 28th of the month after next in the morning", 4, 28, 2011, 8, 0, 0); + validateDateTime("this morning", 2, 24, 2011, 8, 0, 0); + validateDateTime("this afternoon", 2, 24, 2011, 12, 0, 0); + validateDateTime("this evening", 2, 24, 2011, 19, 0, 0); + validateDateTime("today evening", 2, 24, 2011, 19, 0, 0); + validateDateTime("tomorrow evening", 2, 25, 2011, 19, 0, 0); + validateDateTime("friday evening", 2, 25, 2011, 19, 0, 0); + validateDateTime("monday 6 in the morning", 2, 28, 2011, 6, 0, 0); + validateDateTime("monday 4 in the afternoon", 2, 28, 2011, 16, 0, 0); + validateDateTime("monday 9 in the evening", 2, 28, 2011, 21, 0, 0); + validateDateTime("tomorrow @ noon", 2, 25, 2011, 12, 0, 0); + validateDateTime("Acknowledged. Let's meet at 9pm.", 2, 24, 2011, 21, 0, 0); + + validateDateTime("tuesday, 12:50 PM", 3, 1, 2011, 12, 50, 0); + } + + @Test + @Ignore("breaks with non-breaking space utf-8") + public void testRelativeWithNonBreakingSpaceUTF8() { + validateDateTime("tuesday,\u00A012:50 PM", 2, 24, 2011, 12, 50, 0); + } + + @Test + public void testRange() throws Exception { + Date reference = DateFormat.getDateInstance(DateFormat.SHORT).parse("6/12/2010"); + CalendarSource.setBaseDate(reference); + + List dates = parseCollection("2009-03-10 9:00 to 11:00"); + Assert.assertEquals(2, dates.size()); + validateDateTime(dates.get(0), 3, 10, 2009, 9, 0, 0); + validateDateTime(dates.get(1), 3, 10, 2009, 11, 0, 0); + + dates = parseCollection("26 oct 10:00 am to 11:00 am"); + Assert.assertEquals(2, dates.size()); + validateDateTime(dates.get(0), 10, 26, 2010, 10, 0, 0); + validateDateTime(dates.get(1), 10, 26, 2010, 11, 0, 0); + + dates = parseCollection("16:00 nov 6 to 17:00"); + Assert.assertEquals(2, dates.size()); + validateDateTime(dates.get(0), 11, 6, 2010, 16, 0, 0); + validateDateTime(dates.get(1), 11, 6, 2010, 17, 0, 0); + + dates = parseCollection("6am dec 5 to 7am"); + Assert.assertEquals(2, dates.size()); + validateDateTime(dates.get(0), 12, 5, 2010, 6, 0, 0); + validateDateTime(dates.get(1), 12, 5, 2010, 7, 0, 0); + + dates = parseCollection("3/3 21:00 to in 5 days"); + Assert.assertEquals(2, dates.size()); + validateDateTime(dates.get(0), 3, 3, 2010, 21, 0, 0); + validateDateTime(dates.get(1), 6, 17, 2010, 21, 0, 0); + + dates = parseCollection("November 20 2 p.m. to 3 p.m"); + Assert.assertEquals(2, dates.size()); + validateDateTime(dates.get(0), 11, 20, 2010, 14, 0, 0); + validateDateTime(dates.get(1), 11, 20, 2010, 15, 0, 0); + + dates = parseCollection("November 20 2 p.m. - 3 p.m."); + Assert.assertEquals(2, dates.size()); + validateDateTime(dates.get(0), 11, 20, 2010, 14, 0, 0); + validateDateTime(dates.get(1), 11, 20, 2010, 15, 0, 0); + } + + @Test + public void testList() throws Exception { + Date reference = DateFormat.getDateInstance(DateFormat.SHORT).parse("05/19/2012"); + CalendarSource.setBaseDate(reference); + + List dates = + parseCollection("June 25th at 9am and July 2nd at 10am and August 16th at 11am"); + Assert.assertEquals(3, dates.size()); + validateDateTime(dates.get(0), 6, 25, 2012, 9, 0, 0); + validateDateTime(dates.get(1), 7, 2, 2012, 10, 0, 0); + validateDateTime(dates.get(2), 8, 16, 2012, 11, 0, 0); + + dates = parseCollection("June 25th at 10am and July 2nd and August 16th"); + Assert.assertEquals(3, dates.size()); + validateDateTime(dates.get(0), 6, 25, 2012, 10, 0, 0); + validateDateTime(dates.get(1), 7, 2, 2012, 10, 0, 0); + validateDateTime(dates.get(2), 8, 16, 2012, 10, 0, 0); + + dates = parseCollection("June 25th and July 2nd at 10am and August 16th"); + Assert.assertEquals(3, dates.size()); + validateDateTime(dates.get(0), 6, 25, 2012, 0, 0, 0); + validateDateTime(dates.get(1), 7, 2, 2012, 10, 0, 0); + validateDateTime(dates.get(2), 8, 16, 2012, 10, 0, 0); + + dates = parseCollection("June 25th and July 2nd and August 16th at 10am"); + Assert.assertEquals(3, dates.size()); + validateDateTime(dates.get(0), 6, 25, 2012, 0, 0, 0); + validateDateTime(dates.get(1), 7, 2, 2012, 0, 0, 0); + validateDateTime(dates.get(2), 8, 16, 2012, 10, 0, 0); + + dates = parseCollection("slept from 3:30 a.m. To 9:41 a.m. On April 10th"); + Assert.assertEquals(2, dates.size()); + validateDateTime(dates.get(0), 4, 10, 2012, 3, 30, 0); + validateDateTime(dates.get(1), 4, 10, 2012, 9, 41, 0); + } + + @Test + public void shouldParseYYYYasDateNotTime() throws Exception { + Date reference = DateFormat.getDateInstance(DateFormat.SHORT).parse("06/30/2014"); + CalendarSource.setBaseDate(reference); + + //Assert.assertEquals("Thu Jan 01 00:00:00 EDT 2009", parseSingleDate("2009").toString()); + validateDateTime("2009", 1, 1, 2009, 0, 0, 0); + } + +} diff --git a/src/test/java/com/joestelmach/natty/IcsTest.java b/src/test/java/com/joestelmach/natty/IcsTest.java new file mode 100644 index 00000000..f344a8e0 --- /dev/null +++ b/src/test/java/com/joestelmach/natty/IcsTest.java @@ -0,0 +1,149 @@ +package com.joestelmach.natty; + +import java.text.DateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +import org.junit.BeforeClass; +import org.junit.Test; + + +public class IcsTest extends AbstractTest { + + @BeforeClass + public static void oneTime() { + Locale.setDefault(Locale.US); + TimeZone.setDefault(TimeZone.getTimeZone("US/Eastern")); + initCalendarAndParser(); + } + + @Test + public void testUpcomingSeason() throws Exception { + Date reference = DateFormat.getDateInstance(DateFormat.SHORT).parse("5/05/2011"); + CalendarSource.setBaseDate(reference); + + validateDate("spring", 3, 20, 2012); + validateDate("summer", 6, 21, 2011); + validateDate("fall", 9, 23, 2011); + validateDate("autumn", 9, 23, 2011); + validateDate("winter", 12, 22, 2011); + } + + @Test + public void testUpcomingHoliday() throws Exception { + Date reference = DateFormat.getDateInstance(DateFormat.SHORT).parse("11/05/2011"); + CalendarSource.setBaseDate(reference); + + validateDate("april fool's day", 4, 1, 2012); + validateDate("black friday", 11, 25, 2011); + validateDate("christmas", 12, 25, 2011); + validateDate("christmas eve", 12, 24, 2011); + validateDate("columbus day", 10, 8, 2012); + validateDate("earth day", 4, 22, 2012); + validateDate("easter", 4, 8, 2012); + validateDate("father's day", 6, 17, 2012); + validateDate("flag day", 6, 14, 2012); + validateDate("good friday", 4, 6, 2012); + validateDate("groundhog day", 2, 2, 2012); + validateDate("halloween", 10, 31, 2012); + validateDate("independence day", 7, 4, 2012); + validateDate("kwanzaa", 12, 26, 2011); + validateDate("labor day", 9, 3, 2012); + validateDate("mlk day", 1, 16, 2012); + validateDate("memorial day", 5, 28, 2012); + validateDate("mother's day", 5, 13, 2012); + validateDate("new year's day", 1, 1, 2012); + validateDate("new year's eve", 12, 31, 2011); + validateDate("patriot day", 9, 11, 2012); + validateDate("president's day", 2, 20, 2012); + validateDate("st patty's day", 3, 17, 2012); + validateDate("tax day", 4, 15, 2012); + validateDate("thanksgiving", 11, 24, 2011); + validateDate("election day", 11, 8, 2011); + validateDate("valentine day", 2, 14, 2012); + validateDate("veterans day", 11, 11, 2011); + } + + @Test + public void testRelativeHolidays() throws Exception { + Date reference = DateFormat.getDateInstance(DateFormat.SHORT).parse("11/05/2011"); + CalendarSource.setBaseDate(reference); + + validateDate("2 black fridays from now", 11, 23, 2012); + validateDate("three memorial days ago", 5, 25, 2009); + } + + @Test + public void testSeasonsByYear() throws Exception { + Date reference = DateFormat.getDateInstance(DateFormat.SHORT).parse("11/05/2011"); + CalendarSource.setBaseDate(reference); + + validateDate("spring 2010", 3, 20, 2010); + validateDate("spring 2018", 3, 20, 2018); + + validateDate("summer 2012", 6, 20, 2012); + validateDate("summer 2015", 6, 21, 2015); + + validateDate("fall 2011", 9, 23, 2011); + validateDate("fall 2012", 9, 22, 2012); + validateDate("autumn 2016", 9, 22, 2016); + + validateDate("winter 2016", 12, 21, 2016); + validateDate("winter 2011", 12, 22, 2011); + } + + @Test + public void testHolidaysByYear() throws Exception { + Date reference = DateFormat.getDateInstance(DateFormat.SHORT).parse("11/05/2011"); + CalendarSource.setBaseDate(reference); + + validateDate("april fool's day 2000", 4, 1, 2000); + validateDate("black friday 2001", 11, 23, 2001); + validateDate("christmas 2002", 12, 25, 2002); + validateDate("christmas eve 2003", 12, 24, 2003); + validateDate("columbus day 2010", 10, 11, 2010); + validateDate("earth day 2005", 4, 22, 2005); + validateDate("easter '06", 4, 16, 2006); + validateDate("father's day '07", 6, 17, 2007); + validateDate("flag day '08", 6, 14, 2008); + validateDate("good friday '09", 4, 10, 2009); + validateDate("groundhog day '10", 2, 2, 2010); + validateDate("halloween '11", 10, 31, 2011); + validateDate("independence day '12", 7, 4, 2012); + validateDate("kwanzaa '13", 12, 26, 2013); + validateDate("labor day '14", 9, 1, 2014); + validateDate("mlk day '15", 1, 19, 2015); + validateDate("memorial day '16", 5, 30, 2016); + validateDate("mother's day 2017", 5, 14, 2017); + validateDate("new year's day 2018", 1, 1, 2018); + validateDate("new year's eve 2019", 12, 31, 2018); + validateDate("patriot day 2020", 9, 11, 2020); + validateDate("president's day 2019", 2, 18, 2019); + validateDate("st patty's day 2018", 3, 17, 2018); + validateDate("tax day 2017", 4, 15, 2017); + validateDate("thanksgiving 2016", 11, 24, 2016); + validateDate("election day 2015", 11, 3, 2015); + validateDate("valentine day 2014", 2, 14, 2014); + validateDate("veterans day 2013", 11, 11, 2013); + } + + @Test + public void testHolidaysWithModifiers() throws Exception { + Date reference = DateFormat.getDateInstance(DateFormat.SHORT).parse("11/05/2011"); + CalendarSource.setBaseDate(reference); + + validateDate("four days before veterans day 2013", 11, 7, 2013); + validateDate("two days after two thanksgivings from now", 11, 24, 2012); + } + + @Test + public void testSeasonsWithModifiers() throws Exception { + Date reference = DateFormat.getDateInstance(DateFormat.SHORT).parse("11/05/2011"); + CalendarSource.setBaseDate(reference); + + validateDate("four days before fall 2013", 9, 18, 2013); + validateDate("two days after two summers from now", 6, 23, 2013); + validateDate("three summers ago", 6, 21, 2009); + } +} diff --git a/src/test/java/com/joestelmach/natty/RecurrenceTest.java b/src/test/java/com/joestelmach/natty/RecurrenceTest.java new file mode 100644 index 00000000..187ac292 --- /dev/null +++ b/src/test/java/com/joestelmach/natty/RecurrenceTest.java @@ -0,0 +1,44 @@ +package com.joestelmach.natty; + +import java.text.DateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +import junit.framework.Assert; +import org.junit.BeforeClass; + +import org.junit.Test; + +/** + * + * @author Joe Stelmach + */ +public class RecurrenceTest extends AbstractTest { + @BeforeClass + public static void oneTime() { + Locale.setDefault(Locale.US); + TimeZone.setDefault(TimeZone.getTimeZone("US/Eastern")); + initCalendarAndParser(); + } + + @Test + public void testRelative() throws Exception { + Date reference = DateFormat.getDateTimeInstance(DateFormat.SHORT, + DateFormat.SHORT).parse("3/3/2011 12:00 am"); + CalendarSource.setBaseDate(reference); + + DateGroup group = _parser.parse("every friday until two tuesdays from now").get(0); + Assert.assertEquals(1, group.getDates().size()); + validateDate(group.getDates().get(0), 3, 4, 2011); + Assert.assertTrue(group.isRecurring()); + validateDate(group.getRecursUntil(), 3, 15, 2011); + + group = _parser.parse("every saturday or sunday").get(0); + Assert.assertEquals(2, group.getDates().size()); + validateDate(group.getDates().get(0), 3, 5, 2011); + validateDate(group.getDates().get(1), 3, 6, 2011); + Assert.assertTrue(group.isRecurring()); + Assert.assertNull(group.getRecursUntil()); + } +} diff --git a/src/test/java/com/joestelmach/natty/SearchTest.java b/src/test/java/com/joestelmach/natty/SearchTest.java new file mode 100644 index 00000000..f5787d89 --- /dev/null +++ b/src/test/java/com/joestelmach/natty/SearchTest.java @@ -0,0 +1,287 @@ +package com.joestelmach.natty; + +import java.text.DateFormat; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.TimeZone; + +import junit.framework.Assert; + +import org.junit.Test; +import org.junit.BeforeClass; + +import com.joestelmach.natty.CalendarSource; +import com.joestelmach.natty.DateGroup; +import com.joestelmach.natty.Parser; + +/** + * + * @author Joe Stelmach + */ +public class SearchTest extends AbstractTest { + @BeforeClass + public static void oneTime() { + Locale.setDefault(Locale.US); + TimeZone.setDefault(TimeZone.getTimeZone("US/Eastern")); + initCalendarAndParser(); + } + + @Test + public void test() throws Exception { + Date reference = DateFormat.getDateInstance(DateFormat.SHORT).parse("2/20/2011"); + CalendarSource.setBaseDate(reference); + + Parser parser = new Parser(); + List groups = parser.parse("golf tomorrow at 9 AM at pebble beach"); + Assert.assertEquals(1, groups.size()); + DateGroup group = groups.get(0); + Assert.assertEquals(1, group.getLine()); + Assert.assertEquals(5, group.getPosition()); + Assert.assertEquals(1, group.getDates().size()); + validateDate(group.getDates().get(0), 2, 21, 2011); + validateTime(group.getDates().get(0), 9, 0, 0); + + groups = parser.parse("golf with friends tomorrow at 10 "); + Assert.assertEquals(1, groups.size()); + group = groups.get(0); + Assert.assertEquals(1, group.getLine()); + Assert.assertEquals(18, group.getPosition()); + Assert.assertEquals(1, group.getDates().size()); + validateDate(group.getDates().get(0), 2, 21, 2011); + validateTime(group.getDates().get(0), 10, 0, 0); + + parser = new Parser(); + groups = parser.parse("golf with freinds tomorrow at 9 or Thursday at 10 am"); + Assert.assertEquals(1, groups.size()); + List dates = groups.get(0).getDates(); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 2, 21, 2011); + validateTime(dates.get(0), 9, 0, 0); + validateDate(dates.get(1), 2, 24, 2011); + validateTime(dates.get(1), 10, 0, 0); + + groups = parser.parse("golf with friends tomorrow at 9 or Thursday at 10"); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 2, 21, 2011); + validateTime(dates.get(0), 9, 0, 0); + validateDate(dates.get(1), 2, 24, 2011); + validateTime(dates.get(1), 10, 0, 0); + + groups = parser.parse("I want to go to park tomorrow and then email john@aol.com"); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(1, dates.size()); + validateDate(dates.get(0), 2, 21, 2011); + + groups = parser.parse("I want to pay off all my debt in the next two years."); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 2, 20, 2011); + validateDate(dates.get(1), 2, 20, 2013); + + groups = parser.parse("I want to purchase a car in the next month."); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(1, dates.size()); + validateDate(dates.get(0), 3, 20, 2011); + + groups = parser.parse("I want to plan a get-together with my friends for this Friday."); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(1, dates.size()); + validateDate(dates.get(0), 2, 25, 2011); + + groups = parser.parse("I want to lose five pounds in the next two months."); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 2, 20, 2011); + validateDate(dates.get(1), 4, 20, 2011); + + groups = parser.parse("I want to finalize my college schedule by next week."); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(1, dates.size()); + validateDate(dates.get(0), 2, 27, 2011); + + groups = parser.parse("I want to read this weekend."); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(1, dates.size()); + validateDate(dates.get(0), 2, 26, 2011); + + groups = parser.parse("I want to travel a big chunk of world next year."); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(1, dates.size()); + validateDate(dates.get(0), 2, 20, 2012); + + groups = parser.parse("last 2 weeks"); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 2, 20, 2011); + validateDate(dates.get(1), 2, 6, 2011); + + groups = parser.parse("last 5 years"); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 2, 20, 2011); + validateDate(dates.get(1), 2, 20, 2006); + + groups = parser.parse("next 5 years"); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(2, dates.size()); + validateDate(dates.get(0), 2, 20, 2011); + validateDate(dates.get(1), 2, 20, 2016); + + groups = parser.parse("I want to go to my doctors appointment on May 15, 2011."); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(1, dates.size()); + validateDate(dates.get(0), 5, 15, 2011); + + groups = parser.parse("I intend to become a zombie on December, 21st 2012."); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(1, dates.size()); + validateDate(dates.get(0), 12, 21, 2012); + + groups = parser.parse("I want to hire a virtual assistant to do research for me on March 15, 2011"); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(1, dates.size()); + validateDate(dates.get(0), 3, 15, 2011); + + groups = parser.parse("I want to see my mother on sunday."); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(1, dates.size()); + validateDate(dates.get(0), 2, 27, 2011); + + groups = parser.parse("I want to be able to jog 3 miles non-stop by September."); + Assert.assertEquals(2, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(1, dates.size()); + validateDateTime(dates.get(0), 2, 20, 2011, 3, 0, 0); + dates = groups.get(1).getDates(); + Assert.assertEquals(1, dates.size()); + validateDate(dates.get(0), 9, 1, 2011); + + groups = parser.parse("I want to lose 10 lbs in 10 days"); + Assert.assertEquals(2, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(1, dates.size()); + validateDateTime(dates.get(0), 2, 20, 2011, 10, 0, 0); + dates = groups.get(1).getDates(); + Assert.assertEquals(1, dates.size()); + validateDate(dates.get(0), 3, 2, 2011); + + groups = parser.parse("I want to visit my grandfathers grave on December 30 2011"); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(1, dates.size()); + validateDate(dates.get(0), 12, 30, 2011); + + groups = parser.parse("i want to have 1 kid this year"); + Assert.assertEquals(2, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(1, dates.size()); + validateDateTime(dates.get(0), 2, 20, 2011, 1, 0, 0); + dates = groups.get(1).getDates(); + Assert.assertEquals(1, dates.size()); + validateDate(dates.get(0), 2, 20, 2011); + + groups = parser.parse("save $1000 by September"); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(1, dates.size()); + validateDate(dates.get(0), 9, 1, 2011); + + groups = parser.parse("have my son play at muse music in provo UT at the 3 band cause they always have fog on the third band at 7:30"); + Assert.assertEquals(2, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(1, dates.size()); + validateDateTime(dates.get(0), 2, 20, 2011, 3, 0, 0); + dates = groups.get(1).getDates(); + Assert.assertEquals(1, dates.size()); + validateDateTime(dates.get(0), 2, 20, 2011, 7, 30, 0); + + groups = parser.parse("i want to eat chinese tonight"); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(1, dates.size()); + validateDateTime(dates.get(0), 2, 20, 2011, 20, 0, 0); + + groups = parser.parse("Watch School Spirits on June 20 on syfy channel"); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(1, dates.size()); + validateDate(dates.get(0), 6, 20, 2011); + + groups = parser.parse("Watch School Spirits on June 20 on"); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(1, dates.size()); + validateDate(dates.get(0), 6, 20, 2011); + + groups = parser.parse("Watch School Spirits on June 20"); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(1, dates.size()); + validateDate(dates.get(0), 6, 20, 2011); + + groups = parser.parse("hillary clinton sep 13, 2013"); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(1, dates.size()); + validateDate(dates.get(0), 9, 13, 2013); + Assert.assertEquals(16, groups.get(0).getPosition()); + Assert.assertEquals("sep 13, 2013", groups.get(0).getText()); + + groups = parser.parse("hillary clinton 9/13/2013"); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(1, dates.size()); + validateDate(dates.get(0), 9, 13, 2013); + Assert.assertEquals(16, groups.get(0).getPosition()); + Assert.assertEquals("9/13/2013", groups.get(0).getText()); + + groups = parser.parse("hillary clintoo sep 13, 2013"); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(1, dates.size()); + validateDate(dates.get(0), 9, 13, 2013); + Assert.assertEquals(16, groups.get(0).getPosition()); + Assert.assertEquals("sep 13, 2013", groups.get(0).getText()); + + groups = parser.parse("clinton sep 13 2013"); + Assert.assertEquals(1, groups.size()); + dates = groups.get(0).getDates(); + Assert.assertEquals(1, dates.size()); + validateDate(dates.get(0), 9, 13, 2013); + Assert.assertEquals(8, groups.get(0).getPosition()); + Assert.assertEquals("sep 13 2013", groups.get(0).getText()); + + groups = parser.parse("wedding dinner with Pam"); + Assert.assertEquals(0, groups.size()); + + groups = parser.parse("yummy fried chicken"); + Assert.assertEquals(0, groups.size()); + + groups = parser.parse("I am friend with Pam"); + Assert.assertEquals(0, groups.size()); + + groups = parser.parse("bfriday blah blah"); + Assert.assertEquals(0, groups.size()); + + groups = parser.parse("dinner bmong friends"); + Assert.assertEquals(0, groups.size()); + } +} diff --git a/src/test/java/com/joestelmach/natty/ThreadSafetyTest.java b/src/test/java/com/joestelmach/natty/ThreadSafetyTest.java new file mode 100644 index 00000000..5acbcb8b --- /dev/null +++ b/src/test/java/com/joestelmach/natty/ThreadSafetyTest.java @@ -0,0 +1,74 @@ +package com.joestelmach.natty; + +import java.text.DateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.TimeZone; + +import org.junit.Assert; + +import org.junit.Test; +import org.junit.BeforeClass; + +public class ThreadSafetyTest extends AbstractTest { + + private final static int NUM_OF_THREADS = 10; + private final static int JOIN_TIMEOUT = 2000; // 2 seconds + private static DateFormat dateFormat; + + private AtomicInteger numOfCorrectResults = new AtomicInteger(); + + @BeforeClass + public static void oneTime() { + Locale.setDefault(Locale.US); + TimeZone.setDefault(TimeZone.getTimeZone("US/Eastern")); + dateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.US); + initCalendarAndParser(); + } + + @Test + public void testManyThreads() throws Exception { + Thread[] threads = new Thread[NUM_OF_THREADS]; + for (int i = 0; i < NUM_OF_THREADS; i++) { + threads[i] = new Thread(new Tester(i)); + } + for (Thread thread : threads) { + thread.start(); + } + for (Thread thread : threads) { + thread.join(JOIN_TIMEOUT); + } + Assert.assertEquals(NUM_OF_THREADS, numOfCorrectResults.get()); + } + + private class Tester implements Runnable { + + private Date baseDate; + private int baseMinute; + + public Tester(int baseMinute) throws Exception { + String date = String.format("3/3/2011 1:%02d am", baseMinute); + this.baseDate = dateFormat.parse(date); + this.baseMinute = baseMinute; + } + + public void run() { + CalendarSource.setBaseDate(baseDate); + try { + // Immitate some long running task. + Thread.sleep(100); + } catch (Exception e) { } + String newDate = "4/4/2012"; + Date parsed = _parser.parse(newDate).get(0).getDates().get(0); + validateThread(parsed, baseMinute); + numOfCorrectResults.incrementAndGet(); + } + } + + // We need this method, because validateDate and validateTime are not thread safe. + private synchronized void validateThread(Date date, int baseMinute) { + validateDate(date, 4, 4, 2012); + validateTime(date, 1, baseMinute, 0); + } +} diff --git a/src/test/java/com/joestelmach/natty/TimeTest.java b/src/test/java/com/joestelmach/natty/TimeTest.java new file mode 100644 index 00000000..1752b93d --- /dev/null +++ b/src/test/java/com/joestelmach/natty/TimeTest.java @@ -0,0 +1,107 @@ +package com.joestelmach.natty; + +import java.text.DateFormat; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.TimeZone; + +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * Runs the parser through the various time formats + * + * @author Joe Stelmach + */ +public class TimeTest extends AbstractTest { + @BeforeClass + public static void oneTime() { + Locale.setDefault(Locale.US); + TimeZone.setDefault(TimeZone.getTimeZone("US/Eastern")); + initCalendarAndParser(); + } + + /** + * Runs the parser through the various time formats + * @throws Exception + */ + @Test + public void testFormal() throws Exception { + Date reference = DateFormat.getDateInstance(DateFormat.SHORT).parse("1/02/2011"); + CalendarSource.setBaseDate(reference); + validateTime("0600h", 6, 0, 0); + validateTime("06:00h", 6, 0, 0); + validateTime("06:00 hours", 6, 0, 0); + validateTime("0000", 0, 0, 0); + validateTime("0700h", 7, 0, 0); + validateTime("6pm", 18, 0, 0); + validateTime("5:30 a.m.", 5, 30, 0); + validateTime("5", 5, 0, 0); + validateTime("12:59", 12, 59, 0); + validateTime("23:59:28", 23, 59, 28); + validateTime("00:00", 0, 0, 0); + validateTime("10:00am", 10, 0, 0); + validateTime("10a", 10, 0, 0); + validateTime("10am", 10, 0, 0); + validateTime("10", 10, 0, 0); + validateTime("8p", 20, 0, 0); + validateTime("8pm", 20, 0, 0); + validateTime("8 pm", 20, 0, 0); + } + + @Test + public void testRelaxed() throws Exception { + Date reference = DateFormat.getDateInstance(DateFormat.SHORT).parse("1/02/2011"); + CalendarSource.setBaseDate(reference); + validateTime("noon", 12, 0, 0); + validateTime("afternoon", 12, 0, 0); + validateTime("midnight", 0, 0, 0); + validateTime("mid-night", 0, 0, 0); + validateTime("6 in the morning", 6, 0, 0); + validateTime("4 in the afternoon", 16, 0, 0); + validateTime("evening", 19, 0, 0); + validateTime("10 hours before noon", 2, 0, 0); + validateTime("10 hours before midnight", 14, 0, 0); + validateTime("5 hours after noon", 17, 0, 0); + validateTime("5 hours after midnight", 5, 0, 0); + } + + @Test + public void testRelative() throws Exception { + CalendarSource.setBaseDate(DateFormat.getTimeInstance(DateFormat.SHORT).parse("12:00 pm")); + validateTime("in 5 seconds", 12, 0, 5); + validateTime("in 5 minutes", 12, 5, 0); + validateTime("in 5 hours", 17, 0, 0); + validateTime("4 seconds from now", 12, 0, 4); + validateTime("4 minutes from now", 12, 4, 0); + validateTime("4 hours from now", 16, 0, 0); + validateTime("next minute", 12, 1, 0); + validateTime("last minute", 11, 59, 0); + validateTime("next second", 12, 0, 1); + validateTime("this second", 12, 0, 0); + validateTime("this minute", 12, 0, 0); + validateTime("this hour", 12, 0, 0); + } + + @Test + public void testRange() throws Exception { + CalendarSource.setBaseDate(DateFormat.getTimeInstance(DateFormat.SHORT).parse("12:00 pm")); + + List dates = parseCollection("for six hours"); + Assert.assertEquals(2, dates.size()); + validateTime(dates.get(0), 12, 0, 0); + validateTime(dates.get(1), 18, 0, 0); + + dates = parseCollection("for 12 minutes"); + Assert.assertEquals(2, dates.size()); + validateTime(dates.get(0), 12, 0, 0); + validateTime(dates.get(1), 12, 12, 0); + + dates = parseCollection("for 10 seconds"); + Assert.assertEquals(2, dates.size()); + validateTime(dates.get(0), 12, 0, 0); + validateTime(dates.get(1), 12, 0, 10); + } +} diff --git a/src/test/java/com/joestelmach/natty/grammar/AbstractGrammarTest.java b/src/test/java/com/joestelmach/natty/grammar/AbstractGrammarTest.java new file mode 100644 index 00000000..1e3a69fc --- /dev/null +++ b/src/test/java/com/joestelmach/natty/grammar/AbstractGrammarTest.java @@ -0,0 +1,70 @@ +package com.joestelmach.natty.grammar; + +import java.io.ByteArrayInputStream; +import java.lang.reflect.Method; + +import junit.framework.Assert; + +import org.antlr.runtime.ANTLRInputStream; +import org.antlr.runtime.CommonTokenStream; +import org.antlr.runtime.ParserRuleReturnScope; +import org.antlr.runtime.tree.CommonTree; +import org.antlr.runtime.tree.CommonTreeNodeStream; +import org.antlr.runtime.tree.Tree; + +import com.joestelmach.natty.ANTLRNoCaseInputStream; +import com.joestelmach.natty.ParseListener; +import com.joestelmach.natty.generated.DateLexer; +import com.joestelmach.natty.generated.DateParser; +import com.joestelmach.natty.generated.TreeRewrite; + +public abstract class AbstractGrammarTest { + protected String _ruleName; + + /** + * + * @param input + * @param ast + */ + protected void assertAST(String input, String ast) throws Exception { + Assert.assertEquals(ast, buildAST(input)); + } + + /** + * + * @param value + * @return + * @throws Exception + */ + protected String buildAST(String value) throws Exception { + DateParser parser = buildParser(value); + Class klass = Class.forName("com.joestelmach.natty.generated.DateParser"); + Method meth = klass.getMethod(_ruleName, (Class[]) null); + ParserRuleReturnScope ret = (ParserRuleReturnScope) meth.invoke(parser, (Object[]) null); + + Tree tree = (Tree)ret.getTree(); + // rewrite the tree (temporary fix for http://www.antlr.org/jira/browse/ANTLR-427) + CommonTreeNodeStream nodes = new CommonTreeNodeStream(tree); + TreeRewrite s = new TreeRewrite(nodes); + tree = (CommonTree)s.downup(tree); + + return tree.toStringTree(); + } + + /** + * + * @param value + * @return + */ + private DateParser buildParser(String value) throws Exception { + // lex + ANTLRInputStream input = new ANTLRNoCaseInputStream( + new ByteArrayInputStream(value.getBytes())); + DateLexer lexer = new DateLexer(input); + CommonTokenStream tokens = new CommonTokenStream(lexer); + + // parse + ParseListener listener = new ParseListener(); + return new DateParser(tokens, listener); + } +} diff --git a/src/test/java/com/joestelmach/natty/grammar/DateGrammarTest.java b/src/test/java/com/joestelmach/natty/grammar/DateGrammarTest.java new file mode 100644 index 00000000..4f714188 --- /dev/null +++ b/src/test/java/com/joestelmach/natty/grammar/DateGrammarTest.java @@ -0,0 +1,647 @@ +package com.joestelmach.natty.grammar; + +import org.junit.Test; + +public class DateGrammarTest extends AbstractGrammarTest { + @Test + public void date() throws Exception { + _ruleName = "date"; + + assertAST("the day before yesterday", "(RELATIVE_DATE (SEEK < by_day 1 (RELATIVE_DATE (SEEK < by_day 1 day))))"); + assertAST("1st oct in the year '89", "(EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 89))"); + assertAST("2009-10-10", "(EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 10) (YEAR_OF 2009))"); + assertAST("seven years ago", "(RELATIVE_DATE (SEEK < by_day 7 year))"); + assertAST("next monday", "(RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 2)))"); + } + + @Test + public void alternative_day_of_month_list() throws Exception { + _ruleName = "alternative_day_of_month_list"; + + assertAST("mon may 15 or 16", + "(DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 5) (DAY_OF_MONTH 15))) (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 5) (DAY_OF_MONTH 16)))"); + + assertAST("mon may 15 to 16", + "(DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 5) (DAY_OF_MONTH 15))) (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 5) (DAY_OF_MONTH 16)))"); + + assertAST("mon may 15 and 16", + "(DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 5) (DAY_OF_MONTH 15))) (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 5) (DAY_OF_MONTH 16)))"); + + assertAST("mon may 15 through 16", + "(DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 5) (DAY_OF_MONTH 15))) (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 5) (DAY_OF_MONTH 16)))"); + + assertAST("mon may 15 or 16", + "(DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 5) (DAY_OF_MONTH 15))) (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 5) (DAY_OF_MONTH 16)))"); + + assertAST("first or last day of september", + "(DATE_TIME (RELATIVE_DATE (EXPLICIT_SEEK (MONTH_OF_YEAR 9)) (EXPLICIT_SEEK (DAY_OF_MONTH 1)))) (DATE_TIME (RELATIVE_DATE (EXPLICIT_SEEK (MONTH_OF_YEAR 9)) (EXPLICIT_SEEK (DAY_OF_MONTH 31))))"); + + assertAST("first or last day of september at 5pm", + "(DATE_TIME (RELATIVE_DATE (EXPLICIT_SEEK (MONTH_OF_YEAR 9)) (EXPLICIT_SEEK (DAY_OF_MONTH 1))) (EXPLICIT_TIME (HOURS_OF_DAY 5) (MINUTES_OF_HOUR 0) pm)) (DATE_TIME (RELATIVE_DATE (EXPLICIT_SEEK (MONTH_OF_YEAR 9)) (EXPLICIT_SEEK (DAY_OF_MONTH 31))) (EXPLICIT_TIME (HOURS_OF_DAY 5) (MINUTES_OF_HOUR 0) pm))"); + + + assertAST("first or last day of next september at 6am", + "(DATE_TIME (RELATIVE_DATE (SEEK > by_week 1 (MONTH_OF_YEAR 9)) (EXPLICIT_SEEK (DAY_OF_MONTH 1))) (EXPLICIT_TIME (HOURS_OF_DAY 6) (MINUTES_OF_HOUR 0) am)) (DATE_TIME (RELATIVE_DATE (SEEK > by_week 1 (MONTH_OF_YEAR 9)) (EXPLICIT_SEEK (DAY_OF_MONTH 31))) (EXPLICIT_TIME (HOURS_OF_DAY 6) (MINUTES_OF_HOUR 0) am))"); + + assertAST("first or last day of 2 septembers from now at 5pm", + "(DATE_TIME (RELATIVE_DATE (SEEK > by_day 2 (MONTH_OF_YEAR 9)) (EXPLICIT_SEEK (DAY_OF_MONTH 1))) (EXPLICIT_TIME (HOURS_OF_DAY 5) (MINUTES_OF_HOUR 0) pm)) (DATE_TIME (RELATIVE_DATE (SEEK > by_day 2 (MONTH_OF_YEAR 9)) (EXPLICIT_SEEK (DAY_OF_MONTH 31))) (EXPLICIT_TIME (HOURS_OF_DAY 5) (MINUTES_OF_HOUR 0) pm))"); + } + + @Test + public void global_date_prefix() throws Exception { + _ruleName = "global_date_prefix"; + + assertAST("the day after", "> by_day 1"); + assertAST("day after", "> by_day 1"); + assertAST("2 days after", "> by_day 2"); + assertAST("three days before", "< by_day 3"); + assertAST("six months after", "> by_month 6"); + assertAST("3 weeks before", "< by_week 3"); + assertAST("10 years after", "> by_year 10"); + assertAST("the day before", "< by_day 1"); + assertAST("day before", "< by_day 1"); + } + + @Test + public void relaxed_date() throws Exception { + _ruleName = "relaxed_date"; + + assertAST("oct 1, 1980", "(EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 1980))"); + assertAST("oct. 1, 1980", "(EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 1980))"); + assertAST("oct 1,1980", "(EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 1980))"); + assertAST("1st oct in the year '89", "(EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 89))"); + assertAST("thirty first of december '80", "(EXPLICIT_DATE (MONTH_OF_YEAR 12) (DAY_OF_MONTH 31) (YEAR_OF 80))"); + assertAST("the first of december in the year 1980", "(EXPLICIT_DATE (MONTH_OF_YEAR 12) (DAY_OF_MONTH 1) (YEAR_OF 1980))"); + assertAST("the 2 of february in the year 1980", "(EXPLICIT_DATE (MONTH_OF_YEAR 2) (DAY_OF_MONTH 2) (YEAR_OF 1980))"); + assertAST("the 2nd of february in the year 1980", "(EXPLICIT_DATE (MONTH_OF_YEAR 2) (DAY_OF_MONTH 2) (YEAR_OF 1980))"); + assertAST("the second of february in the year 1980", "(EXPLICIT_DATE (MONTH_OF_YEAR 2) (DAY_OF_MONTH 2) (YEAR_OF 1980))"); + assertAST("jan. 2nd", "(EXPLICIT_DATE (MONTH_OF_YEAR 1) (DAY_OF_MONTH 2))"); + assertAST("sun, nov 21 2010", "(EXPLICIT_DATE (MONTH_OF_YEAR 11) (DAY_OF_MONTH 21) (DAY_OF_WEEK 1) (YEAR_OF 2010))"); + } + + @Test + public void formal_date() throws Exception { + _ruleName = "formal_date"; + + assertAST("2009-10-10", "(EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 10) (YEAR_OF 2009))"); + assertAST("1980-1-2", "(EXPLICIT_DATE (MONTH_OF_YEAR 1) (DAY_OF_MONTH 2) (YEAR_OF 1980))"); + assertAST("12/12/12", "(EXPLICIT_DATE (MONTH_OF_YEAR 12) (DAY_OF_MONTH 12) (YEAR_OF 12))"); + assertAST("3/4", "(EXPLICIT_DATE (MONTH_OF_YEAR 3) (DAY_OF_MONTH 4))"); + assertAST("sun, 11/21/2010", "(EXPLICIT_DATE (MONTH_OF_YEAR 11) (DAY_OF_MONTH 21) (DAY_OF_WEEK 1) (YEAR_OF 2010))"); + } + + @Test + public void formal_month_of_year() throws Exception { + _ruleName = "formal_month_of_year"; + + assertAST("01", "(MONTH_OF_YEAR 01)"); + assertAST("1", "(MONTH_OF_YEAR 1)"); + assertAST("02", "(MONTH_OF_YEAR 02)"); + assertAST("2", "(MONTH_OF_YEAR 2)"); + assertAST("03", "(MONTH_OF_YEAR 03)"); + assertAST("3", "(MONTH_OF_YEAR 3)"); + assertAST("04", "(MONTH_OF_YEAR 04)"); + assertAST("4", "(MONTH_OF_YEAR 4)"); + assertAST("05", "(MONTH_OF_YEAR 05)"); + assertAST("5", "(MONTH_OF_YEAR 5)"); + assertAST("06", "(MONTH_OF_YEAR 06)"); + assertAST("6", "(MONTH_OF_YEAR 6)"); + assertAST("07", "(MONTH_OF_YEAR 07)"); + assertAST("7", "(MONTH_OF_YEAR 7)"); + assertAST("08", "(MONTH_OF_YEAR 08)"); + assertAST("8", "(MONTH_OF_YEAR 8)"); + assertAST("09", "(MONTH_OF_YEAR 09)"); + assertAST("9", "(MONTH_OF_YEAR 9)"); + assertAST("10", "(MONTH_OF_YEAR 10)"); + assertAST("11", "(MONTH_OF_YEAR 11)"); + assertAST("12", "(MONTH_OF_YEAR 12)"); + //"00" FAIL + //"0" FAIL + //"13" FAIL + } + + @Test + public void formal_day_of_month() throws Exception { + _ruleName = "formal_day_of_month"; + + assertAST("01", "(DAY_OF_MONTH 01)"); + assertAST("1", "(DAY_OF_MONTH 1)"); + assertAST("02", "(DAY_OF_MONTH 02)"); + assertAST("2", "(DAY_OF_MONTH 2)"); + assertAST("03", "(DAY_OF_MONTH 03)"); + assertAST("3", "(DAY_OF_MONTH 3)"); + assertAST("04", "(DAY_OF_MONTH 04)"); + assertAST("4", "(DAY_OF_MONTH 4)"); + assertAST("05", "(DAY_OF_MONTH 05)"); + assertAST("5", "(DAY_OF_MONTH 5)"); + assertAST("06", "(DAY_OF_MONTH 06)"); + assertAST("6", "(DAY_OF_MONTH 6)"); + assertAST("07", "(DAY_OF_MONTH 07)"); + assertAST("7", "(DAY_OF_MONTH 7)"); + assertAST("08", "(DAY_OF_MONTH 08)"); + assertAST("8", "(DAY_OF_MONTH 8)"); + assertAST("09", "(DAY_OF_MONTH 09)"); + assertAST("9", "(DAY_OF_MONTH 9)"); + assertAST("10", "(DAY_OF_MONTH 10)"); + assertAST("11", "(DAY_OF_MONTH 11)"); + assertAST("12", "(DAY_OF_MONTH 12)"); + assertAST("13", "(DAY_OF_MONTH 13)"); + assertAST("14", "(DAY_OF_MONTH 14)"); + assertAST("15", "(DAY_OF_MONTH 15)"); + assertAST("16", "(DAY_OF_MONTH 16)"); + assertAST("17", "(DAY_OF_MONTH 17)"); + assertAST("18", "(DAY_OF_MONTH 18)"); + assertAST("19", "(DAY_OF_MONTH 19)"); + assertAST("20", "(DAY_OF_MONTH 20)"); + assertAST("21", "(DAY_OF_MONTH 21)"); + assertAST("22", "(DAY_OF_MONTH 22)"); + assertAST("23", "(DAY_OF_MONTH 23)"); + assertAST("24", "(DAY_OF_MONTH 24)"); + assertAST("25", "(DAY_OF_MONTH 25)"); + assertAST("26", "(DAY_OF_MONTH 26)"); + assertAST("27", "(DAY_OF_MONTH 27)"); + assertAST("28", "(DAY_OF_MONTH 28)"); + assertAST("29", "(DAY_OF_MONTH 29)"); + assertAST("30", "(DAY_OF_MONTH 30)"); + assertAST("31", "(DAY_OF_MONTH 31)"); + //"00" FAIL + //"0" FAIL + //"32" FAIL + } + + @Test + public void formal_year() throws Exception { + _ruleName = "formal_year"; + + assertAST("1999", "(YEAR_OF 1999)"); + assertAST("80", "(YEAR_OF 80)"); + assertAST("0000", "(YEAR_OF 0000)"); + assertAST("2010", "(YEAR_OF 2010)"); + assertAST("03", "(YEAR_OF 03)"); + //"037" FAIL + //"0" FAIL + //"03700" FAIL + } + + @Test + public void formal_year_four_digits() throws Exception { + _ruleName = "formal_year_four_digits"; + + assertAST("1999", "(YEAR_OF 1999)"); + assertAST("0000", "(YEAR_OF 0000)"); + assertAST("2010", "(YEAR_OF 2010)"); + //assertAST("80" FAIL + //assertAST("03" FAIL + //assertAST("037" FAIL + //assertAST("0" FAIL + //assertAST("03700" FAIL + } + + @Test + public void formal_date_separator() throws Exception { + _ruleName = "formal_date_separator"; + + assertAST("-", "-"); + assertAST("/", "/"); + } + + @Test + public void relative_date() throws Exception { + _ruleName = "relative_date"; + + assertAST("yesterday", "(RELATIVE_DATE (SEEK < by_day 1 day))"); + assertAST("tomorrow", "(RELATIVE_DATE (SEEK > by_day 1 day))"); + assertAST("in 3 days", "(RELATIVE_DATE (SEEK > by_day 3 day))"); + assertAST("3 days ago", "(RELATIVE_DATE (SEEK < by_day 3 day))"); + assertAST("in 3 weeks", "(RELATIVE_DATE (SEEK > by_day 3 week))"); + assertAST("four weeks ago", "(RELATIVE_DATE (SEEK < by_day 4 week))"); + assertAST("in 3 months", "(RELATIVE_DATE (SEEK > by_day 3 month))"); + assertAST("three months ago", "(RELATIVE_DATE (SEEK < by_day 3 month))"); + assertAST("in 3 years", "(RELATIVE_DATE (SEEK > by_day 3 year))"); + assertAST("seven years ago", "(RELATIVE_DATE (SEEK < by_day 7 year))"); + assertAST("60 years ago", "(RELATIVE_DATE (SEEK < by_day 60 year))"); + assertAST("32 days ago", "(RELATIVE_DATE (SEEK < by_day 32 day))"); + assertAST("next monday", "(RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 2)))"); + assertAST("next mon", "(RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 2)))"); + assertAST("4 mondays from now", "(RELATIVE_DATE (SEEK > by_day 4 (DAY_OF_WEEK 2)))"); + assertAST("4 mondays from today", "(RELATIVE_DATE (SEEK > by_day 4 (DAY_OF_WEEK 2)))"); + assertAST("next weekend", "(RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 7)))"); + assertAST("six mondays ago", "(RELATIVE_DATE (SEEK < by_day 6 (DAY_OF_WEEK 2)))"); + assertAST("last monday", "(RELATIVE_DATE (SEEK < by_week 1 (DAY_OF_WEEK 2)))"); + assertAST("last mon", "(RELATIVE_DATE (SEEK < by_week 1 (DAY_OF_WEEK 2)))"); + assertAST("this past mon", "(RELATIVE_DATE (SEEK < by_day 1 (DAY_OF_WEEK 2)))"); + assertAST("this coming mon", "(RELATIVE_DATE (SEEK > by_day 1 (DAY_OF_WEEK 2)))"); + assertAST("this upcoming mon", "(RELATIVE_DATE (SEEK > by_day 1 (DAY_OF_WEEK 2)))"); + assertAST("next thurs", "(RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 5)))"); + assertAST("next month", "(RELATIVE_DATE (SEEK > by_week 1 month))"); + assertAST("last month", "(RELATIVE_DATE (SEEK < by_week 1 month))"); + assertAST("next week", "(RELATIVE_DATE (SEEK > by_week 1 week))"); + assertAST("last week", "(RELATIVE_DATE (SEEK < by_week 1 week))"); + assertAST("next year", "(RELATIVE_DATE (SEEK > by_week 1 year))"); + assertAST("last year", "(RELATIVE_DATE (SEEK < by_week 1 year))"); + } + + @Test + public void explicit_relative_date() throws Exception { + _ruleName = "explicit_relative_date"; + + assertAST("monday of last week", + "(RELATIVE_DATE (SEEK < by_week 1 week) (EXPLICIT_SEEK (DAY_OF_WEEK 2)))"); + + assertAST("tuesday of next week", + "(RELATIVE_DATE (SEEK > by_week 1 week) (EXPLICIT_SEEK (DAY_OF_WEEK 3)))"); + + assertAST("the monday of 2 weeks ago", + "(RELATIVE_DATE (SEEK < by_day 2 week) (EXPLICIT_SEEK (DAY_OF_WEEK 2)))"); + + assertAST("tuesday of 3 weeks from now", + "(RELATIVE_DATE (SEEK > by_day 3 week) (EXPLICIT_SEEK (DAY_OF_WEEK 3)))"); + + assertAST("monday of 3 weeks from now", + "(RELATIVE_DATE (SEEK > by_day 3 week) (EXPLICIT_SEEK (DAY_OF_WEEK 2)))"); + + assertAST("1st of three months ago", + "(RELATIVE_DATE (SEEK < by_day 3 month) (EXPLICIT_SEEK (DAY_OF_MONTH 1)))"); + + assertAST("10th of next month", + "(RELATIVE_DATE (SEEK > by_week 1 month) (EXPLICIT_SEEK (DAY_OF_MONTH 10)))"); + + assertAST("28th of last month", + "(RELATIVE_DATE (SEEK < by_week 1 month) (EXPLICIT_SEEK (DAY_OF_MONTH 28)))"); + + assertAST("10th of next october", + "(RELATIVE_DATE (SEEK > by_week 1 (MONTH_OF_YEAR 10)) (EXPLICIT_SEEK (DAY_OF_MONTH 10)))"); + + assertAST("the 30th of this month", + "(RELATIVE_DATE (SEEK > by_day 0 month) (EXPLICIT_SEEK (DAY_OF_MONTH 30)))"); + + assertAST("10th of the month after next", + "(RELATIVE_DATE (SEEK > by_day 2 month) (EXPLICIT_SEEK (DAY_OF_MONTH 10)))"); + + assertAST("the last thursday in november", + "(RELATIVE_DATE (SEEK > by_day 0 (MONTH_OF_YEAR 11)) (EXPLICIT_SEEK 5 (DAY_OF_WEEK 5)))"); + + assertAST("the last thursday in november 1999", + "(RELATIVE_DATE (SEEK > by_day 0 (MONTH_OF_YEAR 11)) (EXPLICIT_SEEK (YEAR_OF 1999)) (EXPLICIT_SEEK 5 (DAY_OF_WEEK 5)))"); + + assertAST("3rd wed in next month", + "(RELATIVE_DATE (SEEK > by_week 1 month) (EXPLICIT_SEEK 3 (DAY_OF_WEEK 4)))"); + + assertAST("the last sunday in november", + "(RELATIVE_DATE (SEEK > by_day 0 (MONTH_OF_YEAR 11)) (EXPLICIT_SEEK 5 (DAY_OF_WEEK 1)))"); + + assertAST("the first wed. in january", + "(RELATIVE_DATE (SEEK > by_day 0 (MONTH_OF_YEAR 1)) (EXPLICIT_SEEK 1 (DAY_OF_WEEK 4)))"); + + assertAST("the last day of february 1999", + "(RELATIVE_DATE (SEEK > by_day 0 (MONTH_OF_YEAR 2)) (EXPLICIT_SEEK (DAY_OF_MONTH 31)) (EXPLICIT_SEEK (YEAR_OF 1999)))"); + + assertAST("the first wed. in january in the year 2004", + "(RELATIVE_DATE (SEEK > by_day 0 (MONTH_OF_YEAR 1)) (EXPLICIT_SEEK (YEAR_OF 2004)) (EXPLICIT_SEEK 1 (DAY_OF_WEEK 4)))"); + + assertAST("last monday of last month", + "(RELATIVE_DATE (SEEK < by_week 1 month) (EXPLICIT_SEEK 5 (DAY_OF_WEEK 2)))"); + + assertAST("the last sunday of next nov", + "(RELATIVE_DATE (SEEK > by_week 1 (MONTH_OF_YEAR 11)) (EXPLICIT_SEEK 5 (DAY_OF_WEEK 1)))"); + + assertAST("the 3rd sunday of 2 novembers from now", + "(RELATIVE_DATE (SEEK > by_day 2 (MONTH_OF_YEAR 11)) (EXPLICIT_SEEK 3 (DAY_OF_WEEK 1)))"); + + assertAST("the last monday in 2 novembers ago", + "(RELATIVE_DATE (SEEK < by_day 2 (MONTH_OF_YEAR 11)) (EXPLICIT_SEEK 5 (DAY_OF_WEEK 2)))"); + + assertAST("the beginning of next week", + "(RELATIVE_DATE (SEEK > by_week 1 week) (EXPLICIT_SEEK (DAY_OF_WEEK 2)))"); + + assertAST("the end of next week", + "(RELATIVE_DATE (SEEK > by_week 1 week) (EXPLICIT_SEEK (DAY_OF_WEEK 6)))"); + + assertAST("the end of this week", + "(RELATIVE_DATE (SEEK > by_day 0 week) (EXPLICIT_SEEK (DAY_OF_WEEK 6)))"); + + assertAST("the start of this week", + "(RELATIVE_DATE (SEEK > by_day 0 week) (EXPLICIT_SEEK (DAY_OF_WEEK 2)))"); + + assertAST("start of 3 weeks from now", + "(RELATIVE_DATE (SEEK > by_day 3 week) (EXPLICIT_SEEK (DAY_OF_WEEK 2)))"); + + assertAST("the end of 3 weeks ago", + "(RELATIVE_DATE (SEEK < by_day 3 week) (EXPLICIT_SEEK (DAY_OF_WEEK 6)))"); + + assertAST("the first day of this week", + "(RELATIVE_DATE (SEEK > by_day 0 week) (EXPLICIT_SEEK (DAY_OF_WEEK 2)))"); + + assertAST("the last day of next week", + "(RELATIVE_DATE (SEEK > by_week 1 week) (EXPLICIT_SEEK (DAY_OF_WEEK 6)))"); + + assertAST("first day of next week", + "(RELATIVE_DATE (SEEK > by_week 1 week) (EXPLICIT_SEEK (DAY_OF_WEEK 2)))"); + + assertAST("last day of last week", + "(RELATIVE_DATE (SEEK < by_week 1 week) (EXPLICIT_SEEK (DAY_OF_WEEK 6)))"); + + assertAST("start of 3 months from now", + "(RELATIVE_DATE (SEEK > by_day 3 month) (EXPLICIT_SEEK (DAY_OF_MONTH 1)))"); + + assertAST("beginning of next month", + "(RELATIVE_DATE (SEEK > by_week 1 month) (EXPLICIT_SEEK (DAY_OF_MONTH 1)))"); + + assertAST("end of next month", + "(RELATIVE_DATE (SEEK > by_week 1 month) (EXPLICIT_SEEK (DAY_OF_MONTH 31)))"); + + assertAST("last day of next month", + "(RELATIVE_DATE (SEEK > by_week 1 month) (EXPLICIT_SEEK (DAY_OF_MONTH 31)))"); + + assertAST("first day of 3 months from now", + "(RELATIVE_DATE (SEEK > by_day 3 month) (EXPLICIT_SEEK (DAY_OF_MONTH 1)))"); + + assertAST("end of next october", + "(RELATIVE_DATE (SEEK > by_week 1 (MONTH_OF_YEAR 10)) (EXPLICIT_SEEK (DAY_OF_MONTH 31)))"); + + assertAST("first day of feb", + "(RELATIVE_DATE (SEEK > by_day 0 (MONTH_OF_YEAR 2)) (EXPLICIT_SEEK (DAY_OF_MONTH 1)))"); + + assertAST("last day of three februarys from now", + "(RELATIVE_DATE (SEEK > by_day 3 (MONTH_OF_YEAR 2)) (EXPLICIT_SEEK (DAY_OF_MONTH 31)))"); + + assertAST("in the end of next week", + "(RELATIVE_DATE (SEEK > by_week 1 week) (EXPLICIT_SEEK (DAY_OF_WEEK 6)))"); + + assertAST("at the end of last week", + "(RELATIVE_DATE (SEEK < by_week 1 week) (EXPLICIT_SEEK (DAY_OF_WEEK 6)))"); + + assertAST("at the end of 2 weeks", + "(RELATIVE_DATE (SEEK > by_day 2 week) (EXPLICIT_SEEK (DAY_OF_WEEK 6)))"); + + assertAST("in the start of june", + "(RELATIVE_DATE (SEEK > by_day 0 (MONTH_OF_YEAR 6)) (EXPLICIT_SEEK (DAY_OF_MONTH 1)))"); + + assertAST("at the end of next week", + "(RELATIVE_DATE (SEEK > by_week 1 week) (EXPLICIT_SEEK (DAY_OF_WEEK 6)))"); + + assertAST("at the end of last month", + "(RELATIVE_DATE (SEEK < by_week 1 month) (EXPLICIT_SEEK (DAY_OF_MONTH 31)))"); + + assertAST("the second day of april", + "(RELATIVE_DATE (SEEK > by_day 0 (MONTH_OF_YEAR 4)) (EXPLICIT_SEEK (DAY_OF_MONTH 2)))"); + + assertAST("the thirtieth day of next april", + "(RELATIVE_DATE (SEEK > by_week 1 (MONTH_OF_YEAR 4)) (EXPLICIT_SEEK (DAY_OF_MONTH 30)))"); + } + + @Test + public void relative_occurrence_index() throws Exception { + _ruleName = "relative_occurrence_index"; + + assertAST("1", "1"); + assertAST("2", "2"); + assertAST("3", "3"); + assertAST("4", "4"); + assertAST("5", "5"); + assertAST("first", "1"); + assertAST("second", "2"); + assertAST("third", "3"); + assertAST("fourth", "4"); + assertAST("fifth", "5"); + assertAST("last", "5"); + //assertAST("sixth" FAIL); + //assertAST("6" FAIL); + } + + @Test + public void relative_target() throws Exception { + _ruleName = "relative_target"; + + assertAST("sunday" , "(DAY_OF_WEEK 1)"); + assertAST("sundays" , "(DAY_OF_WEEK 1)"); + assertAST("sun" , "(DAY_OF_WEEK 1)"); + assertAST("monday" , "(DAY_OF_WEEK 2)"); + assertAST("mondays" , "(DAY_OF_WEEK 2)"); + assertAST("mon" , "(DAY_OF_WEEK 2)"); + assertAST("tuesday" , "(DAY_OF_WEEK 3)"); + assertAST("tuesdays" , "(DAY_OF_WEEK 3)"); + assertAST("tues" , "(DAY_OF_WEEK 3)"); + assertAST("tue" , "(DAY_OF_WEEK 3)"); + assertAST("wednesday" , "(DAY_OF_WEEK 4)"); + assertAST("wednesdays", "(DAY_OF_WEEK 4)"); + assertAST("wed" , "(DAY_OF_WEEK 4)"); + assertAST("thursday" , "(DAY_OF_WEEK 5)"); + assertAST("thursdays" , "(DAY_OF_WEEK 5)"); + assertAST("thur" , "(DAY_OF_WEEK 5)"); + assertAST("thu" , "(DAY_OF_WEEK 5)"); + assertAST("friday" , "(DAY_OF_WEEK 6)"); + assertAST("fridays" , "(DAY_OF_WEEK 6)"); + assertAST("fri" , "(DAY_OF_WEEK 6)"); + assertAST("saturday" , "(DAY_OF_WEEK 7)"); + assertAST("saturdays" , "(DAY_OF_WEEK 7)"); + assertAST("sat" , "(DAY_OF_WEEK 7)"); + assertAST("day", "day"); + assertAST("days", "day"); + assertAST("week", "week"); + assertAST("weeks", "week"); + assertAST("month", "month"); + assertAST("months", "month"); + assertAST("year", "year"); + assertAST("years", "year"); + } + + @Test + public void relaxed_day_of_month() throws Exception { + _ruleName = "relaxed_day_of_month"; + + assertAST("three", "(DAY_OF_MONTH 3)"); + assertAST("third", "(DAY_OF_MONTH 3)"); + assertAST("3rd", "(DAY_OF_MONTH 3)"); + assertAST("3", "(DAY_OF_MONTH 3)"); + assertAST("03", "(DAY_OF_MONTH 03)"); + assertAST("21", "(DAY_OF_MONTH 21)"); + assertAST("thirty one", "(DAY_OF_MONTH 31)"); + assertAST("thirty-one", "(DAY_OF_MONTH 31)"); + assertAST("thirty first", "(DAY_OF_MONTH 31)"); + assertAST("thirty-first", "(DAY_OF_MONTH 31)"); + assertAST("31st", "(DAY_OF_MONTH 31)"); + //assertAST("32" FAIL + } + + @Test + public void relaxed_year() throws Exception { + _ruleName = "relaxed_year"; + + assertAST("'69", "(YEAR_OF 69)"); + assertAST("79", "(YEAR_OF 79)"); + assertAST("2079", "(YEAR_OF 2079)"); + assertAST("'80", "(YEAR_OF 80)"); + assertAST("1979", "(YEAR_OF 1979)"); + assertAST("2004", "(YEAR_OF 2004)"); + //assertAST("999" FAIL"); + //assertAST("999" FAIL"); + } + + @Test + public void relaxed_year_prefix() throws Exception { + _ruleName = "relaxed_year_prefix"; + + //assertAST(", in the year ", ", in the year"); + //assertAST(" in the year ", " in the year"); + //assertAST("in the year ", "in the year"); + //assertAST("in the yesr ", "in the year"); + } + + @Test + public void implicit_prefix() throws Exception { + _ruleName = "implicit_prefix"; + + assertAST("this", "> by_day 0"); + } + + @Test + public void relative_date_prefix() throws Exception { + _ruleName = "relative_date_prefix"; + + assertAST("this last" , "< by_week 1"); + assertAST("last" , "< by_week 1"); + assertAST("this past" , "< by_day 1"); + assertAST("past" , "< by_day 1"); + assertAST("this next" , "> by_week 1"); + assertAST("next" , "> by_week 1"); + assertAST("this coming" , "> by_day 1"); + assertAST("coming" , "> by_day 1"); + assertAST("this upcoming", "> by_day 1"); + assertAST("upcoming" , "> by_day 1"); + assertAST("3" , "> by_day 3"); + assertAST("twenty-eight" , "> by_day 28"); + //assertAST("in 3" , "> by_day 3"); + //assertAST("in twenty" , "> by_day 20"); + } + + @Test + public void relative_date_suffix() throws Exception { + _ruleName = "relative_date_suffix"; + + assertAST("from now", "> by_day"); + assertAST("ago", "< by_day"); + } + + @Test + public void relative_date_span() throws Exception { + _ruleName = "relative_date_span"; + + assertAST("day", "day"); + assertAST("days", "day"); + assertAST("week", "week"); + assertAST("weeks", "week"); + assertAST("month", "month"); + assertAST("months", "month"); + assertAST("year", "year"); + assertAST("years", "year"); + } + + @Test + public void relaxed_month() throws Exception { + _ruleName = "relaxed_month"; + + assertAST("january", "(MONTH_OF_YEAR 1)"); + assertAST("jan", "(MONTH_OF_YEAR 1)"); + assertAST("february", "(MONTH_OF_YEAR 2)"); + assertAST("feb", "(MONTH_OF_YEAR 2)"); + assertAST("march", "(MONTH_OF_YEAR 3)"); + assertAST("mar", "(MONTH_OF_YEAR 3)"); + assertAST("april", "(MONTH_OF_YEAR 4)"); + assertAST("apr", "(MONTH_OF_YEAR 4)"); + assertAST("may", "(MONTH_OF_YEAR 5)"); + assertAST("june", "(MONTH_OF_YEAR 6)"); + assertAST("jun", "(MONTH_OF_YEAR 6)"); + assertAST("july", "(MONTH_OF_YEAR 7)"); + assertAST("jul", "(MONTH_OF_YEAR 7)"); + assertAST("august", "(MONTH_OF_YEAR 8)"); + assertAST("aug", "(MONTH_OF_YEAR 8)"); + assertAST("september", "(MONTH_OF_YEAR 9)"); + assertAST("sep", "(MONTH_OF_YEAR 9)"); + assertAST("sept", "(MONTH_OF_YEAR 9)"); + assertAST("october", "(MONTH_OF_YEAR 10)"); + assertAST("oct", "(MONTH_OF_YEAR 10)"); + assertAST("november", "(MONTH_OF_YEAR 11)"); + assertAST("nov", "(MONTH_OF_YEAR 11)"); + assertAST("december", "(MONTH_OF_YEAR 12)"); + assertAST("dec", "(MONTH_OF_YEAR 12)"); + assertAST("jan.", "(MONTH_OF_YEAR 1)"); + assertAST("feb.", "(MONTH_OF_YEAR 2)"); + assertAST("mar.", "(MONTH_OF_YEAR 3)"); + assertAST("apr.", "(MONTH_OF_YEAR 4)"); + assertAST("jun.", "(MONTH_OF_YEAR 6)"); + assertAST("jul.", "(MONTH_OF_YEAR 7)"); + assertAST("aug.", "(MONTH_OF_YEAR 8)"); + assertAST("sep.", "(MONTH_OF_YEAR 9)"); + assertAST("sept.", "(MONTH_OF_YEAR 9)"); + assertAST("oct.", "(MONTH_OF_YEAR 10)"); + assertAST("nov.", "(MONTH_OF_YEAR 11)"); + assertAST("dec.", "(MONTH_OF_YEAR 12)"); + } + + @Test + public void day_of_week() throws Exception { + _ruleName = "day_of_week"; + + assertAST("sunday" , "(DAY_OF_WEEK 1)"); + assertAST("sundays" , "(DAY_OF_WEEK 1)"); + assertAST("sun" , "(DAY_OF_WEEK 1)"); + assertAST("sun." , "(DAY_OF_WEEK 1)"); + assertAST("monday" , "(DAY_OF_WEEK 2)"); + assertAST("mondays" , "(DAY_OF_WEEK 2)"); + assertAST("mon" , "(DAY_OF_WEEK 2)"); + assertAST("mon." , "(DAY_OF_WEEK 2)"); + assertAST("tuesday" , "(DAY_OF_WEEK 3)"); + assertAST("tuesdays" , "(DAY_OF_WEEK 3)"); + assertAST("tues" , "(DAY_OF_WEEK 3)"); + assertAST("tues." , "(DAY_OF_WEEK 3)"); + assertAST("tue" , "(DAY_OF_WEEK 3)"); + assertAST("tue." , "(DAY_OF_WEEK 3)"); + assertAST("wednesday" , "(DAY_OF_WEEK 4)"); + assertAST("wednesdays", "(DAY_OF_WEEK 4)"); + assertAST("wed" , "(DAY_OF_WEEK 4)"); + assertAST("wed." , "(DAY_OF_WEEK 4)"); + assertAST("thursday" , "(DAY_OF_WEEK 5)"); + assertAST("thursdays" , "(DAY_OF_WEEK 5)"); + assertAST("thur" , "(DAY_OF_WEEK 5)"); + assertAST("thur." , "(DAY_OF_WEEK 5)"); + assertAST("thu" , "(DAY_OF_WEEK 5)"); + assertAST("thu." , "(DAY_OF_WEEK 5)"); + assertAST("friday" , "(DAY_OF_WEEK 6)"); + assertAST("fridays" , "(DAY_OF_WEEK 6)"); + assertAST("fri" , "(DAY_OF_WEEK 6)"); + assertAST("fri." , "(DAY_OF_WEEK 6)"); + assertAST("saturday" , "(DAY_OF_WEEK 7)"); + assertAST("saturdays" , "(DAY_OF_WEEK 7)"); + assertAST("sat" , "(DAY_OF_WEEK 7)"); + assertAST("sat." , "(DAY_OF_WEEK 7)"); + } + + @Test + public void named_relative_date() throws Exception { + _ruleName = "named_relative_date"; + + assertAST("today", "(RELATIVE_DATE (SEEK > by_day 0 day))"); + assertAST("now", "(RELATIVE_DATE (SEEK > by_day 0 day))"); + assertAST("tomorow" , "(RELATIVE_DATE (SEEK > by_day 1 day))"); + assertAST("tomorrow" , "(RELATIVE_DATE (SEEK > by_day 1 day))"); + assertAST("tommorow" , "(RELATIVE_DATE (SEEK > by_day 1 day))"); + assertAST("tommorrow", "(RELATIVE_DATE (SEEK > by_day 1 day))"); + assertAST("yesterday", "(RELATIVE_DATE (SEEK < by_day 1 day))"); + } + + @Test + public void date_time_should_parse_YYYY_as_explicit_date() throws Exception { + _ruleName = "date_time"; + + assertAST("2014", "(DATE_TIME (EXPLICIT_DATE (YEAR_OF 2014)))"); + } + + @Test + public void date_should_parse_YYYY_as_explicit_date() throws Exception { + _ruleName = "date"; + + assertAST("2014", "(EXPLICIT_DATE (YEAR_OF 2014))"); + } + +} diff --git a/src/test/java/com/joestelmach/natty/grammar/DateTimeGrammarTest.java b/src/test/java/com/joestelmach/natty/grammar/DateTimeGrammarTest.java new file mode 100644 index 00000000..49d09f97 --- /dev/null +++ b/src/test/java/com/joestelmach/natty/grammar/DateTimeGrammarTest.java @@ -0,0 +1,86 @@ +package com.joestelmach.natty.grammar; + +import org.junit.Test; + +public class DateTimeGrammarTest extends AbstractGrammarTest { + + @Test + public void date_time_alternative() throws Exception { + _ruleName = "date_time_alternative"; + + assertAST("this wed. or next at 5pm", "(DATE_TIME_ALTERNATIVE (DATE_TIME (RELATIVE_DATE (SEEK > by_day 0 (DAY_OF_WEEK 4))) (EXPLICIT_TIME (HOURS_OF_DAY 5) (MINUTES_OF_HOUR 0) pm)) (DATE_TIME (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 4))) (EXPLICIT_TIME (HOURS_OF_DAY 5) (MINUTES_OF_HOUR 0) pm)))"); + assertAST("feb 28th or 2 days after", "(DATE_TIME_ALTERNATIVE (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 2) (DAY_OF_MONTH 28))) (DATE_TIME (RELATIVE_DATE (SEEK > by_day 2 (EXPLICIT_DATE (MONTH_OF_YEAR 2) (DAY_OF_MONTH 28))))))"); + assertAST("january fourth or the friday after", "(DATE_TIME_ALTERNATIVE (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 1) (DAY_OF_MONTH 4))) (DATE_TIME (RELATIVE_DATE (SEEK > by_day 1 (DAY_OF_WEEK 6) (EXPLICIT_DATE (MONTH_OF_YEAR 1) (DAY_OF_MONTH 4))))))"); + assertAST("10/10/2008 or 10/12/2008", "(DATE_TIME_ALTERNATIVE (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 10) (YEAR_OF 2008))) (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 12) (YEAR_OF 2008))))"); + assertAST("next wed or thursday", "(DATE_TIME_ALTERNATIVE (DATE_TIME (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 4)))) (DATE_TIME (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 5)))))"); + assertAST("next wed, thurs, fri", "(DATE_TIME_ALTERNATIVE (DATE_TIME (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 4)))) (DATE_TIME (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 5)))) (DATE_TIME (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 6)))))"); + assertAST("next wed, thurs, or fri at 6pm", "(DATE_TIME_ALTERNATIVE (DATE_TIME (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 4))) (EXPLICIT_TIME (HOURS_OF_DAY 6) (MINUTES_OF_HOUR 0) pm)) (DATE_TIME (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 5))) (EXPLICIT_TIME (HOURS_OF_DAY 6) (MINUTES_OF_HOUR 0) pm)) (DATE_TIME (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 6))) (EXPLICIT_TIME (HOURS_OF_DAY 6) (MINUTES_OF_HOUR 0) pm)))"); + assertAST("10/10 or 12/30 or 10/15 at 5pm", "(DATE_TIME_ALTERNATIVE (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 10))) (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 12) (DAY_OF_MONTH 30))) (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 15)) (EXPLICIT_TIME (HOURS_OF_DAY 5) (MINUTES_OF_HOUR 0) pm)))"); + assertAST("monday to friday", "(DATE_TIME_ALTERNATIVE (DATE_TIME (RELATIVE_DATE (SEEK > by_day 0 (DAY_OF_WEEK 2)))) (DATE_TIME (RELATIVE_DATE (SEEK > by_day 0 (DAY_OF_WEEK 6)))))"); + assertAST("1999-12-31 to tomorrow", "(DATE_TIME_ALTERNATIVE (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 12) (DAY_OF_MONTH 31) (YEAR_OF 1999))) (DATE_TIME (RELATIVE_DATE (SEEK > by_day 1 day))))"); + assertAST("now to 2010-01-01", "(DATE_TIME_ALTERNATIVE (DATE_TIME (RELATIVE_DATE (SEEK > by_day 0 day))) (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 01) (DAY_OF_MONTH 01) (YEAR_OF 2010))))"); + assertAST("2009-03-10 9:00 to 11:00", "(DATE_TIME_ALTERNATIVE (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 03) (DAY_OF_MONTH 10) (YEAR_OF 2009)) (EXPLICIT_TIME (HOURS_OF_DAY 9) (MINUTES_OF_HOUR 00))) (DATE_TIME (EXPLICIT_TIME (HOURS_OF_DAY 11) (MINUTES_OF_HOUR 00))))"); + assertAST("26 oct 10:00 am to 11:00 am", "(DATE_TIME_ALTERNATIVE (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 26)) (EXPLICIT_TIME (HOURS_OF_DAY 10) (MINUTES_OF_HOUR 00) am)) (DATE_TIME (EXPLICIT_TIME (HOURS_OF_DAY 11) (MINUTES_OF_HOUR 00) am)))"); + assertAST("jan 1 to 2", "(DATE_TIME_ALTERNATIVE (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 1) (DAY_OF_MONTH 1))) (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 1) (DAY_OF_MONTH 2))))"); + assertAST("16:00 nov 6 to 17:00", "(DATE_TIME_ALTERNATIVE (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 11) (DAY_OF_MONTH 6)) (EXPLICIT_TIME (HOURS_OF_DAY 16) (MINUTES_OF_HOUR 00))) (DATE_TIME (EXPLICIT_TIME (HOURS_OF_DAY 17) (MINUTES_OF_HOUR 00))))"); + assertAST("may 2nd to 5th", "(DATE_TIME_ALTERNATIVE (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 5) (DAY_OF_MONTH 2))) (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 5) (DAY_OF_MONTH 5))))"); + assertAST("6am dec 5 to 7am", "(DATE_TIME_ALTERNATIVE (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 12) (DAY_OF_MONTH 5)) (EXPLICIT_TIME (HOURS_OF_DAY 6) (MINUTES_OF_HOUR 0) am)) (DATE_TIME (EXPLICIT_TIME (HOURS_OF_DAY 7) (MINUTES_OF_HOUR 0) am)))"); + assertAST("1/3 to 2/3", "(DATE_TIME_ALTERNATIVE (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 1) (DAY_OF_MONTH 3))) (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 2) (DAY_OF_MONTH 3))))"); + assertAST("2/3 to in 1 week", "(DATE_TIME_ALTERNATIVE (DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 2) (DAY_OF_MONTH 3))) (DATE_TIME (RELATIVE_DATE (SEEK > by_day 1 week))))"); + assertAST("first day of may to last day of may", "(DATE_TIME_ALTERNATIVE (DATE_TIME (RELATIVE_DATE (SEEK > by_day 0 (MONTH_OF_YEAR 5)) (EXPLICIT_SEEK (DAY_OF_MONTH 1)))) (DATE_TIME (RELATIVE_DATE (SEEK > by_day 0 (MONTH_OF_YEAR 5)) (EXPLICIT_SEEK (DAY_OF_MONTH 31)))))"); + } + + + @Test + public void date_time() throws Exception { + _ruleName = "date_time"; + + assertAST("seven years ago at 3pm", "(DATE_TIME (RELATIVE_DATE (SEEK < by_day 7 year)) (EXPLICIT_TIME (HOURS_OF_DAY 3) (MINUTES_OF_HOUR 0) pm))"); + assertAST("1st oct in the year '89 1300 hours", "(DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 89)) (EXPLICIT_TIME (HOURS_OF_DAY 13) (MINUTES_OF_HOUR 00)))"); + assertAST("1st oct in the year '89 at 1300 hours", "(DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 89)) (EXPLICIT_TIME (HOURS_OF_DAY 13) (MINUTES_OF_HOUR 00)))"); + assertAST("1st oct in the year '89, 13:00", "(DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 89)) (EXPLICIT_TIME (HOURS_OF_DAY 13) (MINUTES_OF_HOUR 00)))"); + assertAST("1st oct in the year '89,13:00", "(DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 89)) (EXPLICIT_TIME (HOURS_OF_DAY 13) (MINUTES_OF_HOUR 00)))"); + assertAST("1st oct in the year '89, at 13:00", "(DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 89)) (EXPLICIT_TIME (HOURS_OF_DAY 13) (MINUTES_OF_HOUR 00)))"); + assertAST("3am on oct 1st 2010", "(DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 2010)) (EXPLICIT_TIME (HOURS_OF_DAY 3) (MINUTES_OF_HOUR 0) am))"); + assertAST("3am, october first 2010", "(DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 2010)) (EXPLICIT_TIME (HOURS_OF_DAY 3) (MINUTES_OF_HOUR 0) am))"); + assertAST("3am,october first 2010", "(DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 2010)) (EXPLICIT_TIME (HOURS_OF_DAY 3) (MINUTES_OF_HOUR 0) am))"); + assertAST("3am, on october first 2010", "(DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 2010)) (EXPLICIT_TIME (HOURS_OF_DAY 3) (MINUTES_OF_HOUR 0) am))"); + assertAST("3am october first 2010", "(DATE_TIME (EXPLICIT_DATE (MONTH_OF_YEAR 10) (DAY_OF_MONTH 1) (YEAR_OF 2010)) (EXPLICIT_TIME (HOURS_OF_DAY 3) (MINUTES_OF_HOUR 0) am))"); + assertAST("next wed. at 5pm", "(DATE_TIME (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 4))) (EXPLICIT_TIME (HOURS_OF_DAY 5) (MINUTES_OF_HOUR 0) pm))"); + assertAST("3 days after next wed", "(DATE_TIME (RELATIVE_DATE (SEEK > by_day 3 (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 4))))))"); + assertAST("the sunday after next wed", "(DATE_TIME (RELATIVE_DATE (SEEK > by_day 1 (DAY_OF_WEEK 1) (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 4))))))"); + assertAST("two days after today", "(DATE_TIME (RELATIVE_DATE (SEEK > by_day 2 day)))"); + assertAST("two days from today", "(DATE_TIME (RELATIVE_DATE (SEEK > by_day 2 day)))"); + assertAST("3 sundays after next wed", "(DATE_TIME (RELATIVE_DATE (SEEK > by_day 3 (DAY_OF_WEEK 1) (RELATIVE_DATE (SEEK > by_week 1 (DAY_OF_WEEK 4))))))"); + assertAST("the day after next", "(DATE_TIME (RELATIVE_DATE (SEEK > by_day 2 day)))"); + assertAST("the week after next", "(DATE_TIME (RELATIVE_DATE (SEEK > by_day 2 week)))"); + assertAST("the month after next", "(DATE_TIME (RELATIVE_DATE (SEEK > by_day 2 month)))"); + assertAST("the year after next", "(DATE_TIME (RELATIVE_DATE (SEEK > by_day 2 year)))"); + assertAST("wed of the week after next", "(DATE_TIME (RELATIVE_DATE (SEEK > by_day 2 week) (EXPLICIT_SEEK (DAY_OF_WEEK 4))))"); + assertAST("the 28th of the month after next", "(DATE_TIME (RELATIVE_DATE (SEEK > by_day 2 month) (EXPLICIT_SEEK (DAY_OF_MONTH 28))))"); + assertAST("6 in the morning", "(DATE_TIME (EXPLICIT_TIME (HOURS_OF_DAY 6) (MINUTES_OF_HOUR 0) am))"); + assertAST("4 in the afternoon", "(DATE_TIME (EXPLICIT_TIME (HOURS_OF_DAY 4) (MINUTES_OF_HOUR 0) pm))"); + assertAST("monday 6 in the morning", "(DATE_TIME (RELATIVE_DATE (SEEK > by_day 0 (DAY_OF_WEEK 2))) (EXPLICIT_TIME (HOURS_OF_DAY 6) (MINUTES_OF_HOUR 0) am))"); + assertAST("monday 4 in the afternoon", "(DATE_TIME (RELATIVE_DATE (SEEK > by_day 0 (DAY_OF_WEEK 2))) (EXPLICIT_TIME (HOURS_OF_DAY 4) (MINUTES_OF_HOUR 0) pm))"); + assertAST("monday 9 in the evening", "(DATE_TIME (RELATIVE_DATE (SEEK > by_day 0 (DAY_OF_WEEK 2))) (EXPLICIT_TIME (HOURS_OF_DAY 9) (MINUTES_OF_HOUR 0) pm))"); + assertAST("this morning", "(DATE_TIME (EXPLICIT_TIME (HOURS_OF_DAY 8) (MINUTES_OF_HOUR 0) (SECONDS_OF_MINUTE 0) am))"); + assertAST("this afternoon", "(DATE_TIME (EXPLICIT_TIME (HOURS_OF_DAY 12) (MINUTES_OF_HOUR 0) (SECONDS_OF_MINUTE 0) pm))"); + assertAST("final thursday in april", "(DATE_TIME (RELATIVE_DATE (SEEK > by_day 0 (MONTH_OF_YEAR 4)) (EXPLICIT_SEEK 5 (DAY_OF_WEEK 5))))"); + assertAST("final thurs in sep", "(DATE_TIME (RELATIVE_DATE (SEEK > by_day 0 (MONTH_OF_YEAR 9)) (EXPLICIT_SEEK 5 (DAY_OF_WEEK 5))))"); + assertAST("tomorrow @ noon", "(DATE_TIME (RELATIVE_DATE (SEEK > by_day 1 day)) (EXPLICIT_TIME (HOURS_OF_DAY 12) (MINUTES_OF_HOUR 0) (SECONDS_OF_MINUTE 0) pm))"); + assertAST("6 hours ago", "(DATE_TIME (RELATIVE_TIME (SEEK < by_day 6 hour)))"); + assertAST("10 hrs before noon", "(DATE_TIME (RELATIVE_TIME (SEEK < by_day (EXPLICIT_SEEK (EXPLICIT_TIME (HOURS_OF_DAY 12) (MINUTES_OF_HOUR 0) (SECONDS_OF_MINUTE 0) pm)) 10 hour)))"); + assertAST("10 hr before midnight", "(DATE_TIME (RELATIVE_TIME (SEEK < by_day (EXPLICIT_SEEK (EXPLICIT_TIME (HOURS_OF_DAY 12) (MINUTES_OF_HOUR 0) (SECONDS_OF_MINUTE 0) am)) 10 hour)))"); + assertAST("5 hours after noon", "(DATE_TIME (RELATIVE_TIME (SEEK > by_day (EXPLICIT_SEEK (EXPLICIT_TIME (HOURS_OF_DAY 12) (MINUTES_OF_HOUR 0) (SECONDS_OF_MINUTE 0) pm)) 5 hour)))"); + assertAST("5 hours after midnight", "(DATE_TIME (RELATIVE_TIME (SEEK > by_day (EXPLICIT_SEEK (EXPLICIT_TIME (HOURS_OF_DAY 12) (MINUTES_OF_HOUR 0) (SECONDS_OF_MINUTE 0) am)) 5 hour)))"); + assertAST("in 5 seconds", "(DATE_TIME (RELATIVE_TIME (SEEK > by_day 5 second)))"); + assertAST("in 5 minutes", "(DATE_TIME (RELATIVE_TIME (SEEK > by_day 5 minute)))"); + assertAST("in 5 hours", "(DATE_TIME (RELATIVE_TIME (SEEK > by_day 5 hour)))"); + assertAST("4 secs from now", "(DATE_TIME (RELATIVE_TIME (SEEK > by_day (RELATIVE_DATE (SEEK > by_day 0 day)) 4 second)))"); + assertAST("4 sec from now", "(DATE_TIME (RELATIVE_TIME (SEEK > by_day (RELATIVE_DATE (SEEK > by_day 0 day)) 4 second)))"); + assertAST("4 minutes from now", "(DATE_TIME (RELATIVE_TIME (SEEK > by_day (RELATIVE_DATE (SEEK > by_day 0 day)) 4 minute)))"); + assertAST("4 mins from now", "(DATE_TIME (RELATIVE_TIME (SEEK > by_day (RELATIVE_DATE (SEEK > by_day 0 day)) 4 minute)))"); + assertAST("4 min from now", "(DATE_TIME (RELATIVE_TIME (SEEK > by_day (RELATIVE_DATE (SEEK > by_day 0 day)) 4 minute)))"); + assertAST("4 hours from now", "(DATE_TIME (RELATIVE_TIME (SEEK > by_day (RELATIVE_DATE (SEEK > by_day 0 day)) 4 hour)))"); + } +} diff --git a/src/test/java/com/joestelmach/natty/grammar/HolidayGrammarTest.java b/src/test/java/com/joestelmach/natty/grammar/HolidayGrammarTest.java new file mode 100644 index 00000000..fce221b4 --- /dev/null +++ b/src/test/java/com/joestelmach/natty/grammar/HolidayGrammarTest.java @@ -0,0 +1,132 @@ +package com.joestelmach.natty.grammar; + +import org.junit.Test; + +public class HolidayGrammarTest extends AbstractGrammarTest { + + @Test + public void names() throws Exception { + _ruleName = "holiday_name"; + + assertAST("april fool's day", "APRIL_FOOLS_DAY"); + assertAST("april fools day", "APRIL_FOOLS_DAY"); + assertAST("april fool day", "APRIL_FOOLS_DAY"); + + assertAST("black friday", "BLACK_FRIDAY"); + assertAST("black fri", "BLACK_FRIDAY"); + assertAST("black fri.", "BLACK_FRIDAY"); + + assertAST("christmas", "CHRISTMAS"); + assertAST("christmas day", "CHRISTMAS"); + + assertAST("christmas eve", "CHRISTMAS_EVE"); + assertAST("christmas eve.", "CHRISTMAS_EVE"); + assertAST("christmas evening", "CHRISTMAS_EVE"); + + assertAST("columbus day", "COLUMBUS_DAY"); + + assertAST("earth day", "EARTH_DAY"); + + assertAST("easter", "EASTER"); + assertAST("easter day", "EASTER"); + assertAST("easter sunday", "EASTER"); + + assertAST("father's day", "FATHERS_DAY"); + assertAST("fathers day", "FATHERS_DAY"); + + assertAST("flag day", "FLAG_DAY"); + + assertAST("good friday", "GOOD_FRIDAY"); + assertAST("good fri", "GOOD_FRIDAY"); + assertAST("good fri.", "GOOD_FRIDAY"); + + assertAST("groundhog day", "GROUNDHOG_DAY"); + assertAST("groundhogs day", "GROUNDHOG_DAY"); + assertAST("groundhog's day", "GROUNDHOG_DAY"); + + assertAST("halloween", "HALLOWEEN"); + assertAST("haloween", "HALLOWEEN"); + assertAST("halloween day", "HALLOWEEN"); + + assertAST("independence day", "INDEPENDENCE_DAY"); + + assertAST("kwanzaa", "KWANZAA"); + assertAST("kwanza", "KWANZAA"); + assertAST("kwanzaa day", "KWANZAA"); + + assertAST("labor day", "LABOR_DAY"); + + assertAST("martin luther king day", "MLK_DAY"); + assertAST("martin luther king jr.'s day", "MLK_DAY"); + assertAST("martin luther king jr. day", "MLK_DAY"); + assertAST("martin luther king jr day", "MLK_DAY"); + assertAST("mlk day", "MLK_DAY"); + + assertAST("memorial day", "MEMORIAL_DAY"); + + assertAST("memorial day", "MEMORIAL_DAY"); + + assertAST("mothers day", "MOTHERS_DAY"); + assertAST("mother's day", "MOTHERS_DAY"); + + assertAST("new year's day", "NEW_YEARS_DAY"); + assertAST("new years day", "NEW_YEARS_DAY"); + assertAST("new years", "NEW_YEARS_DAY"); + + assertAST("new year's eve", "NEW_YEARS_EVE"); + assertAST("new years eve", "NEW_YEARS_EVE"); + assertAST("new years eve.", "NEW_YEARS_EVE"); + + assertAST("patriot day", "PATRIOT_DAY"); + + assertAST("president's day", "PRESIDENTS_DAY"); + assertAST("presidents day", "PRESIDENTS_DAY"); + assertAST("president day", "PRESIDENTS_DAY"); + + assertAST("st. patricks day", "ST_PATRICKS_DAY"); + assertAST("st patrick's day", "ST_PATRICKS_DAY"); + assertAST("saint patrick's day", "ST_PATRICKS_DAY"); + assertAST("saint paddy's day", "ST_PATRICKS_DAY"); + assertAST("saint paddys day", "ST_PATRICKS_DAY"); + + assertAST("tax day", "TAX_DAY"); + + assertAST("thanksgiving", "THANKSGIVING"); + assertAST("thanksgiving day", "THANKSGIVING"); + + assertAST("election day", "ELECTION_DAY"); + + assertAST("valentine's day", "VALENTINES_DAY"); + assertAST("valentines day", "VALENTINES_DAY"); + assertAST("valentine day", "VALENTINES_DAY"); + + assertAST("veterans day", "VETERANS_DAY"); + assertAST("veteran's day", "VETERANS_DAY"); + assertAST("veteran day", "VETERANS_DAY"); + } + + @Test + public void statments() throws Exception { + _ruleName = "holiday"; + + assertAST("april fool's day", "(SEEK > by_day 1 APRIL_FOOLS_DAY)"); + + assertAST("next christmas", "(SEEK > by_week 1 CHRISTMAS)"); + assertAST("coming christmas", "(SEEK > by_day 1 CHRISTMAS)"); + assertAST("upcoming christmas", "(SEEK > by_day 1 CHRISTMAS)"); + + assertAST("last halloween", "(SEEK < by_week 1 HALLOWEEN)"); + assertAST("past halloween", "(SEEK < by_day 1 HALLOWEEN)"); + + assertAST("in three christmases", "(SEEK > by_day 3 CHRISTMAS)"); + + assertAST("three christmases from now", "(SEEK > by_day 3 CHRISTMAS)"); + assertAST("3 christmases ago", "(SEEK < by_day 3 CHRISTMAS)"); + assertAST("2 april fool's days ago", "(SEEK < by_day 2 APRIL_FOOLS_DAY)"); + + assertAST("in 10 thanksgivings", "(SEEK > by_day 10 THANKSGIVING)"); + + assertAST("thanksgiving 2011", "(EXPLICIT_SEEK THANKSGIVING (YEAR_OF 2011))"); + assertAST("christmas, '95", "(EXPLICIT_SEEK CHRISTMAS (YEAR_OF 95))"); + } +} diff --git a/src/test/java/com/joestelmach/natty/grammar/NumericGrammarTest.java b/src/test/java/com/joestelmach/natty/grammar/NumericGrammarTest.java new file mode 100644 index 00000000..606dd15b --- /dev/null +++ b/src/test/java/com/joestelmach/natty/grammar/NumericGrammarTest.java @@ -0,0 +1,725 @@ +package com.joestelmach.natty.grammar; + +import org.junit.Test; + +public class NumericGrammarTest extends AbstractGrammarTest { + + @Test + public void int_00_to_23_optional_prefix() throws Exception { + _ruleName = "int_00_to_23_optional_prefix"; + + assertAST("00", "00"); + assertAST("01", "01"); + assertAST("02", "02"); + assertAST("03", "03"); + assertAST("04", "04"); + assertAST("05", "05"); + assertAST("06", "06"); + assertAST("07", "07"); + assertAST("08", "08"); + assertAST("09", "09"); + assertAST("10", "10"); + assertAST("11", "11"); + assertAST("12", "12"); + assertAST("13", "13"); + assertAST("14", "14"); + assertAST("15", "15"); + assertAST("16", "16"); + assertAST("17", "17"); + assertAST("18", "18"); + assertAST("19", "19"); + assertAST("20", "20"); + assertAST("21", "21"); + assertAST("22", "22"); + assertAST("23", "23"); + } + + @Test + public void test_int_00_to_59_mandatory_prefix() throws Exception { + _ruleName = "int_00_to_59_mandatory_prefix"; + + assertAST("00", "00"); + assertAST("01", "01"); + assertAST("02", "02"); + assertAST("03", "03"); + assertAST("04", "04"); + assertAST("05", "05"); + assertAST("06", "06"); + assertAST("07", "07"); + assertAST("08", "08"); + assertAST("09", "09"); + assertAST("10", "10"); + assertAST("11", "11"); + assertAST("12", "12"); + assertAST("13", "13"); + assertAST("14", "14"); + assertAST("15", "15"); + assertAST("16", "16"); + assertAST("17", "17"); + assertAST("18", "18"); + assertAST("19", "19"); + assertAST("20", "20"); + assertAST("21", "21"); + assertAST("22", "22"); + assertAST("23", "23"); + assertAST("24", "24"); + assertAST("25", "25"); + assertAST("26", "26"); + assertAST("27", "27"); + assertAST("28", "28"); + assertAST("29", "29"); + assertAST("30", "30"); + assertAST("31", "31"); + assertAST("32", "32"); + assertAST("33", "33"); + assertAST("34", "34"); + assertAST("35", "35"); + assertAST("36", "36"); + assertAST("37", "37"); + assertAST("38", "38"); + assertAST("39", "39"); + assertAST("40", "40"); + assertAST("41", "41"); + assertAST("42", "42"); + assertAST("43", "43"); + assertAST("44", "44"); + assertAST("45", "45"); + assertAST("46", "46"); + assertAST("47", "47"); + assertAST("48", "48"); + assertAST("49", "49"); + assertAST("50", "50"); + assertAST("51", "51"); + assertAST("52", "52"); + assertAST("53", "53"); + assertAST("54", "54"); + assertAST("55", "55"); + assertAST("56", "56"); + assertAST("57", "57"); + assertAST("58", "58"); + assertAST("59", "59"); + /* + "0" FAIL + "1" FAIL + "2" FAIL + "3" FAIL + "4" FAIL + "5" FAIL + "6" FAIL + "7" FAIL + "8" FAIL + "9" FAIL + "60" FAIL + */ + } + + @Test + public void test_int_00_to_99_mandatory_prefix() throws Exception { + _ruleName = "int_00_to_99_mandatory_prefix"; + + assertAST("00", "00"); + assertAST("01", "01"); + assertAST("02", "02"); + assertAST("03", "03"); + assertAST("04", "04"); + assertAST("05", "05"); + assertAST("06", "06"); + assertAST("07", "07"); + assertAST("08", "08"); + assertAST("09", "09"); + assertAST("10", "10"); + assertAST("11", "11"); + assertAST("12", "12"); + assertAST("13", "13"); + assertAST("14", "14"); + assertAST("15", "15"); + assertAST("16", "16"); + assertAST("17", "17"); + assertAST("18", "18"); + assertAST("19", "19"); + assertAST("20", "20"); + assertAST("21", "21"); + assertAST("22", "22"); + assertAST("23", "23"); + assertAST("24", "24"); + assertAST("25", "25"); + assertAST("26", "26"); + assertAST("27", "27"); + assertAST("28", "28"); + assertAST("29", "29"); + assertAST("30", "30"); + assertAST("31", "31"); + assertAST("32", "32"); + assertAST("33", "33"); + assertAST("34", "34"); + assertAST("35", "35"); + assertAST("36", "36"); + assertAST("37", "37"); + assertAST("38", "38"); + assertAST("39", "39"); + assertAST("40", "40"); + assertAST("41", "41"); + assertAST("42", "42"); + assertAST("43", "43"); + assertAST("44", "44"); + assertAST("45", "45"); + assertAST("46", "46"); + assertAST("47", "47"); + assertAST("48", "48"); + assertAST("49", "49"); + assertAST("50", "50"); + assertAST("51", "51"); + assertAST("52", "52"); + assertAST("53", "53"); + assertAST("54", "54"); + assertAST("55", "55"); + assertAST("56", "56"); + assertAST("57", "57"); + assertAST("58", "58"); + assertAST("59", "59"); + assertAST("60", "60"); + assertAST("61", "61"); + assertAST("62", "62"); + assertAST("63", "63"); + assertAST("64", "64"); + assertAST("65", "65"); + assertAST("66", "66"); + assertAST("67", "67"); + assertAST("68", "68"); + assertAST("69", "69"); + assertAST("70", "70"); + assertAST("71", "71"); + assertAST("72", "72"); + assertAST("73", "73"); + assertAST("74", "74"); + assertAST("75", "75"); + assertAST("76", "76"); + assertAST("77", "77"); + assertAST("78", "78"); + assertAST("79", "79"); + assertAST("80", "80"); + assertAST("81", "81"); + assertAST("82", "82"); + assertAST("83", "83"); + assertAST("84", "84"); + assertAST("85", "85"); + assertAST("86", "86"); + assertAST("87", "87"); + assertAST("88", "88"); + assertAST("89", "89"); + assertAST("90", "90"); + assertAST("91", "91"); + assertAST("92", "92"); + assertAST("93", "93"); + assertAST("94", "94"); + assertAST("95", "95"); + assertAST("96", "96"); + assertAST("97", "97"); + assertAST("98", "98"); + assertAST("99", "99"); + /* + "0" FAIL + "1" FAIL + "2" FAIL + "3" FAIL + "4" FAIL + "5" FAIL + "6" FAIL + "7" FAIL + "8" FAIL + "9" FAIL + "100" FAIL + */ + } + + @Test + public void int_1_to_9() throws Exception { + _ruleName = "int_1_to_9"; + + assertAST("1", "1"); + assertAST("2", "2"); + assertAST("3", "3"); + assertAST("4", "4"); + assertAST("5", "5"); + assertAST("6", "6"); + assertAST("7", "7"); + assertAST("8", "8"); + assertAST("9", "9"); + /* + FAIL: + 0 + 00 + 01 + 02 + 03 + 04 + 05 + 06 + 07 + 08 + 09 + */ + } + + @Test + public void int_01_to_12() throws Exception { + _ruleName = "int_01_to_12"; + + assertAST("01", "01"); + assertAST("02", "02"); + assertAST("03", "03"); + assertAST("04", "04"); + assertAST("05", "05"); + assertAST("06", "06"); + assertAST("07", "07"); + assertAST("08", "08"); + assertAST("09", "09"); + assertAST("10", "10"); + assertAST("11", "11"); + assertAST("12", "12"); + /* + FAIL: + 0 + 00 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + */ + } + + + @Test + public void int_13_to_23() throws Exception { + _ruleName = "int_13_to_23"; + + assertAST("13", "13"); + assertAST("14", "14"); + assertAST("15", "15"); + assertAST("16", "16"); + assertAST("17", "17"); + assertAST("18", "18"); + assertAST("19", "19"); + assertAST("20", "20"); + assertAST("21", "21"); + assertAST("22", "22"); + assertAST("23", "23"); + } + + @Test + public void int_24_to_31() throws Exception { + _ruleName = "int_24_to_31"; + + assertAST("24", "24"); + assertAST("25", "25"); + assertAST("26", "26"); + assertAST("27", "27"); + assertAST("28", "28"); + assertAST("29", "29"); + assertAST("30", "30"); + assertAST("31", "31"); + } + + @Test + public void int_32_to_59() throws Exception { + _ruleName = "int_32_to_59"; + + assertAST("32", "32"); + assertAST("33", "33"); + assertAST("34", "34"); + assertAST("35", "35"); + assertAST("36", "36"); + assertAST("37", "37"); + assertAST("38", "38"); + assertAST("39", "39"); + assertAST("40", "40"); + assertAST("41", "41"); + assertAST("42", "42"); + assertAST("43", "43"); + assertAST("44", "44"); + assertAST("45", "45"); + assertAST("46", "46"); + assertAST("47", "47"); + assertAST("48", "48"); + assertAST("49", "49"); + assertAST("50", "50"); + assertAST("51", "51"); + assertAST("52", "52"); + assertAST("53", "53"); + assertAST("54", "54"); + assertAST("55", "55"); + assertAST("56", "56"); + assertAST("57", "57"); + assertAST("58", "58"); + assertAST("59", "59"); + } + + @Test + public void int_60_to_99() throws Exception { + _ruleName = "int_60_to_99"; + + assertAST("60", "60"); + assertAST("61", "61"); + assertAST("62", "62"); + assertAST("63", "63"); + assertAST("64", "64"); + assertAST("65", "65"); + assertAST("66", "66"); + assertAST("67", "67"); + assertAST("68", "68"); + assertAST("69", "69"); + assertAST("70", "70"); + assertAST("71", "71"); + assertAST("72", "72"); + assertAST("73", "73"); + assertAST("74", "74"); + assertAST("75", "75"); + assertAST("76", "76"); + assertAST("77", "77"); + assertAST("78", "78"); + assertAST("79", "79"); + assertAST("80", "80"); + assertAST("81", "81"); + assertAST("82", "82"); + assertAST("83", "83"); + assertAST("84", "84"); + assertAST("85", "85"); + assertAST("86", "86"); + assertAST("87", "87"); + assertAST("88", "88"); + assertAST("89", "89"); + assertAST("90", "90"); + assertAST("91", "91"); + assertAST("92", "92"); + assertAST("93", "93"); + assertAST("94", "94"); + assertAST("95", "95"); + assertAST("96", "96"); + assertAST("97", "97"); + assertAST("98", "98"); + assertAST("99", "99"); + } + + @Test + public void int_01_to_12_optional_prefix() throws Exception { + _ruleName = "int_01_to_12_optional_prefix"; + + assertAST("01", "01"); + assertAST("1", "1"); + assertAST("02", "02"); + assertAST("2", "2"); + assertAST("03", "03"); + assertAST("3", "3"); + assertAST("04", "04"); + assertAST("4", "4"); + assertAST("05", "05"); + assertAST("5", "5"); + assertAST("06", "06"); + assertAST("6", "6"); + assertAST("07", "07"); + assertAST("7", "7"); + assertAST("08", "08"); + assertAST("8", "8"); + assertAST("09", "09"); + assertAST("9", "9"); + assertAST("10", "10"); + assertAST("11", "11"); + assertAST("12", "12"); + } + + @Test + public void int_01_to_31_optional_prefix() throws Exception { + _ruleName = "int_01_to_31_optional_prefix"; + + assertAST("01", "01"); + assertAST("1", "1"); + assertAST("02", "02"); + assertAST("2", "2"); + assertAST("03", "03"); + assertAST("3", "3"); + assertAST("04", "04"); + assertAST("4", "4"); + assertAST("05", "05"); + assertAST("5", "5"); + assertAST("06", "06"); + assertAST("6", "6"); + assertAST("07", "07"); + assertAST("7", "7"); + assertAST("08", "08"); + assertAST("8", "8"); + assertAST("09", "09"); + assertAST("9", "9"); + assertAST("10", "10"); + assertAST("11", "11"); + assertAST("12", "12"); + assertAST("13", "13"); + assertAST("14", "14"); + assertAST("15", "15"); + assertAST("16", "16"); + assertAST("17", "17"); + assertAST("18", "18"); + assertAST("19", "19"); + assertAST("20", "20"); + assertAST("21", "21"); + assertAST("22", "22"); + assertAST("23", "23"); + assertAST("24", "24"); + assertAST("25", "25"); + assertAST("26", "26"); + assertAST("27", "27"); + assertAST("28", "28"); + assertAST("29", "29"); + assertAST("30", "30"); + assertAST("31", "31"); + } + + @Test + public void int_four_digits() throws Exception { + _ruleName = "int_four_digits"; + + assertAST("0000", "0000"); + assertAST("0100", "0100"); + assertAST("0020", "0020"); + assertAST("0003", "0003"); + assertAST("9999", "9999"); + assertAST("5050", "5050"); + //"000" FAIL + //"33" FAIL + //"2" FAIL + } + + @Test + public void spelled_or_int_01_to_31_optional_prefix() throws Exception { + _ruleName = "spelled_or_int_01_to_31_optional_prefix"; + + assertAST("one", "1"); + assertAST("two", "2"); + assertAST("three", "3"); + assertAST("four", "4"); + assertAST("five", "5"); + assertAST("six", "6"); + assertAST("seven", "7"); + assertAST("eight", "8"); + assertAST("nine", "9"); + assertAST("ten", "10"); + assertAST("eleven", "11"); + assertAST("twelve", "12"); + assertAST("thirteen", "13"); + assertAST("fourteen", "14"); + assertAST("fifteen", "15"); + assertAST("sixteen", "16"); + assertAST("seventeen", "17"); + assertAST("eighteen", "18"); + assertAST("nineteen", "19"); + assertAST("twenty", "20"); + assertAST("twenty one", "21"); + assertAST("twenty-one", "21"); + assertAST("twenty two", "22"); + assertAST("twenty-two", "22"); + assertAST("twenty three", "23"); + assertAST("twenty-three", "23"); + assertAST("twenty four", "24"); + assertAST("twenty-four", "24"); + assertAST("twenty five", "25"); + assertAST("twenty-five", "25"); + assertAST("twenty six", "26"); + assertAST("twenty-six", "26"); + assertAST("twenty seven", "27"); + assertAST("twenty-seven", "27"); + assertAST("twenty-eight", "28"); + assertAST("twenty nine", "29"); + assertAST("twenty-nine", "29"); + assertAST("thirty", "30"); + assertAST("thirty one", "31"); + assertAST("thirty-one", "31"); + assertAST("01", "01"); + assertAST("1", "1"); + assertAST("02", "02"); + assertAST("2", "2"); + assertAST("03", "03"); + assertAST("3", "3"); + assertAST("04", "04"); + assertAST("4", "4"); + assertAST("05", "05"); + assertAST("5", "5"); + assertAST("06", "06"); + assertAST("6", "6"); + assertAST("07", "07"); + assertAST("7", "7"); + assertAST("08", "08"); + assertAST("8", "8"); + assertAST("09", "09"); + assertAST("9", "9"); + assertAST("10", "10"); + assertAST("11", "11"); + assertAST("12", "12"); + assertAST("13", "13"); + assertAST("14", "14"); + assertAST("15", "15"); + assertAST("16", "16"); + assertAST("17", "17"); + assertAST("18", "18"); + assertAST("19", "19"); + assertAST("20", "20"); + assertAST("21", "21"); + assertAST("22", "22"); + assertAST("23", "23"); + assertAST("24", "24"); + assertAST("25", "25"); + assertAST("26", "26"); + assertAST("27", "27"); + assertAST("28", "28"); + assertAST("29", "29"); + assertAST("30", "30"); + assertAST("31", "31"); + //assertAST("zero", FAIL + } + + @Test + public void spelled_or_int_optional_prefix() throws Exception { + _ruleName = "spelled_or_int_optional_prefix"; + + assertAST("1", "1"); + assertAST("01", "01"); + assertAST("60", "60"); + assertAST("99", "99"); + assertAST("one", "1"); + assertAST("two", "2"); + //"00" FAIL + //"0" FAIL + //"zero" FAIL + } + + @Test + public void spelled_one_to_thirty_one() throws Exception { + _ruleName = "spelled_one_to_thirty_one"; + + assertAST("one", "1"); + assertAST("two", "2"); + assertAST("three", "3"); + assertAST("four", "4"); + assertAST("five", "5"); + assertAST("six", "6"); + assertAST("seven", "7"); + assertAST("eight", "8"); + assertAST("nine", "9"); + assertAST("ten", "10"); + assertAST("eleven", "11"); + assertAST("twelve", "12"); + assertAST("thirteen", "13"); + assertAST("fourteen", "14"); + assertAST("fifteen", "15"); + assertAST("sixteen", "16"); + assertAST("seventeen", "17"); + assertAST("eighteen", "18"); + assertAST("nineteen", "19"); + assertAST("twenty", "20"); + assertAST("twenty one", "21"); + assertAST("twenty-one", "21"); + assertAST("twenty two", "22"); + assertAST("twenty-two", "22"); + assertAST("twenty three", "23"); + assertAST("twenty-three", "23"); + assertAST("twenty four", "24"); + assertAST("twenty-four", "24"); + assertAST("twenty five", "25"); + assertAST("twenty-five", "25"); + assertAST("twenty six", "26"); + assertAST("twenty-six", "26"); + assertAST("twenty seven", "27"); + assertAST("twenty-seven", "27"); + assertAST("twenty-eight", "28"); + assertAST("twenty nine", "29"); + assertAST("twenty-nine", "29"); + assertAST("thirty", "30"); + assertAST("thirty one", "31"); + assertAST("thirty-one", "31"); + + //"zero" FAIL + //"thirty two" FAIL + //"thirty-two" FAIL + } + + @Test + public void spelled_first_to_thirty_first() throws Exception { + _ruleName = "spelled_first_to_thirty_first"; + + assertAST("first", "1"); + assertAST("1st", "1"); + assertAST("second", "2"); + assertAST("2nd", "2"); + assertAST("third", "3"); + assertAST("3rd", "3"); + assertAST("fourth", "4"); + assertAST("4th", "4"); + assertAST("fifth", "5"); + assertAST("5th", "5"); + assertAST("sixth", "6"); + assertAST("6th", "6"); + assertAST("seventh", "7"); + assertAST("7th", "7"); + assertAST("eighth", "8"); + assertAST("8th", "8"); + assertAST("ninth", "9"); + assertAST("9th", "9"); + assertAST("tenth", "10"); + assertAST("10th", "10"); + assertAST("eleventh", "11"); + assertAST("11th", "11"); + assertAST("twelfth", "12"); + assertAST("12th", "12"); + assertAST("thirteenth", "13"); + assertAST("13th", "13"); + assertAST("fourteenth", "14"); + assertAST("14th", "14"); + assertAST("fifteenth", "15"); + assertAST("15th", "15"); + assertAST("sixteenth", "16"); + assertAST("16th", "16"); + assertAST("seventeenth", "17"); + assertAST("17th", "17"); + assertAST("eighteenth", "18"); + assertAST("18th", "18"); + assertAST("nineteenth", "19"); + assertAST("19th", "19"); + assertAST("twentieth", "20"); + assertAST("20th", "20"); + assertAST("twenty-first", "21"); + assertAST("twenty first", "21"); + assertAST("21st", "21"); + assertAST("twenty-second", "22"); + assertAST("twenty second", "22"); + assertAST("22nd", "22"); + assertAST("twenty-third", "23"); + assertAST("twenty third", "23"); + assertAST("23rd", "23"); + assertAST("twenty-fourth", "24"); + assertAST("twenty fourth", "24"); + assertAST("24th", "24"); + assertAST("twenty-fifth", "25"); + assertAST("twenty fifth", "25"); + assertAST("25th", "25"); + assertAST("twenty-sixth", "26"); + assertAST("twenty sixth", "26"); + assertAST("26th", "26"); + assertAST("twenty-seventh", "27"); + assertAST("twenty seventh", "27"); + assertAST("27th", "27"); + assertAST("twenty-eighth", "28"); + assertAST("twenty eighth", "28"); + assertAST("28th", "28"); + assertAST("twenty-ninth", "29"); + assertAST("twenty ninth", "29"); + assertAST("29th", "29"); + assertAST("thirtieth", "30"); + assertAST("30th", "30"); + assertAST("thirty-first", "31"); + assertAST("thirty first", "31"); + assertAST("31st", "31"); + } +} diff --git a/src/test/java/com/joestelmach/natty/grammar/TimeGrammarTest.java b/src/test/java/com/joestelmach/natty/grammar/TimeGrammarTest.java new file mode 100644 index 00000000..b0eaba2f --- /dev/null +++ b/src/test/java/com/joestelmach/natty/grammar/TimeGrammarTest.java @@ -0,0 +1,189 @@ +package com.joestelmach.natty.grammar; + +import org.junit.Test; + +public class TimeGrammarTest extends AbstractGrammarTest { + + @Test + public void time_zone_abbreviation() throws Exception { + _ruleName = "time_zone_abbreviation"; + + assertAST("est", "America/New_York"); + assertAST("edt", "America/New_York"); + assertAST("et", "America/New_York"); + assertAST("pst", "America/Los_Angeles"); + assertAST("pdt", "America/Los_Angeles"); + assertAST("pt", "America/Los_Angeles"); + assertAST("cst", "America/Chicago"); + assertAST("cdt", "America/Chicago"); + assertAST("ct", "America/Chicago"); + assertAST("mst", "America/Denver"); + assertAST("mdt", "America/Denver"); + assertAST("mt", "America/Denver"); + assertAST("akst", "America/Anchorage"); + assertAST("akdt", "America/Anchorage"); + assertAST("akt", "America/Anchorage"); + assertAST("hast", "Pacific/Honolulu"); + assertAST("hadt" , "Pacific/Honolulu"); + assertAST("hat" , "Pacific/Honolulu"); + assertAST("hst", "Pacific/Honolulu"); + } + + @Test + public void meridian_indicator() throws Exception { + _ruleName = "meridian_indicator"; + + assertAST("am", "am"); + assertAST("a.m.", "am"); + assertAST("a", "am"); + assertAST("pm", "pm"); + assertAST("p.m.", "pm"); + assertAST("p", "pm"); + } + + @Test + public void minutes() throws Exception { + _ruleName = "minutes"; + + assertAST("00", "(MINUTES_OF_HOUR 00)"); + assertAST("01", "(MINUTES_OF_HOUR 01)"); + assertAST("02", "(MINUTES_OF_HOUR 02)"); + assertAST("03", "(MINUTES_OF_HOUR 03)"); + assertAST("04", "(MINUTES_OF_HOUR 04)"); + assertAST("05", "(MINUTES_OF_HOUR 05)"); + assertAST("06", "(MINUTES_OF_HOUR 06)"); + assertAST("07", "(MINUTES_OF_HOUR 07)"); + assertAST("08", "(MINUTES_OF_HOUR 08)"); + assertAST("09", "(MINUTES_OF_HOUR 09)"); + assertAST("10", "(MINUTES_OF_HOUR 10)"); + assertAST("11", "(MINUTES_OF_HOUR 11)"); + assertAST("12", "(MINUTES_OF_HOUR 12)"); + assertAST("13", "(MINUTES_OF_HOUR 13)"); + assertAST("14", "(MINUTES_OF_HOUR 14)"); + assertAST("15", "(MINUTES_OF_HOUR 15)"); + assertAST("16", "(MINUTES_OF_HOUR 16)"); + assertAST("17", "(MINUTES_OF_HOUR 17)"); + assertAST("18", "(MINUTES_OF_HOUR 18)"); + assertAST("19", "(MINUTES_OF_HOUR 19)"); + assertAST("20", "(MINUTES_OF_HOUR 20)"); + assertAST("21", "(MINUTES_OF_HOUR 21)"); + assertAST("22", "(MINUTES_OF_HOUR 22)"); + assertAST("23", "(MINUTES_OF_HOUR 23)"); + assertAST("24", "(MINUTES_OF_HOUR 24)"); + assertAST("25", "(MINUTES_OF_HOUR 25)"); + assertAST("26", "(MINUTES_OF_HOUR 26)"); + assertAST("27", "(MINUTES_OF_HOUR 27)"); + assertAST("28", "(MINUTES_OF_HOUR 28)"); + assertAST("29", "(MINUTES_OF_HOUR 29)"); + assertAST("30", "(MINUTES_OF_HOUR 30)"); + assertAST("31", "(MINUTES_OF_HOUR 31)"); + assertAST("32", "(MINUTES_OF_HOUR 32)"); + assertAST("33", "(MINUTES_OF_HOUR 33)"); + assertAST("34", "(MINUTES_OF_HOUR 34)"); + assertAST("35", "(MINUTES_OF_HOUR 35)"); + assertAST("36", "(MINUTES_OF_HOUR 36)"); + assertAST("37", "(MINUTES_OF_HOUR 37)"); + assertAST("38", "(MINUTES_OF_HOUR 38)"); + assertAST("39", "(MINUTES_OF_HOUR 39)"); + assertAST("40", "(MINUTES_OF_HOUR 40)"); + assertAST("41", "(MINUTES_OF_HOUR 41)"); + assertAST("42", "(MINUTES_OF_HOUR 42)"); + assertAST("43", "(MINUTES_OF_HOUR 43)"); + assertAST("44", "(MINUTES_OF_HOUR 44)"); + assertAST("45", "(MINUTES_OF_HOUR 45)"); + assertAST("46", "(MINUTES_OF_HOUR 46)"); + assertAST("47", "(MINUTES_OF_HOUR 47)"); + assertAST("48", "(MINUTES_OF_HOUR 48)"); + assertAST("49", "(MINUTES_OF_HOUR 49)"); + assertAST("50", "(MINUTES_OF_HOUR 50)"); + assertAST("51", "(MINUTES_OF_HOUR 51)"); + assertAST("52", "(MINUTES_OF_HOUR 52)"); + assertAST("53", "(MINUTES_OF_HOUR 53)"); + assertAST("54", "(MINUTES_OF_HOUR 54)"); + assertAST("55", "(MINUTES_OF_HOUR 55)"); + assertAST("56", "(MINUTES_OF_HOUR 56)"); + assertAST("57", "(MINUTES_OF_HOUR 57)"); + assertAST("58", "(MINUTES_OF_HOUR 58)"); + assertAST("59", "(MINUTES_OF_HOUR 59)"); + //assertAST("0", "FAIL "); + //assertAST("1", "FAIL"); + //assertAST("2", "FAIL"); + //assertAST("3", "FAIL "); + //assertAST("4", "FAIL"); + //assertAST("5", "FAIL"); + //assertAST("6", "FAIL"); + //assertAST("7", "FAIL"); + //assertAST("8", "FAIL"); + //assertAST("9", "FAIL"); + //"60" FAIL + } + + @Test + public void hours() throws Exception { + _ruleName = "hours"; + + assertAST("00", "(HOURS_OF_DAY 00)"); + assertAST("01", "(HOURS_OF_DAY 01)"); + assertAST("1" , "(HOURS_OF_DAY 1)"); + assertAST("02", "(HOURS_OF_DAY 02)"); + assertAST("2" , "(HOURS_OF_DAY 2)"); + assertAST("03", "(HOURS_OF_DAY 03)"); + assertAST("3" , "(HOURS_OF_DAY 3)"); + assertAST("04", "(HOURS_OF_DAY 04)"); + assertAST("4" , "(HOURS_OF_DAY 4)"); + assertAST("05", "(HOURS_OF_DAY 05)"); + assertAST("5" , "(HOURS_OF_DAY 5)"); + assertAST("06", "(HOURS_OF_DAY 06)"); + assertAST("6" , "(HOURS_OF_DAY 6)"); + assertAST("07", "(HOURS_OF_DAY 07)"); + assertAST("7" , "(HOURS_OF_DAY 7)"); + assertAST("08", "(HOURS_OF_DAY 08)"); + assertAST("8" , "(HOURS_OF_DAY 8)"); + assertAST("09", "(HOURS_OF_DAY 09)"); + assertAST("9" , "(HOURS_OF_DAY 9)"); + assertAST("10", "(HOURS_OF_DAY 10)"); + assertAST("11", "(HOURS_OF_DAY 11)"); + assertAST("12", "(HOURS_OF_DAY 12)"); + assertAST("13", "(HOURS_OF_DAY 13)"); + assertAST("14", "(HOURS_OF_DAY 14)"); + assertAST("15", "(HOURS_OF_DAY 15)"); + assertAST("16", "(HOURS_OF_DAY 16)"); + assertAST("17", "(HOURS_OF_DAY 17)"); + assertAST("18", "(HOURS_OF_DAY 18)"); + assertAST("19", "(HOURS_OF_DAY 19)"); + assertAST("20", "(HOURS_OF_DAY 20)"); + assertAST("21", "(HOURS_OF_DAY 21)"); + assertAST("22", "(HOURS_OF_DAY 22)"); + assertAST("23", "(HOURS_OF_DAY 23)"); + //"-1" FAIL + //"24" FAIL + } + + @Test + public void explicit_time() throws Exception { + _ruleName = "explicit_time"; + + assertAST("0600h", "(EXPLICIT_TIME (HOURS_OF_DAY 06) (MINUTES_OF_HOUR 00))"); + assertAST("06:00h", "(EXPLICIT_TIME (HOURS_OF_DAY 06) (MINUTES_OF_HOUR 00))"); + assertAST("06:00 hours", "(EXPLICIT_TIME (HOURS_OF_DAY 06) (MINUTES_OF_HOUR 00))"); + assertAST("0000", "(EXPLICIT_TIME (HOURS_OF_DAY 00) (MINUTES_OF_HOUR 00))"); + assertAST("0700h", "(EXPLICIT_TIME (HOURS_OF_DAY 07) (MINUTES_OF_HOUR 00))"); + assertAST("6pm", "(EXPLICIT_TIME (HOURS_OF_DAY 6) (MINUTES_OF_HOUR 0) pm)"); + assertAST("5:30 a.m.", "(EXPLICIT_TIME (HOURS_OF_DAY 5) (MINUTES_OF_HOUR 30) am)"); + assertAST("5", "(EXPLICIT_TIME (HOURS_OF_DAY 5) (MINUTES_OF_HOUR 0))"); + assertAST("12:59", "(EXPLICIT_TIME (HOURS_OF_DAY 12) (MINUTES_OF_HOUR 59))"); + assertAST("23:59", "(EXPLICIT_TIME (HOURS_OF_DAY 23) (MINUTES_OF_HOUR 59))"); + assertAST("00:00", "(EXPLICIT_TIME (HOURS_OF_DAY 00) (MINUTES_OF_HOUR 00))"); + assertAST("10:00am", "(EXPLICIT_TIME (HOURS_OF_DAY 10) (MINUTES_OF_HOUR 00) am)"); + assertAST("10a", "(EXPLICIT_TIME (HOURS_OF_DAY 10) (MINUTES_OF_HOUR 0) am)"); + assertAST("10am", "(EXPLICIT_TIME (HOURS_OF_DAY 10) (MINUTES_OF_HOUR 0) am)"); + assertAST("10", "(EXPLICIT_TIME (HOURS_OF_DAY 10) (MINUTES_OF_HOUR 0))"); + assertAST("8p", "(EXPLICIT_TIME (HOURS_OF_DAY 8) (MINUTES_OF_HOUR 0) pm)"); + assertAST("8pm", "(EXPLICIT_TIME (HOURS_OF_DAY 8) (MINUTES_OF_HOUR 0) pm)"); + assertAST("8 pm", "(EXPLICIT_TIME (HOURS_OF_DAY 8) (MINUTES_OF_HOUR 0) pm)"); + assertAST("noon", "(EXPLICIT_TIME (HOURS_OF_DAY 12) (MINUTES_OF_HOUR 0) (SECONDS_OF_MINUTE 0) pm)"); + assertAST("afternoon", "(EXPLICIT_TIME (HOURS_OF_DAY 12) (MINUTES_OF_HOUR 0) (SECONDS_OF_MINUTE 0) pm)"); + assertAST("midnight", "(EXPLICIT_TIME (HOURS_OF_DAY 12) (MINUTES_OF_HOUR 0) (SECONDS_OF_MINUTE 0) am)"); + assertAST("mid-night", "(EXPLICIT_TIME (HOURS_OF_DAY 12) (MINUTES_OF_HOUR 0) (SECONDS_OF_MINUTE 0) am)"); + } +} diff --git a/src/test/java/com/natty/parse/ParserTest.java b/src/test/java/com/natty/parse/ParserTest.java deleted file mode 100644 index b3e570d3..00000000 --- a/src/test/java/com/natty/parse/ParserTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.natty.parse; - -import java.util.Arrays; -import java.util.Date; -import java.util.List; - -import junit.framework.Assert; - -import org.junit.Test; - -import com.joestelmach.natty.ParseResult; -import com.joestelmach.natty.Parser; -import com.joestelmach.natty.WalkerState; - -/** - * Runs the DateParser through it's paces - * - * @author Joe Stelmach - */ -public class ParserTest { - - @Test - public void leadingSpace() { - Parser parser = new Parser(); - ParseResult result = parser.parse(" 02/18/1985 "); - Assert.assertEquals(1, result.getDates().size()); - } - - public static void main(String[] args) { - String inputString = "in the end of june"; - inputString = "the second day of april"; - inputString = "the thirtieth day of 3 aprils ago"; - Parser parser = new Parser(); - parser.setDebug(true); - ParseResult result = parser.parse(inputString); - System.out.println(result.getSyntaxTree()); - List dateTimes = result.getDates(); - System.out.println(Arrays.toString(dateTimes.toArray())); - } -} diff --git a/src/test/resources/cpan.txt b/src/test/resources/cpan.txt new file mode 100644 index 00000000..2f46f92f --- /dev/null +++ b/src/test/resources/cpan.txt @@ -0,0 +1,320 @@ + now + yesterday + today + tomorrow + morning + afternoon + evening + noon + midnight + yesterday at noon + yesterday at midnight + today at noon + today at midnight + tomorrow at noon + tomorrow at midnight + this morning + this afternoon + this evening + yesterday morning + yesterday afternoon + yesterday evening + today morning + today afternoon + today evening + tomorrow morning + tomorrow afternoon + tomorrow evening + thursday morning + thursday afternoon + thursday evening + 6:00 yesterday + 6:00 today + 6:00 tomorrow + 5am yesterday + 5am today + 5am tomorrow + 4pm yesterday + 4pm today + 4pm tomorrow + last second + this second + next second + last minute + this minute + next minute + last hour + this hour + next hour + last day + this day + next day + last week + this week + next week + last month + this month + next month + last year + this year + next year + last friday + this friday + next friday + tuesday last week + tuesday this week + tuesday next week + last week wednesday + this week wednesday + next week wednesday + 10 seconds ago + 10 minutes ago + 10 hours ago + 10 days ago + 10 weeks ago + 10 months ago + 10 years ago + in 5 seconds + in 5 minutes + in 5 hours + in 5 days + in 5 weeks + in 5 months + in 5 years + saturday + sunday 11:00 + yesterday at 4:00 + today at 4:00 + tomorrow at 4:00 + yesterday at 6:45am + today at 6:45am + tomorrow at 6:45am + yesterday at 6:45pm + today at 6:45pm + tomorrow at 6:45pm + yesterday at 2:32 AM + today at 2:32 AM + tomorrow at 2:32 AM + yesterday at 2:32 PM + today at 2:32 PM + tomorrow at 2:32 PM + yesterday 02:32 + today 02:32 + tomorrow 02:32 + yesterday 2:32am + today 2:32am + tomorrow 2:32am + yesterday 2:32pm + today 2:32pm + tomorrow 2:32pm + wednesday at 14:30 + wednesday at 02:30am + wednesday at 02:30pm + wednesday 14:30 + wednesday 02:30am + wednesday 02:30pm + friday 03:00 am + friday 03:00 pm + sunday at 05:00 am + sunday at 05:00 pm + 4th february + november 3rd + last june + next october + 6 am + 5am + 5:30am + 8 pm + 4pm + 4:20pm + 06:56:06 am + 06:56:06 pm + mon 2:35 + 1:00 sun + 1am sun + 1pm sun + 1:00 on sun + 1am on sun + 1pm on sun + 12:14 PM + 12:14 AM + 2 seconds before now + 2 minutes before now + 2 hours before now + 2 days before now + 2 weeks before now + 2 months before now + 2 years before now + 4 seconds from now + 4 minutes from now + 4 hours from now + 4 days from now + 4 weeks from now + 4 months from now + 4 years from now + 6 in the morning + 4 in the afternoon + 9 in the evening + monday 6 in the morning + monday 4 in the afternoon + monday 9 in the evening + last sunday at 21:45 + monday last week + 12th day last month + 12th day this month + 12th day next month + 1st tuesday last november + 1st tuesday this november + 1st tuesday next november + 10 hours before noon + 10 hours before midnight + 5 hours after noon + 5 hours after midnight + noon last friday + midnight last friday + noon this friday + midnight this friday + noon next friday + midnight next friday + last friday at 20:00 + this friday at 20:00 + next friday at 20:00 + 1:00 last friday + 1:00 this friday + 1:00 next friday + 1am last friday + 1am this friday + 1am next friday + 1pm last friday + 1pm this friday + 1pm next friday + 5 am last monday + 5 am this monday + 5 am next monday + 5 pm last monday + 5 pm this monday + 5 pm next monday + last wednesday 7am + this wednesday 7am + next wednesday 7am + last wednesday 7pm + this wednesday 7pm + next wednesday 7pm + last tuesday 11 am + this tuesday 11 am + next tuesday 11 am + last tuesday 11 pm + this tuesday 11 pm + next tuesday 11 pm + yesterday at 13:00 + today at 13:00 + tomorrow at 13 + 2nd friday in august + 3rd wednesday in november + tomorrow 1 year ago + 11 january 2 years ago + 6 mondays from now + final thursday in april + last thursday in april + monday to friday + 1 April to 31 August + 1999-12-31 to tomorrow + now to 2010-01-01 + 2009-03-10 9:00 to 11:00 + 26 oct 10:00 am to 11:00 am + jan 1 to 2 + 16:00 nov 6 to 17:00 + may 2nd to 5th + 6am dec 5 to 7am + 1/3 to 2/3 + 2/3 to in 1 week + 3/3 21:00 to in 5 days + first day of 2009 to last day of 2009 + first day of may to last day of may + first to last day of 2008 + first to last day of september + for 4 seconds + for 4 minutes + for 4 hours + for 4 days + for 4 weeks + for 4 months + for 4 years + january 11 + 11 january + 18 oct 17:00 + 18 oct 5am + 18 oct 5pm + 18 oct 5 am + 18 oct 5 pm + dec 25 + feb 28 3:00 + feb 28 3am + feb 28 3pm + feb 28 3 am + feb 28 3 pm + 19:00 jul 1 + 7am jul 1 + 7pm jul 1 + 7 am jul 1 + 7 pm jul 1 + jan 24, 2011 12:00 + jan 24, 2011 12am + jan 24, 2011 12pm + may 27th + 2005 + march 1st 2009 + february 14, 2004 + jan 3 2010 + 3 jan 2000 + 2010 october 28 + 1/3 + 1/3 16:00 + 4:00 + 17:00 + 3:20:00 + + #1st day last year + #1st day this year + #1st day next year + #6th day last week + #6th day this week + #6th day next week + #yesterday 7 seconds ago + #yesterday 7 minutes ago + #yesterday 7 hours ago + #yesterday 7 days ago + #yesterday 7 weeks ago + #yesterday 7 months ago + #yesterday 7 years ago + #tomorrow 3 seconds ago + #tomorrow 3 minutes ago + #tomorrow 3 hours ago + #tomorrow 3 days ago + #tomorrow 3 weeks ago + #tomorrow 3 months ago + #tomorrow 3 years ago + #2nd monday + #100th day + #11 january next year + #11 january this year + #11 january last year + #6 hours before yesterday + #6 hours before tomorrow + #3 hours after yesterday + #3 hours after tomorrow + #saturday 3 months ago at 17:00 + #saturday 3 months ago at 5:00am + #saturday 3 months ago at 5:00pm + #4th day last week + #8th month last year + #8th month this year + #8th month next year + #fri 3 months ago at 5am + #wednesday 1 month ago at 8pm + #October 2006 + #27/5/1979 + #-5min + #+2d + #100th day to 200th + #march \ No newline at end of file