Duckling/Time/DE/Rules.hs (1,815 lines of code) (raw):

-- Copyright (c) 2016-present, Facebook, Inc. -- All rights reserved. -- -- This source code is licensed under the BSD-style license found in the -- LICENSE file in the root directory of this source tree. {-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE NoRebindableSyntax #-} {-# LANGUAGE OverloadedStrings #-} module Duckling.Time.DE.Rules ( rules ) where import Prelude import qualified Data.Text as Text import Duckling.Dimensions.Types import Duckling.Duration.Helpers (isGrain) import Duckling.Numeral.Helpers (parseInt) import Duckling.Ordinal.Types (OrdinalData(..)) import Duckling.Regex.Types (GroupMatch(..)) import Duckling.Time.Computed import Duckling.Time.Helpers import Duckling.Time.HolidayHelpers import Duckling.Time.Types (TimeData(..)) import Duckling.Types import qualified Duckling.Ordinal.Types as TOrdinal import qualified Duckling.Time.Types as TTime import qualified Duckling.TimeGrain.Types as TG ruleInstants :: [Rule] ruleInstants = mkRuleInstants [ ( "now" , TG.Second, 0, "(genau)? ?jetzt|diesen moment|in diesem moment|gerade eben" ) , ( "today" , TG.Day , 0, "heute|(um diese zeit|zu dieser zeit|um diesen zeitpunkt|zu diesem zeitpunkt)" ) , ( "tomorrow" , TG.Day , 1, "morgen" ) , ( "yesterday" , TG.Day , -1, "gestern" ) , ( "after tomorrow" , TG.Day , 2, "(ü)bermorgen" ) , ( "before yesterday", TG.Day , -2, "vorgestern" ) , ( "3 days ago" , TG.Day , -3, "vorvorgestern" ) , ( "EOM|End of month", TG.Month , 1, "(das )?ende des monats?" ) , ( "EOY|End of year" , TG.Year , 1, "(das )?(EOY|jahr(es)? ?ende|ende (des )?jahr(es)?)" ) ] ruleDaysOfWeek :: [Rule] ruleDaysOfWeek = mkRuleDaysOfWeek [ ( "Montag" , "montags?|mo\\.?" ) , ( "Dienstag" , "die?nstags?|di\\.?" ) , ( "Mittwoch" , "mittwochs?|mi\\.?" ) , ( "Donnerstag", "donn?erstag|do\\.?" ) , ( "Freitag" , "freitags?|fr\\.?" ) , ( "Samstag" , "samstags?|sonnabends?|sa\\.?" ) , ( "Sonntag" , "sonntags?|so\\." ) ] ruleMonths :: [Rule] ruleMonths = mkRuleMonths [ ( "Januar" , "januar|jan\\.?" ) , ( "Februar" , "februar|feb\\.?" ) , ( "Marz" , "m(ä)rz|m(ä)r\\.?" ) , ( "April" , "april|apr\\.?" ) , ( "Mai" , "mai\\.?" ) , ( "Juni" , "juni|jun\\.?" ) , ( "Juli" , "juli|jul\\.?" ) , ( "August" , "august|aug\\.?" ) , ( "September", "september|sept?\\.?" ) , ( "Oktober" , "oktober|okt\\.?" ) , ( "November" , "november|nov\\.?" ) , ( "Dezember" , "dezember|dez\\.?" ) ] ruleSeasons :: [Rule] ruleSeasons = mkRuleSeasons [ ( "sommer" , "sommer" , monthDay 6 21, monthDay 9 23 ) , ( "herbst" , "herbst" , monthDay 9 23, monthDay 12 21 ) , ( "winter" , "winter" , monthDay 12 21, monthDay 3 20 ) , ( "fruhling", "fr(ü)h(ling|jahr)", monthDay 3 20, monthDay 6 21 ) ] ruleHolidays :: [Rule] ruleHolidays = mkRuleHolidays [ ( "Neujahr" , "neujahr(s?tag)?" , monthDay 1 1 ) , ( "Valentinstag" , "valentin'?stag" , monthDay 2 14 ) , ( "Schweizer Bundesfeiertag" , "schweiz(er)? (bundes)?feiertag|bundes feiertag" , monthDay 8 1 ) , ( "Tag der Deutschen Einheit" , "tag (der)? deutsc?hen? einheit" , monthDay 10 3 ) , ( "Oesterreichischer Nationalfeiertag" , "((ö)sterreichischer?)? nationalfeiertag|national feiertag" , monthDay 10 26 ) , ( "Halloween" , "hall?owe?en?" , monthDay 10 31 ) , ( "Allerheiligen" , "allerheiligen?|aller heiligen?" , monthDay 11 1 ) , ( "Nikolaus" , "nikolaus(tag)?|nikolaus tag|nikolo" , monthDay 12 6 ) , ( "Heiligabend" , "heilig(er)? abend" , monthDay 12 24 ) , ( "Weihnachten" , "weih?nacht(en|stag)?" , monthDay 12 25 ) , ( "Silvester" , "silvester" , monthDay 12 31 ) , ( "Muttertag" , "mutt?ertag|mutt?er (tag)?" , nthDOWOfMonth 2 7 5 ) , ( "Vatertag" , "vatt?er( ?tag)?" , nthDOWOfMonth 3 7 6 ) ] ruleComputedHolidays :: [Rule] ruleComputedHolidays = mkRuleHolidays [ ( "Christi Himmelfahrt", "(christi\\s+)?himmelfahrt(stag)?" , cycleNthAfter False TG.Day 39 easterSunday ) , ( "Aschermittwoch", "ascher?(tag|mittwoch)" , cycleNthAfter False TG.Day (-46) easterSunday ) , ( "Aschura", "asc?hura(\\-?tag)?" , cycleNthAfter False TG.Day 9 muharram ) , ( "Bhai Dooj", "bhai(ya)?\\s+d(u|oo)j|bhau\\-beej|bhai\\s+(tika|phonta)" , cycleNthAfter False TG.Day 4 dhanteras ) , ( "Chhath", "chhathi?|chhath (parv|puja)|dala (chhath|puja)|surya shashthi" , cycleNthAfter False TG.Day 8 dhanteras ) , ( "Boghi", "boghi|bogi\\s+pandigai" , cycleNthAfter False TG.Day (-1) thaiPongal ) , ( "Chinesisches Neujahr", "chinesische(s|r)\\s+(neujahr(s(tag|fest))?|frühlingsfest)" , chineseNewYear ) , ( "Aschermontag" , "(orthodoxer?\\s+)?(ascher|reiner?\\s+|sauberer?\\s+)montag" , cycleNthAfter False TG.Day (-48) orthodoxEaster ) , ( "Corpus Christi", "corpus\\s+christi|fronleichnam" , cycleNthAfter False TG.Day 60 easterSunday ) , ( "Dhanteras", "dhanatrayodashi|dhanteras|dhanvantari\\s+trayodashi" , dhanteras ) , ( "Diwali", "deepavali|diwali|lakshmi\\s+puja" , cycleNthAfter False TG.Day 2 dhanteras ) , ( "Durga Ashtami", "(durga|maha)(\\s+a)?shtami" , cycleNthAfter False TG.Day 7 navaratri ) , ( "Ostermontag", "ostermontag" , cycleNthAfter False TG.Day 1 easterSunday ) , ( "Ostersonntag", "ostersonntag", easterSunday ) , ( "Eid al-Adha", "bakr[\\-\\s]e?id|e?id [au]l\\-adha|opferfest" , eidalAdha ) , ( "Eid al-Fitr", "eid al\\-fitr", eidalFitr ) , ( "Govardhan Puja", "govardhan\\s+puja|annak(u|oo)t" , cycleNthAfter False TG.Day 3 dhanteras ) , ( "Karfreitag", "(kar|stiller\\s+|hoher\\s+)freitag" , cycleNthAfter False TG.Day (-2) easterSunday ) , ( "Guru Gobind Singh Jayanti" , "guru\\s+(gobind|govind)\\s+singh\\s+(Geburtstag|jayanti)" , guruGobindSinghJayanti ) , ( "Holi", "(rangwali )?holi|dhuleti|dhulandi|phagwah" , cycleNthAfter False TG.Day 39 vasantPanchami ) , ( "Holika Dahan", "holika dahan|kamudu pyre|chhoti holi" , cycleNthAfter False TG.Day 38 vasantPanchami ) , ( "Karsamstag" , "(kar|stiller\\s+)samstag|karsonnabend" , cycleNthAfter False TG.Day (-1) easterSunday ) , ( "Islamisches Neujahr", "(arabisches|hijri|islamisches) neujahr|amun jadid|muharram" , muharram ) , ( "Isra and Mi'raj" , "isra and mi'raj|aufstieg des propheten|(die\\s+)?nachtreise|aufstieg\\s+in\\s+den\\s+himmel" , cycleNthAfter False TG.Day 26 rajab ) , ( "Jumu'atul-Wida", "jumu'atul\\-widaa?'?|jamat[\\-\\s]ul[\\-\\s]vida" , predNthAfter (-1) (dayOfWeek 5) eidalFitr ) , ( "Kaanum Pongal", "(kaanum|kanni)\\s+pongal" , cycleNthAfter False TG.Day 2 thaiPongal ) , ( "Lag BaOmer", "lag (b|l)[a']omer", lagBaOmer ) , ( "Vaisakhi", "mesadi|[bv]aisakhi|vaisakhadi|vasakhi|vaishakhi", vaisakhi) , ( "Lailat al-Qadr" , "la[iy]lat al[\\-\\s][qk]adr|(die)? nacht der (bestimmung|allmacht)" , cycleNthAfter False TG.Day 26 ramadan ) , ( "Lazarus-Samstag", "lazarus(\\-|\\s+)samstag" , cycleNthAfter False TG.Day (-8) orthodoxEaster ) , ( "Maha Navami", "maha\\s+navami", cycleNthAfter False TG.Day 8 navaratri ) , ( "Maha Saptami", "maha\\s+saptami", cycleNthAfter False TG.Day 6 navaratri ) , ( "Mattu Pongal", "maa?ttu\\s+pongal" , cycleNthAfter False TG.Day 1 thaiPongal ) , ( "Gründonnerstag" , "(grün|hoher\\s+|heiliger\\s+|weißer\\s+|palm)donnerstag" , cycleNthAfter False TG.Day (-3) easterSunday ) , ( "Maulid an-NabÄ«" , "Maulid\\s+an\\-NabÄ«|mawlid(\\s+al\\-nab(awi|i\\s+al\\-sharif))?|mevli[dt]|mulud|geburtstag des propheten( muhammad)?" , mawlid ) , ( "Naraka Chaturdashi" , "naraka? (nivaran )?chaturdashi|(kali|roop) chaudas|choti diwali" , cycleNthAfter False TG.Day 1 dhanteras ) , ( "Orthodoxer Ostermontag", "orthodoxer\\s+ostermontag" , cycleNthAfter False TG.Day 1 orthodoxEaster ) , ( "Orthodoxer Ostersonntag", "orthodoxer\\s+ostersonntag" , orthodoxEaster ) , ( "Orthodoxer Karsamstag", "orthodoxer\\s+karsamstag" , cycleNthAfter False TG.Day (-1) orthodoxEaster ) , ( "Orthodoxer Karfreitag", "orthodoxer\\s+karfreitag" , cycleNthAfter False TG.Day (-2) orthodoxEaster ) , ( "Orthodoxer Palmsonntag", "orthodoxer\\s+palmsonntag" , cycleNthAfter False TG.Day (-7) orthodoxEaster ) , ( "Palmsonntag", "palmsonntag" , cycleNthAfter False TG.Day (-7) easterSunday ) , ( "Pfingsten", "pfingsten|pentecost" , cycleNthAfter False TG.Day 49 easterSunday ) , ( "Purim", "purim", purim ) , ( "Raksha Bandhan", "raksha(\\s+)?bandhan|rakhi", rakshaBandhan ) , ( "Pargat Diwas", "pargat diwas|(maharishi )?valmiki jayanti", pargatDiwas ) , ( "Mahavir Jayanti", "(mahavir|mahaveer) (jayanti|janma kalyanak)" , mahavirJayanti ) , ( "Maha Shivaratri", "maha(\\s+)?shivaratri", mahaShivaRatri) , ( "Dayananda Saraswati Jayanti","((maharishi|swami) )?(dayananda )?saraswati jayanti", saraswatiJayanti ) , ( "Karva Chauth", "karva\\s+chauth|karaka\\s+chaturthi", karvaChauth) , ( "Krishna Janmashtami", "(krishna )?janmashtami|gokulashtami", krishnaJanmashtami ) , ( "Schmini Azeret", "sc?he?mini\\s+at?zeret" , cycleNthAfter False TG.Day 21 roshHashana ) , ( "Fastnacht", "fastnacht(sdienstag)?|mardi gras" , cycleNthAfter False TG.Day (-47) easterSunday ) , ( "Shushan Purim", "shushan\\s+purim", cycleNthAfter False TG.Day 1 purim ) , ( "Simchat Torah", "simc?hat\\s+torah" , cycleNthAfter False TG.Day 22 roshHashana ) , ( "Thai Pongal" , "(thai )?pongal|pongal pandigai|(makara? |magha )?sankranth?i|maghi" , thaiPongal ) , ( "Thiru Onam", "(thiru(v|\\s+))?onam", thiruOnam ) , ( "Tisha B'Av", "tisha b'av", tishaBAv ) , ( "Dreifaltigkeitssonntag", "trinitatis(fest)?|(dreifaltigkeits|goldener\\s+)sonntag|drei(faltigkeit|einigkeit)(sfest)?" , cycleNthAfter False TG.Day 56 easterSunday ) , ( "Vasant Panchami", "[bv]asant\\s+panchami", vasantPanchami ) , ( "Vijayadashami", "dasara|duss(eh|he)ra|vijayadashami" , cycleNthAfter False TG.Day 9 navaratri ) , ( "Tu biSchevat", "tu b[i']sc?he?vat", tuBishvat ) , ( "Vesak", "v(e|ai)sak(ha)?|buddha(\\-?tag|\\s+purnima)|wesakfest", vesak ) , ( "Jom Ha'atzmaut", "[yj]om ha'?atzmaut", yomHaatzmaut ) , ( "Jom HaShoah" , "[yj]om hashoah|[yj]om hazikaron lashoah ve-lag'vurah|holocaust\\-?gedenktag" , cycleNthAfter False TG.Day 12 passover ) , ( "Jom Kippur", "[yj]om\\s+kippur", cycleNthAfter False TG.Day 9 roshHashana ) , ( "Pfingstmontag", "pfingstmontag|(pentecost|whit)\\s+montag" , cycleNthAfter False TG.Day 50 easterSunday ) , ( "Rabindra Jayanti", "rabindra(nath)?\\s+jayanti", rabindraJayanti ) , ("Guru Ravidass Jayanti", "guru\\s+ravidass?\\s+(geburtstag|jayanti)" , ravidassJayanti ) ] ruleComputedHolidays' :: [Rule] ruleComputedHolidays' = mkRuleHolidays' [ ( "Global Youth Service-Tag", "global youth service[\\-\\s]?tag|gysd" , let start = globalYouthServiceDay end = cycleNthAfter False TG.Day 2 globalYouthServiceDay in interval TTime.Open start end ) , ( "Große Fastenzeit", "große\\s+fastenzeit" , let start = cycleNthAfter False TG.Day (-48) orthodoxEaster end = cycleNthAfter False TG.Day (-9) orthodoxEaster in interval TTime.Open start end ) , ( "Chanukka", "c?hann?ukk?ah?" , let start = chanukah end = cycleNthAfter False TG.Day 7 chanukah in interval TTime.Open start end ) , ( "Fastenzeit", "fastenzeit" , let start = cycleNthAfter False TG.Day (-46) easterSunday end = cycleNthAfter False TG.Day (-1) easterSunday in interval TTime.Open start end ) , ( "Navaratri", "durga\\s+puja|durgotsava|nava?rath?ri" , let start = navaratri end = cycleNthAfter False TG.Day 9 navaratri in interval TTime.Open start end ) , ( "Pessach", "passover|pess?a[ck]?h|pascha|Passah?" , let start = passover end = cycleNthAfter False TG.Day 8 passover in interval TTime.Open start end ) , ( "Ramadan", "rama[dt]h?an|ramzaa?n" , let start = ramadan end = cycleNthAfter False TG.Day (-1) eidalFitr in interval TTime.Open start end ) , ( "Rosch haSchana", "rosch ha\\-?schanah?" , let start = roshHashana end = cycleNthAfter False TG.Day 2 roshHashana in interval TTime.Open start end ) , ( "Schawuot", "sc?ha[vw]u'?oth?|shovuos" , let start = cycleNthAfter False TG.Day 50 passover end = cycleNthAfter False TG.Day 52 passover in interval TTime.Open start end ) , ( "Sukkot", "Laubhüttenfest|su[ck]{2}o[st]" , let start = cycleNthAfter False TG.Day 14 roshHashana end = cycleNthAfter False TG.Day 22 roshHashana in interval TTime.Open start end ) -- Does not account for leap years, so every 365 days. , ( "Parsi Neujahr", "parsi neujahr|jamshedi navroz" , predEveryNDaysFrom 365 (2020, 8, 16) ) , ( "Earth Hour", "earth hour|stunde der erde" , computeEarthHour ) , ( "Königstag", "königstag|koningsdag" , computeKingsDay ) ] ruleRelativeMinutesTotillbeforeIntegerHourofday :: Rule ruleRelativeMinutesTotillbeforeIntegerHourofday = Rule { name = "relative minutes to|till|before <integer> (hour-of-day)" , pattern = [ Predicate $ isIntegerBetween 1 59 , regex "vor" , Predicate isAnHourOfDay ] , prod = \tokens -> case tokens of (token:_:Token Time td:_) -> do n <- getIntValue token Token Time <$> minutesBefore n td _ -> Nothing } ruleQuarterTotillbeforeIntegerHourofday :: Rule ruleQuarterTotillbeforeIntegerHourofday = Rule { name = "quarter to|till|before <integer> (hour-of-day)" , pattern = [regex "vie?rtel vor" , Predicate isAnHourOfDay ] , prod = \tokens -> case tokens of (_:Token Time td:_) -> Token Time <$> minutesBefore 15 td _ -> Nothing } ruleHalfTotillbeforeIntegerHourofday :: Rule ruleHalfTotillbeforeIntegerHourofday = Rule { name = "half to|till|before <integer> (hour-of-day)" , pattern = [ regex "halbe? vor" , Predicate isAnHourOfDay ] , prod = \tokens -> case tokens of (_:Token Time td:_) -> Token Time <$> minutesBefore 30 td _ -> Nothing } ruleTheOrdinalCycleOfTime :: Rule ruleTheOrdinalCycleOfTime = Rule { name = "the <ordinal> <cycle> of <time>" , pattern = [ regex "der|die|das" , dimension Ordinal , dimension TimeGrain , regex "im|in|von" , dimension Time ] , prod = \tokens -> case tokens of (_:Token Ordinal od:Token TimeGrain grain:_:Token Time td:_) -> tt $ cycleNthAfter True grain (TOrdinal.value od - 1) td _ -> Nothing } ruleNthTimeOfTime2 :: Rule ruleNthTimeOfTime2 = Rule { name = "nth <time> of <time>" , pattern = [ regex "der|die|das" , dimension Ordinal , dimension Time , regex "im" , dimension Time ] , prod = \tokens -> case tokens of (_: Token Ordinal OrdinalData{TOrdinal.value = v}: Token Time td1: _: Token Time td2: _) -> Token Time . predNth (v - 1) False <$> intersect td2 td1 _ -> Nothing } ruleLastTime :: Rule ruleLastTime = Rule { name = "last <time>" , pattern = [ regex "letzten?|letztes" , Predicate isOkWithThisNext ] , prod = \tokens -> case tokens of (_:Token Time td:_) -> tt $ predNth (-1) False td _ -> Nothing } ruleDatetimeDatetimeInterval :: Rule ruleDatetimeDatetimeInterval = Rule { name = "<datetime> - <datetime> (interval)" , pattern = [ Predicate isNotLatent , regex "\\-|bis( zum)?|auf( den)?" , Predicate isNotLatent ] , prod = \tokens -> case tokens of (Token Time td1:_:Token Time td2:_) -> Token Time <$> interval TTime.Closed td1 td2 _ -> Nothing } ruleDateDateInterval :: Rule ruleDateDateInterval = Rule { name = "dd.(mm.)? - dd.mm.(yy[yy]?)? (interval)" , pattern = [ regex "(?:vo[nm]\\s+)?(10|20|30|31|[012]?[1-9])\\.?((?<=\\.)(?:10|11|12|0?[1-9])(?:\\.?))?" , regex "\\-|/|bis( zum)?|auf( den)?" , regex "(10|20|30|31|[012]?[1-9])\\.(10|11|12|0?[1-9])\\.?((?<=\\.)\\d{2,4})?" ] , prod = \tokens -> case tokens of (Token RegexMatch (GroupMatch (d1:"":_)): _: Token RegexMatch (GroupMatch (d2:m2:"":_)): _) -> do d1 <- parseInt d1 d2 <- parseInt d2 m2 <- parseInt m2 Token Time <$> interval TTime.Closed (monthDay m2 d1) (monthDay m2 d2) (Token RegexMatch (GroupMatch (d1:"":_)): _: Token RegexMatch (GroupMatch (d2:m2:y:_)): _) -> do d1 <- parseInt d1 d2 <- parseInt d2 m2 <- parseInt m2 y <- parseInt y Token Time <$> interval TTime.Closed (yearMonthDay y m2 d1) (yearMonthDay y m2 d2) (Token RegexMatch (GroupMatch (d1:m1:_)): _: Token RegexMatch (GroupMatch (d2:m2:"":_)): _) -> do d1 <- parseInt d1 d2 <- parseInt d2 m1 <- parseInt m1 m2 <- parseInt m2 Token Time <$> interval TTime.Closed (monthDay m1 d1) (monthDay m2 d2) (Token RegexMatch (GroupMatch (d1:m1:_)): _: Token RegexMatch (GroupMatch (d2:m2:y:_)): _) -> do d1 <- parseInt d1 d2 <- parseInt d2 m1 <- parseInt m1 m2 <- parseInt m2 y <- parseInt y Token Time <$> interval TTime.Closed (yearMonthDay y m1 d1) (yearMonthDay y m2 d2) _ -> Nothing } ruleEvening :: Rule ruleEvening = Rule { name = "evening" , pattern = [ regex "abends?" ] , prod = \_ -> let from = hour False 18 to = hour False 0 in Token Time . mkLatent . partOfDay <$> interval TTime.Open from to } ruleTheDayofmonthNonOrdinal :: Rule ruleTheDayofmonthNonOrdinal = Rule { name = "the <day-of-month> (non ordinal)" , pattern = [ regex "der" , Predicate $ isIntegerBetween 1 31 ] , prod = \tokens -> case tokens of (_:token:_) -> do v <- getIntValue token tt $ dayOfMonth v _ -> Nothing } ruleInDuration :: Rule ruleInDuration = Rule { name = "in <duration>" , pattern = [ regex "in" , dimension Duration ] , prod = \tokens -> case tokens of (_:Token Duration dd:_) -> tt $ inDuration dd _ -> Nothing } ruleLastCycleOfTime :: Rule ruleLastCycleOfTime = Rule { name = "last <cycle> of <time>" , pattern = [ regex "letzte(r|n|s)?" , dimension TimeGrain , regex "um|im" , dimension Time ] , prod = \tokens -> case tokens of (_:Token TimeGrain grain:_:Token Time td:_) -> tt $ cycleLastOf grain td _ -> Nothing } ruleFromDatetimeDatetimeInterval :: Rule ruleFromDatetimeDatetimeInterval = Rule { name = "from <datetime> - <datetime> (interval)" , pattern = [ regex "vo[nm]" , dimension Time , regex "\\-|bis( zum)?|auf( den)?" , dimension Time ] , prod = \tokens -> case tokens of (_:Token Time td1:_:Token Time td2:_) -> Token Time <$> interval TTime.Closed td1 td2 _ -> Nothing } ruleRelativeMinutesAfterpastIntegerHourofday :: Rule ruleRelativeMinutesAfterpastIntegerHourofday = Rule { name = "relative minutes after|past <integer> (hour-of-day)" , pattern = [ Predicate $ isIntegerBetween 1 59 , regex "nach" , Predicate isAnHourOfDay ] , prod = \tokens -> case tokens of (token: _: Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) is12H)}: _) -> do n <- getIntValue token tt $ hourMinute is12H hours n _ -> Nothing } ruleQuarterAfterpastIntegerHourofday :: Rule ruleQuarterAfterpastIntegerHourofday = Rule { name = "quarter after|past <integer> (hour-of-day)" , pattern = [ regex "vie?rtel nach" , Predicate isAnHourOfDay ] , prod = \tokens -> case tokens of (_: Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) is12H)}: _) -> tt $ hourMinute is12H hours 15 _ -> Nothing } ruleHalfAfterpastIntegerHourofday :: Rule ruleHalfAfterpastIntegerHourofday = Rule { name = "half after|past <integer> (hour-of-day)" , pattern = [ regex "halbe? nach" , Predicate isAnHourOfDay ] , prod = \tokens -> case tokens of (_: Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) is12H)}: _) -> tt $ hourMinute is12H hours 30 _ -> Nothing } ruleMonthDdddInterval :: Rule ruleMonthDdddInterval = Rule { name = "<month> dd-dd (interval)" , pattern = [ regex "([012]?\\d|30|31)(ter|\\.)?" , regex "\\-|bis( zum)?|auf( den)?" , regex "([012]?\\d|30|31)(ter|\\.)?" , Predicate isAMonth ] , prod = \tokens -> case tokens of (Token RegexMatch (GroupMatch (m1:_)): _: Token RegexMatch (GroupMatch (m2:_)): Token Time td: _) -> do v1 <- parseInt m1 v2 <- parseInt m2 from <- intersect (dayOfMonth v1) td to <- intersect (dayOfMonth v2) td Token Time <$> interval TTime.Closed from to _ -> Nothing } ruleTheCycleAfterTime :: Rule ruleTheCycleAfterTime = Rule { name = "the <cycle> after <time>" , pattern = [ regex "der" , dimension TimeGrain , regex "nach" , dimension Time ] , prod = \tokens -> case tokens of (_:Token TimeGrain grain:_:Token Time td:_) -> tt $ cycleNthAfter False grain 1 td _ -> Nothing } ruleTheCycleBeforeTime :: Rule ruleTheCycleBeforeTime = Rule { name = "the <cycle> before <time>" , pattern = [ regex "der" , dimension TimeGrain , regex "vor" , dimension Time ] , prod = \tokens -> case tokens of (_:Token TimeGrain grain:_:Token Time td:_) -> tt $ cycleNthAfter False grain (-1) td _ -> Nothing } ruleYearLatent2 :: Rule ruleYearLatent2 = Rule { name = "year (latent)" , pattern = [ Predicate $ isIntegerBetween 2101 10000 ] , prod = \tokens -> case tokens of (token:_) -> do v <- getIntValue token tt . mkLatent $ year v _ -> Nothing } ruleTimeAfterNext :: Rule ruleTimeAfterNext = Rule { name = "<time> after next" , pattern = [ dimension Time , regex "nach dem n(ä)chsten" ] , prod = \tokens -> case tokens of (Token Time td:_) -> tt $ predNth 1 True td _ -> Nothing } ruleTheIdesOfNamedmonth :: Rule ruleTheIdesOfNamedmonth = Rule { name = "the ides of <named-month>" , pattern = [ regex "die iden (des?)" , Predicate isAMonth ] , prod = \tokens -> case tokens of (_:Token Time td@TimeData {TTime.form = Just (TTime.Month m)}:_) -> Token Time <$> intersect (dayOfMonth $ if elem m [3, 5, 7, 10] then 15 else 13) td _ -> Nothing } ruleNoon :: Rule ruleNoon = Rule { name = "noon" , pattern = [ regex "mittags?|zw(ö)lf (uhr)?" ] , prod = \_ -> tt $ hour False 12 } ruleThisnextDayofweek :: Rule ruleThisnextDayofweek = Rule { name = "this|next <day-of-week>" , pattern = [ regex "diese(n|r)|kommenden|n(ä)chsten" , Predicate isADayOfWeek ] , prod = \tokens -> case tokens of (_:Token Time td:_) -> tt $ predNth 0 True td _ -> Nothing } ruleBetweenTimeofdayAndTimeofdayInterval :: Rule ruleBetweenTimeofdayAndTimeofdayInterval = Rule { name = "between <time-of-day> and <time-of-day> (interval)" , pattern = [ regex "zwischen" , Predicate isATimeOfDay , regex "und" , Predicate isATimeOfDay ] , prod = \tokens -> case tokens of (_:Token Time td1:_:Token Time td2:_) -> Token Time <$> interval TTime.Closed td1 td2 _ -> Nothing } ruleNextCycle :: Rule ruleNextCycle = Rule { name = "next <cycle>" , pattern = [ regex "n(ä)chste(r|n|s)?|kommende(r|n|s)?" , dimension TimeGrain ] , prod = \tokens -> case tokens of (_:Token TimeGrain grain:_) -> tt $ cycleNth grain 1 _ -> Nothing } ruleAfterNextCycle :: Rule ruleAfterNextCycle = Rule { name = "after next <cycle>" , pattern = [ regex "(ü)ber ?n(ä)chste[ns]?" , dimension TimeGrain ] , prod = \case (_:Token TimeGrain grain:_) -> tt $ cycleNth grain 2 _ -> Nothing } ruleTimeofdayApproximately :: Rule ruleTimeofdayApproximately = Rule { name = "<time-of-day> approximately" , pattern = [ Predicate isATimeOfDay , regex "ca\\.?|circa|zirka|ungef(ä)hr|(in )?etwa" ] , prod = \tokens -> case tokens of (Token Time td:_) -> tt $ notLatent td _ -> Nothing } ruleOnDate :: Rule ruleOnDate = Rule { name = "on <date>" , pattern = [ regex "am" , dimension Time ] , prod = \tokens -> case tokens of (_:x:_) -> Just x _ -> Nothing } ruleDurationFromNow :: Rule ruleDurationFromNow = Rule { name = "<duration> from now" , pattern = [ dimension Duration , regex "ab (heute|jetzt)" ] , prod = \tokens -> case tokens of (Token Duration dd:_) -> tt $ inDuration dd _ -> Nothing } ruleLunch :: Rule ruleLunch = Rule { name = "lunch" , pattern = [ regex "(am |zu )?mittags?" ] , prod = \_ -> let from = hour False 12 to = hour False 14 in Token Time . mkLatent . partOfDay <$> interval TTime.Open from to } ruleLastCycle :: Rule ruleLastCycle = Rule { name = "last <cycle>" , pattern = [ regex "letzte(r|n|s)?|vergangene(r|n|s)?" , dimension TimeGrain ] , prod = \tokens -> case tokens of (_:Token TimeGrain grain:_) -> tt . cycleNth grain $ - 1 _ -> Nothing } ruleAfternoon :: Rule ruleAfternoon = Rule { name = "afternoon" , pattern = [ regex "nach ?mittags?" ] , prod = \_ -> let from = hour False 12 to = hour False 19 in Token Time . mkLatent . partOfDay <$> interval TTime.Open from to } ruleTimeBeforeLast :: Rule ruleTimeBeforeLast = Rule { name = "<time> before last" , pattern = [ regex "vorletzten?|vor ?letztes?" , dimension Time ] , prod = \tokens -> case tokens of (_:Token Time td:_) -> tt $ predNth (-2) False td _ -> Nothing } ruleNamedmonthDayofmonthOrdinal :: Rule ruleNamedmonthDayofmonthOrdinal = Rule { name = "<named-month> <day-of-month> (ordinal)" , pattern = [ Predicate isAMonth , Predicate isDOMOrdinal ] , prod = \tokens -> case tokens of (Token Time td:token:_) -> Token Time <$> intersectDOM td token _ -> Nothing } ruleInduringThePartofday :: Rule ruleInduringThePartofday = Rule { name = "in|during the <part-of-day>" , pattern = [ regex "(in|an|am|w(ä)h?rend)( der| dem| des)?" , Predicate isAPartOfDay ] , prod = \tokens -> case tokens of (_:Token Time td:_) -> tt $ notLatent td _ -> Nothing } ruleHourofdayIntegerAsRelativeMinutes :: Rule ruleHourofdayIntegerAsRelativeMinutes = Rule { name = "<hour-of-day> <integer> (as relative minutes)" , pattern = [ Predicate $ and . sequence [isNotLatent, isAnHourOfDay] , Predicate $ isIntegerBetween 1 59 ] , prod = \tokens -> case tokens of (Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) is12H)}: token: _) -> do n <- getIntValue token tt $ hourMinute is12H hours n _ -> Nothing } ruleHourofdayQuarter :: Rule ruleHourofdayQuarter = Rule { name = "<hour-of-day> <quarter> (as relative minutes)" , pattern = [ Predicate isAnHourOfDay , regex "vie?rtel" ] , prod = \tokens -> case tokens of (Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) is12H)}:_) -> tt $ hourMinute is12H hours 15 _ -> Nothing } ruleHourofdayHalf :: Rule ruleHourofdayHalf = Rule { name = "<hour-of-day> <half> (as relative minutes)" , pattern = [ Predicate isAnHourOfDay , regex "halbe?" ] , prod = \tokens -> case tokens of (Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) is12H)}:_) -> tt $ hourMinute is12H hours 30 _ -> Nothing } ruleDayofmonthordinalNamedmonth :: Rule ruleDayofmonthordinalNamedmonth = Rule { name = "<day-of-month>(ordinal) <named-month>" , pattern = [ Predicate isDOMOrdinal , Predicate isAMonth ] , prod = \tokens -> case tokens of (token:Token Time td:_) -> Token Time <$> intersectDOM td token _ -> Nothing } ruleIntersectBy :: Rule ruleIntersectBy = Rule { name = "intersect by ','" , pattern = [ Predicate isNotLatent , regex ",( den|r)?" , Predicate isNotLatent ] , prod = \tokens -> case tokens of (Token Time td1:_:Token Time td2:_) -> Token Time <$> intersect td1 td2 _ -> Nothing } ruleNthTimeAfterTime :: Rule ruleNthTimeAfterTime = Rule { name = "nth <time> after <time>" , pattern = [ dimension Ordinal , dimension Time , regex "nach" , dimension Time ] , prod = \tokens -> case tokens of (Token Ordinal OrdinalData{TOrdinal.value = v}: Token Time td1: _: Token Time td2: _) -> tt $ predNthAfter (v - 1) td1 td2 _ -> Nothing } ruleMmdd :: Rule ruleMmdd = Rule { name = "mm/dd" , pattern = [ regex "(?:am\\s+)?([012]?[1-9]|10|20|30|31)\\.(10|11|12|0?[1-9])\\.?" ] , prod = \tokens -> case tokens of (Token RegexMatch (GroupMatch (m1:m2:_)):_) -> do d <- parseInt m1 m <- parseInt m2 tt $ monthDay m d _ -> Nothing } ruleAfterDuration :: Rule ruleAfterDuration = Rule { name = "after <duration>" , pattern = [ regex "nach" , dimension Duration ] , prod = \tokens -> case tokens of (_:Token Duration dd:_) -> tt $ inDuration dd _ -> Nothing } ruleTimeofdayLatent :: Rule ruleTimeofdayLatent = Rule { name = "time-of-day (latent)" , pattern = [ Predicate $ isIntegerBetween 0 23 ] , prod = \tokens -> case tokens of (token:_) -> do n <- getIntValue token tt . mkLatent $ hour (n < 12) n _ -> Nothing } ruleFromTimeofdayTimeofdayInterval :: Rule ruleFromTimeofdayTimeofdayInterval = Rule { name = "from <time-of-day> - <time-of-day> (interval)" , pattern = [ regex "(von|nach|ab|fr(ü)hestens (um)?)" , Predicate isATimeOfDay , regex "((noch|aber|jedoch)? vor)|\\-|bis" , Predicate isATimeOfDay ] , prod = \tokens -> case tokens of (_:Token Time td1:_:Token Time td2:_) -> Token Time <$> interval TTime.Closed td1 td2 _ -> Nothing } ruleExactlyTimeofday :: Rule ruleExactlyTimeofday = Rule { name = "exactly <time-of-day>" , pattern = [ regex "genau|exakt|p(ü)nktlich|punkt( um)?" , Predicate isATimeOfDay ] , prod = \tokens -> case tokens of (_:Token Time td:_) -> tt $ notLatent td _ -> Nothing } ruleBetweenDatetimeAndDatetimeInterval :: Rule ruleBetweenDatetimeAndDatetimeInterval = Rule { name = "between <datetime> and <datetime> (interval)" , pattern = [ regex "zwischen" , dimension Time , regex "und" , dimension Time ] , prod = \tokens -> case tokens of (_:Token Time td1:_:Token Time td2:_) -> Token Time <$> interval TTime.Closed td1 td2 _ -> Nothing } ruleDurationAgo :: Rule ruleDurationAgo = Rule { name = "<duration> ago" , pattern = [ regex "vor" , dimension Duration ] , prod = \tokens -> case tokens of (_:Token Duration dd:_) -> tt $ durationAgo dd _ -> Nothing } ruleByTheEndOfTime :: Rule ruleByTheEndOfTime = Rule { name = "by the end of <time>" , pattern = [ regex "bis (zum)? ende (von)?|(noch)? vor" , dimension Time ] , prod = \tokens -> case tokens of (_:Token Time td:_) -> Token Time <$> interval TTime.Closed td now _ -> Nothing } ruleAfterWork :: Rule ruleAfterWork = Rule { name = "after work" , pattern = [ regex "nach (der)? arbeit|(am)? feier ?abend" ] , prod = \_ -> do td2 <- interval TTime.Open (hour False 17) (hour False 21) Token Time . partOfDay <$> intersect today td2 } ruleLastNCycle :: Rule ruleLastNCycle = Rule { name = "last n <cycle>" , pattern = [ regex "letzten?|vergangenen?" , Predicate $ isIntegerBetween 1 9999 , dimension TimeGrain ] , prod = \tokens -> case tokens of (_:token:Token TimeGrain grain:_) -> do n <- getIntValue token tt $ cycleN True grain (- n) _ -> Nothing } ruleTimeofdaySharp :: Rule ruleTimeofdaySharp = Rule { name = "<time-of-day> sharp" , pattern = [ Predicate isATimeOfDay , regex "genau|exakt|p(ü)nktlich|punkt( um)?" ] , prod = \tokens -> case tokens of (Token Time td:_) -> tt $ notLatent td _ -> Nothing } ruleWithinDuration :: Rule ruleWithinDuration = Rule { name = "within <duration>" , pattern = [ regex "binnen|innerhalb( von)?" , dimension Duration ] , prod = \tokens -> case tokens of (_:Token Duration dd:_) -> Token Time <$> interval TTime.Open now (inDuration dd) _ -> Nothing } ruleMidnighteodendOfDay :: Rule ruleMidnighteodendOfDay = Rule { name = "midnight|EOD|end of day" , pattern = [ regex "mitternacht|EOD|tagesende|ende (des)? tag(es)?" ] , prod = \_ -> tt $ hour False 0 } ruleDayofmonthNonOrdinalNamedmonth :: Rule ruleDayofmonthNonOrdinalNamedmonth = Rule { name = "<day-of-month> (non ordinal) <named-month>" , pattern = [ Predicate isDOMInteger , Predicate isAMonth ] , prod = \tokens -> case tokens of (token:Token Time td:_) -> Token Time <$> intersectDOM td token _ -> Nothing } ruleIntersect :: Rule ruleIntersect = Rule { name = "intersect" , pattern = [ Predicate isNotLatent , Predicate isNotLatent ] , prod = \tokens -> case tokens of (Token Time td1:Token Time td2:_) -> Token Time <$> intersect td1 td2 _ -> Nothing } ruleDayOfWeekIntersectDuration :: Rule ruleDayOfWeekIntersectDuration = Rule { name = "<day-of-week> in <duration>" , pattern = [ Predicate isADayOfWeek , regex "(in|vor)" , dimension Duration ] , prod = \case (Token Time td:Token RegexMatch (GroupMatch (match:_)):Token Duration dd:_) -> case Text.toLower match of "vor" -> Token Time <$> intersect td (durationIntervalAgo dd) _ -> Token Time <$> intersect td (inDurationInterval dd) _ -> Nothing } ruleAboutTimeofday :: Rule ruleAboutTimeofday = Rule { name = "about <time-of-day>" , pattern = [ regex "so( um)?|(so |um |so um )?circa|zirka|ca\\.?|ungef(ä)hr|(etwa|gegen)( so| um| so um)?" , Predicate isATimeOfDay ] , prod = \tokens -> case tokens of (_:Token Time td:_) -> tt $ notLatent td _ -> Nothing } ruleUntilTimeofday :: Rule ruleUntilTimeofday = Rule { name = "until <time-of-day>" , pattern = [ regex "vor|bis( zu[rm]?)?|sp(ä)testens?" , dimension Time ] , prod = \tokens -> case tokens of (_:Token Time td:_) -> tt $ withDirection TTime.Before td _ -> Nothing } ruleUntilTimeofdayPostfix :: Rule ruleUntilTimeofdayPostfix = Rule { name = "<time-of-day> until" , pattern = [ dimension Time , regex "sp(ä)testens" ] , prod = \tokens -> case tokens of (Token Time td:_:_) -> tt $ withDirection TTime.Before td _ -> Nothing } ruleAtTimeofday :: Rule ruleAtTimeofday = Rule { name = "at <time-of-day>" , pattern = [ regex "um|@" , Predicate isATimeOfDay ] , prod = \tokens -> case tokens of (_:Token Time td:_) -> tt $ notLatent td _ -> Nothing } ruleNthTimeOfTime :: Rule ruleNthTimeOfTime = Rule { name = "nth <time> of <time>" , pattern = [ dimension Ordinal , dimension Time , regex "im" , dimension Time ] , prod = \tokens -> case tokens of (Token Ordinal OrdinalData{TOrdinal.value = v}: Token Time td1: _: Token Time td2: _) -> Token Time . predNth (v - 1) False <$> intersect td2 td1 _ -> Nothing } ruleTimePartofday :: Rule ruleTimePartofday = Rule { name = "<time> <part-of-day>" , pattern = [ dimension Time , Predicate isAPartOfDay ] , prod = \tokens -> case tokens of (Token Time td1:Token Time td2:_) -> Token Time <$> intersect td1 td2 _ -> Nothing } ruleWeekend :: Rule ruleWeekend = Rule { name = "week-end" , pattern = [ regex "wochen ?ende?" ] , prod = \_ -> tt $ mkOkForThisNext weekend } ruleNthTimeAfterTime2 :: Rule ruleNthTimeAfterTime2 = Rule { name = "nth <time> after <time>" , pattern = [ regex "der|das" , dimension Ordinal , dimension Time , regex "nach" , dimension Time ] , prod = \tokens -> case tokens of (_: Token Ordinal OrdinalData{TOrdinal.value = v}: Token Time td1: _: Token Time td2: _) -> tt $ predNthAfter (v - 1) td1 td2 _ -> Nothing } ruleNextTime :: Rule ruleNextTime = Rule { name = "next <time>" , pattern = [ regex "(n(ä)chste|kommende)[ns]?" , Predicate $ and . sequence [isNotLatent, isOkWithThisNext] ] , prod = \tokens -> case tokens of (_:Token Time td:_) -> tt $ predNth 0 True td _ -> Nothing } ruleOrdinalQuarterYear :: Rule ruleOrdinalQuarterYear = Rule { name = "<ordinal> quarter <year>" , pattern = [ dimension Ordinal , Predicate $ isGrain TG.Quarter , dimension Time ] , prod = \tokens -> case tokens of (Token Ordinal od:_:Token Time td:_) -> tt $ cycleNthAfter False TG.Quarter (TOrdinal.value od - 1) td _ -> Nothing } ruleYyyymmdd :: Rule ruleYyyymmdd = Rule { name = "yyyy-mm-dd" , pattern = [ regex "(\\d{2,4})-(1[0-2]|0?[1-9])-(3[01]|[12]\\d|0?[1-9])" ] , prod = \tokens -> case tokens of (Token RegexMatch (GroupMatch (m1:m2:m3:_)):_) -> do y <- parseInt m1 m <- parseInt m2 d <- parseInt m3 tt $ yearMonthDay y m d _ -> Nothing } ruleTheOrdinalCycleAfterTime :: Rule ruleTheOrdinalCycleAfterTime = Rule { name = "the <ordinal> <cycle> after <time>" , pattern = [ regex "the" , dimension Ordinal , dimension TimeGrain , regex "nach" , dimension Time ] , prod = \tokens -> case tokens of (_:Token Ordinal od:Token TimeGrain grain:_:Token Time td:_) -> tt $ cycleNthAfter True grain (TOrdinal.value od - 1) td _ -> Nothing } ruleIntersectByOfFromS :: Rule ruleIntersectByOfFromS = Rule { name = "intersect by 'of', 'from', 's" , pattern = [ Predicate isNotLatent , regex "von|der|im" , Predicate isNotLatent ] , prod = \tokens -> case tokens of (Token Time td1:_:Token Time td2:_) -> Token Time <$> intersect td1 td2 _ -> Nothing } ruleNextNCycle :: Rule ruleNextNCycle = Rule { name = "next n <cycle>" , pattern = [ regex "n(ä)chsten?|kommenden?" , Predicate $ isIntegerBetween 1 9999 , dimension TimeGrain ] , prod = \tokens -> case tokens of (_:token:Token TimeGrain grain:_) -> do v <- getIntValue token tt $ cycleN True grain v _ -> Nothing } ruleADuration :: Rule ruleADuration = Rule { name = "a <duration>" , pattern = [ regex "(in )?eine?(r|n)?" , dimension Duration ] , prod = \tokens -> case tokens of (_:Token Duration dd:_) -> tt $ inDuration dd _ -> Nothing } ruleMorning :: Rule ruleMorning = Rule { name = "morning" , pattern = [ regex "morgens|(in der )?fr(ü)h|vor ?mittags?|am morgen" ] , prod = \_ -> let from = hour False 3 to = hour False 12 in Token Time . mkLatent . partOfDay <$> interval TTime.Open from to } ruleThisPartofday :: Rule ruleThisPartofday = Rule { name = "this <part-of-day>" , pattern = [ regex "diesen?|dieses|heute" , Predicate isAPartOfDay ] , prod = \tokens -> case tokens of (_:Token Time td:_) -> Token Time . partOfDay <$> intersect today td _ -> Nothing } ruleThisCycle :: Rule ruleThisCycle = Rule { name = "this <cycle>" , pattern = [ regex "diese(r|n|s)?|kommende(r|n|s)?" , dimension TimeGrain ] , prod = \tokens -> case tokens of (_:Token TimeGrain grain:_) -> tt $ cycleNth grain 0 _ -> Nothing } ruleThisTime :: Rule ruleThisTime = Rule { name = "this <time>" , pattern = [ regex "diese(n|r|s)?|(im )?laufenden" , Predicate isOkWithThisNext ] , prod = \tokens -> case tokens of (_:Token Time td:_) -> tt $ predNth 0 False td _ -> Nothing } ruleDurationHence :: Rule ruleDurationHence = Rule { name = "<duration> hence" , pattern = [ dimension Duration , regex "hence" ] , prod = \tokens -> case tokens of (Token Duration dd:_) -> tt $ inDuration dd _ -> Nothing } ruleDayofmonthNonOrdinalOfNamedmonth :: Rule ruleDayofmonthNonOrdinalOfNamedmonth = Rule { name = "<day-of-month> (non ordinal) of <named-month>" , pattern = [ Predicate isDOMInteger , regex "vom|von" , Predicate isAMonth ] , prod = \tokens -> case tokens of (token:_:Token Time td:_) -> Token Time <$> intersectDOM td token _ -> Nothing } ruleAfterLunch :: Rule ruleAfterLunch = Rule { name = "after lunch" , pattern = [ regex "nach dem mittagessen|nachmittags?" ] , prod = \_ -> do td2 <- interval TTime.Open (hour False 13) (hour False 17) Token Time . partOfDay <$> intersect today td2 } ruleOnANamedday :: Rule ruleOnANamedday = Rule { name = "on a named-day" , pattern = [ regex "an einem" , Predicate isADayOfWeek ] , prod = \tokens -> case tokens of (_:x:_) -> Just x _ -> Nothing } ruleYearLatent :: Rule ruleYearLatent = Rule { name = "year (latent)" , pattern = [ Predicate $ isIntegerBetween 25 999 ] , prod = \tokens -> case tokens of (token:_) -> do y <- getIntValue token tt . mkLatent $ year y _ -> Nothing } ruleAfterTimeofday :: Rule ruleAfterTimeofday = Rule { name = "after <time-of-day>" , pattern = [ regex "nach|ab|fr(ü)he?stens" , dimension Time ] , prod = \tokens -> case tokens of (_:Token Time td:_) -> tt $ withDirection TTime.After td _ -> Nothing } ruleAfterTimeofdayPostfix :: Rule ruleAfterTimeofdayPostfix = Rule { name = "<time-of-day> after" , pattern = [ dimension Time , regex "fr(ü)he?stens" ] , prod = \tokens -> case tokens of (Token Time td:_:_) -> tt $ withDirection TTime.After td _ -> Nothing } ruleNight :: Rule ruleNight = Rule { name = "night" , pattern = [ regex "nachts?" ] , prod = \_ -> let from = hour False 0 to = hour False 4 in Token Time . mkLatent . partOfDay <$> interval TTime.Open from to } ruleDayofmonthOrdinal :: Rule ruleDayofmonthOrdinal = Rule { name = "<day-of-month> (ordinal)" , pattern = [ Predicate isDOMOrdinal ] , prod = \tokens -> case tokens of (Token Ordinal OrdinalData{TOrdinal.value = v}:_) -> tt $ dayOfMonth v _ -> Nothing } ruleHalfIntegerGermanStyleHourofday :: Rule ruleHalfIntegerGermanStyleHourofday = Rule { name = "half <integer> (german style hour-of-day)" , pattern = [ regex "halb" , Predicate isAnHourOfDay ] , prod = \tokens -> case tokens of (_:Token Time td:_) -> Token Time <$> minutesBefore 30 td _ -> Nothing } ruleOrdinalCycleAfterTime :: Rule ruleOrdinalCycleAfterTime = Rule { name = "<ordinal> <cycle> after <time>" , pattern = [ dimension Ordinal , dimension TimeGrain , regex "nach" , dimension Time ] , prod = \tokens -> case tokens of (Token Ordinal od:Token TimeGrain grain:_:Token Time td:_) -> tt $ cycleNthAfter True grain (TOrdinal.value od - 1) td _ -> Nothing } ruleOrdinalCycleOfTime :: Rule ruleOrdinalCycleOfTime = Rule { name = "<ordinal> <cycle> of <time>" , pattern = [ dimension Ordinal , dimension TimeGrain , regex "im|in|von" , dimension Time ] , prod = \tokens -> case tokens of (Token Ordinal od:Token TimeGrain grain:_:Token Time td:_) -> tt $ cycleNthAfter True grain (TOrdinal.value od - 1) td _ -> Nothing } ruleAfterNextTime :: Rule ruleAfterNextTime = Rule { name = "after next <time>" , pattern = [ regex "(ü)ber ?n(ä)chste[ns]?" , dimension Time ] , prod = \tokens -> case tokens of (_:Token Time td:_) -> tt $ predNth 1 True td _ -> Nothing } ruleHhmm :: Rule ruleHhmm = Rule { name = "hh:mm" , pattern = [ regex "((?:[01]?\\d)|(?:2[0-3]))[:.h]([0-5]\\d)(?:uhr|h)?" ] , prod = \tokens -> case tokens of (Token RegexMatch (GroupMatch (m1:m2:_)):_) -> do h <- parseInt m1 m <- parseInt m2 tt $ hourMinute False h m _ -> Nothing } ruleTonight :: Rule ruleTonight = Rule { name = "tonight" , pattern = [ regex "heute? (am)? abends?" ] , prod = \_ -> do td2 <- interval TTime.Open (hour False 18) (hour False 0) Token Time . partOfDay <$> intersect today td2 } ruleYear :: Rule ruleYear = Rule { name = "year" , pattern = [ Predicate $ isIntegerBetween 1000 2100 ] , prod = \tokens -> case tokens of (token:_) -> do y <- getIntValue token tt $ year y _ -> Nothing } ruleNamedmonthDayofmonthNonOrdinal :: Rule ruleNamedmonthDayofmonthNonOrdinal = Rule { name = "<named-month> <day-of-month> (non ordinal)" , pattern = [ Predicate isAMonth , Predicate isDOMInteger ] , prod = \tokens -> case tokens of (Token Time td:token:_) -> Token Time <$> intersectDOM td token _ -> Nothing } ruleHhmmMilitary :: Rule ruleHhmmMilitary = Rule { name = "hhmm (military)" , pattern = [ regex "((?:[01]?\\d)|(?:2[0-3]))([0-5]\\d)" ] , prod = \tokens -> case tokens of (Token RegexMatch (GroupMatch (h:m:_)):_) -> do hh <- parseInt h mm <- parseInt m tt . mkLatent $ hourMinute False hh mm _ -> Nothing } ruleAbsorptionOfAfterNamedDay :: Rule ruleAbsorptionOfAfterNamedDay = Rule { name = "absorption of , after named day" , pattern = [ Predicate isADayOfWeek , regex "," ] , prod = \tokens -> case tokens of (x:_) -> Just x _ -> Nothing } ruleLastDayofweekOfTime :: Rule ruleLastDayofweekOfTime = Rule { name = "last <day-of-week> of <time>" , pattern = [ regex "letzte(r|n|s)?" , Predicate isADayOfWeek , regex "[ui]m" , dimension Time ] , prod = \tokens -> case tokens of (_:Token Time td1:_:Token Time td2:_) -> tt $ predLastOf td1 td2 _ -> Nothing } ruleHhmmMilitaryAmpm :: Rule ruleHhmmMilitaryAmpm = Rule { name = "hhmm (military) am|pm" , pattern = [ regex "((?:1[012]|0?\\d))([0-5]\\d)" , regex "([ap])\\.?m\\.?(?:[\\s'\"-_{}\\[\\]()]|$)" ] , prod = \tokens -> case tokens of (Token RegexMatch (GroupMatch (hh:mm:_)):Token RegexMatch (GroupMatch (ap:_)):_) -> do h <- parseInt hh m <- parseInt mm tt . timeOfDayAMPM (Text.toLower ap == "a") $ hourMinute True h m _ -> Nothing } ruleTimeofdayTimeofdayInterval :: Rule ruleTimeofdayTimeofdayInterval = Rule { name = "<time-of-day> - <time-of-day> (interval)" , pattern = [ Predicate $ and . sequence [isNotLatent, isATimeOfDay] , regex "\\-|bis" , Predicate isATimeOfDay ] , prod = \tokens -> case tokens of (Token Time td1:_:Token Time td2:_) -> Token Time <$> interval TTime.Closed td1 td2 _ -> Nothing } ruleTimeofdayTimeofdayInterval2 :: Rule ruleTimeofdayTimeofdayInterval2 = Rule { name = "<time-of-day> - <time-of-day> (interval)" , pattern = [ Predicate isATimeOfDay , regex "\\-|/|bis" , Predicate $ and . sequence [isNotLatent, isATimeOfDay] ] , prod = \tokens -> case tokens of (Token Time td1:_:Token Time td2:_) -> Token Time <$> interval TTime.Closed td1 td2 _ -> Nothing } ruleDurationAfterTime :: Rule ruleDurationAfterTime = Rule { name = "<duration> after <time>" , pattern = [ dimension Duration , regex "nach" , dimension Time ] , prod = \tokens -> case tokens of (Token Duration dd:_:Token Time td:_) -> tt $ durationAfter dd td _ -> Nothing } ruleOrdinalQuarter :: Rule ruleOrdinalQuarter = Rule { name = "<ordinal> quarter" , pattern = [ dimension Ordinal , Predicate $ isGrain TG.Quarter ] , prod = \tokens -> case tokens of (Token Ordinal OrdinalData{TOrdinal.value = v}:_) -> tt . cycleNthAfter False TG.Quarter (v - 1) $ cycleNth TG.Year 0 _ -> Nothing } ruleTheDayofmonthOrdinal :: Rule ruleTheDayofmonthOrdinal = Rule { name = "the <day-of-month> (ordinal)" , pattern = [ regex "der" , Predicate isDOMOrdinal ] , prod = \tokens -> case tokens of (_:Token Ordinal OrdinalData{TOrdinal.value = v}:_) -> tt $ dayOfMonth v _ -> Nothing } ruleDurationBeforeTime :: Rule ruleDurationBeforeTime = Rule { name = "<duration> before <time>" , pattern = [ dimension Duration , regex "vor" , dimension Time ] , prod = \tokens -> case tokens of (Token Duration dd:_:Token Time td:_) -> tt $ durationBefore dd td _ -> Nothing } rulePartofdayOfTime :: Rule rulePartofdayOfTime = Rule { name = "<part-of-day> of <time>" , pattern = [ Predicate isAPartOfDay , regex "des|von|vom|am" , dimension Time ] , prod = \tokens -> case tokens of (Token Time td1:_:Token Time td2:_) -> Token Time <$> intersect td1 td2 _ -> Nothing } ruleMmddyyyy :: Rule ruleMmddyyyy = Rule { name = "mm/dd/yyyy" , pattern = [ regex "([012]?[1-9]|10|20|30|31)\\.(0?[1-9]|10|11|12)\\.(\\d{2,4})" ] , prod = \tokens -> case tokens of (Token RegexMatch (GroupMatch (m1:m2:m3:_)):_) -> do y <- parseInt m3 m <- parseInt m2 d <- parseInt m1 tt $ yearMonthDay y m d _ -> Nothing } ruleTimeofdayOclock :: Rule ruleTimeofdayOclock = Rule { name = "<time-of-day> o'clock" , pattern = [ Predicate isATimeOfDay , regex "uhr|h(?:[\\s'\"-_{}\\[\\]()]|$)" ] , prod = \tokens -> case tokens of (Token Time td:_) -> tt $ notLatent td _ -> Nothing } ruleDayofmonthordinalNamedmonthYear :: Rule ruleDayofmonthordinalNamedmonthYear = Rule { name = "<day-of-month>(ordinal) <named-month> year" , pattern = [ Predicate isDOMOrdinal , Predicate isAMonth , regex "(\\d{2,4})" ] , prod = \tokens -> case tokens of (token: Token Time td: Token RegexMatch (GroupMatch (match:_)): _) -> do n <- parseInt match dom <- intersectDOM td token Token Time <$> intersect dom (year n) _ -> Nothing } ruleTimezone :: Rule ruleTimezone = Rule { name = "<time> timezone" , pattern = [ Predicate $ and . sequence [isNotLatent, isATimeOfDay] , regex "\\b(YEKT|YEKST|YAKT|YAKST|WITA|WIT|WIB|WGT|WGST|WFT|WET|WEST|WAT|WAST|VUT|VLAT|VLAST|VET|UZT|UYT|UYST|UTC|ULAT|TVT|TMT|TLT|TKT|TJT|TFT|TAHT|SST|SRT|SGT|SCT|SBT|SAST|SAMT|RET|PYT|PYST|PWT|PST|PONT|PMST|PMDT|PKT|PHT|PHOT|PGT|PETT|PETST|PET|PDT|OMST|OMSST|NZST|NZDT|NUT|NST|NPT|NOVT|NOVST|NFT|NDT|NCT|MYT|MVT|MUT|MST|MSK|MSD|MMT|MHT|MDT|MAWT|MART|MAGT|MAGST|LINT|LHST|LHDT|KUYT|KST|KRAT|KRAST|KGT|JST|IST|IRST|IRKT|IRKST|IRDT|IOT|IDT|ICT|HOVT|HKT|GYT|GST|GMT|GILT|GFT|GET|GAMT|GALT|FNT|FKT|FKST|FJT|FJST|EST|EGT|EGST|EET|EEST|EDT|ECT|EAT|EAST|EASST|DAVT|ChST|CXT|CVT|CST|COT|CLT|CLST|CKT|CHAST|CHADT|CET|CEST|CDT|CCT|CAT|CAST|BTT|BST|BRT|BRST|BOT|BNT|AZT|AZST|AZOT|AZOST|AWST|AWDT|AST|ART|AQTT|ANAT|ANAST|AMT|AMST|ALMT|AKST|AKDT|AFT|AEST|AEDT|ADT|ACST|ACDT)\\b" ] , prod = \tokens -> case tokens of (Token Time td: Token RegexMatch (GroupMatch (tz:_)): _) -> Token Time <$> inTimezone (Text.toUpper tz) td _ -> Nothing } rules :: [Rule] rules = [ ruleADuration , ruleAboutTimeofday , ruleAbsorptionOfAfterNamedDay , ruleAfterDuration , ruleAfterLunch , ruleAfterNextTime , ruleAfterTimeofday , ruleAfterTimeofdayPostfix , ruleAfterWork , ruleAfternoon , ruleAtTimeofday , ruleBetweenDatetimeAndDatetimeInterval , ruleBetweenTimeofdayAndTimeofdayInterval , ruleByTheEndOfTime , ruleDatetimeDatetimeInterval , ruleDateDateInterval , ruleDayofmonthNonOrdinalNamedmonth , ruleDayofmonthNonOrdinalOfNamedmonth , ruleDayofmonthOrdinal , ruleDayofmonthordinalNamedmonth , ruleDayofmonthordinalNamedmonthYear , ruleDurationAfterTime , ruleDurationAgo , ruleDurationBeforeTime , ruleDurationFromNow , ruleDurationHence , ruleEvening , ruleExactlyTimeofday , ruleFromDatetimeDatetimeInterval , ruleFromTimeofdayTimeofdayInterval , ruleHalfIntegerGermanStyleHourofday , ruleHhmm , ruleHhmmMilitary , ruleHhmmMilitaryAmpm , ruleHourofdayIntegerAsRelativeMinutes , ruleInDuration , ruleInduringThePartofday , ruleIntersect , ruleIntersectBy , ruleIntersectByOfFromS , ruleDayOfWeekIntersectDuration , ruleLastCycle , ruleLastCycleOfTime , ruleLastDayofweekOfTime , ruleLastNCycle , ruleLastTime , ruleLunch , ruleMidnighteodendOfDay , ruleMmdd , ruleMmddyyyy , ruleMonthDdddInterval , ruleMorning , ruleNamedmonthDayofmonthNonOrdinal , ruleNamedmonthDayofmonthOrdinal , ruleNextCycle , ruleAfterNextCycle , ruleNextNCycle , ruleNextTime , ruleNight , ruleNoon , ruleNthTimeAfterTime , ruleNthTimeAfterTime2 , ruleNthTimeOfTime , ruleNthTimeOfTime2 , ruleOnANamedday , ruleOnDate , ruleOrdinalCycleAfterTime , ruleOrdinalCycleOfTime , ruleOrdinalQuarter , ruleOrdinalQuarterYear , rulePartofdayOfTime , ruleRelativeMinutesAfterpastIntegerHourofday , ruleRelativeMinutesTotillbeforeIntegerHourofday , ruleTheCycleAfterTime , ruleTheCycleBeforeTime , ruleTheDayofmonthNonOrdinal , ruleTheDayofmonthOrdinal , ruleTheIdesOfNamedmonth , ruleTheOrdinalCycleAfterTime , ruleTheOrdinalCycleOfTime , ruleThisCycle , ruleThisPartofday , ruleThisTime , ruleThisnextDayofweek , ruleTimeAfterNext , ruleTimeBeforeLast , ruleTimePartofday , ruleTimeofdayApproximately , ruleTimeofdayLatent , ruleTimeofdayOclock , ruleTimeofdaySharp , ruleTimeofdayTimeofdayInterval , ruleTimeofdayTimeofdayInterval2 , ruleTonight , ruleUntilTimeofday , ruleUntilTimeofdayPostfix , ruleWeekend , ruleWithinDuration , ruleYear , ruleYearLatent , ruleYearLatent2 , ruleYyyymmdd , ruleQuarterTotillbeforeIntegerHourofday , ruleHalfTotillbeforeIntegerHourofday , ruleQuarterAfterpastIntegerHourofday , ruleHalfAfterpastIntegerHourofday , ruleHourofdayQuarter , ruleHourofdayHalf , ruleTimezone ] ++ ruleInstants ++ ruleDaysOfWeek ++ ruleMonths ++ ruleSeasons ++ ruleHolidays ++ ruleComputedHolidays ++ ruleComputedHolidays'