content: -- 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 OverloadedStrings #-}
  {-# LANGUAGE NoRebindableSyntax #-}
  
  module Duckling.Time.HR.Rules
    ( rules
    ) where
  
  import Data.Maybe
  import Data.String
  import Data.Text (Text)
  import Prelude
  import qualified Data.Text as Text
  
  import Duckling.Dimensions.Types
  import Duckling.Duration.Helpers (isGrain)
  import Duckling.Numeral.Helpers (parseInt)
  import Duckling.Numeral.Types (NumeralData(..))
  import Duckling.Ordinal.Types (OrdinalData(..))
  import Duckling.Regex.Types
  import Duckling.Time.Helpers
  import Duckling.Time.Types (TimeData(..))
  import Duckling.Types
  import qualified Duckling.Numeral.Types as TNumeral
  import qualified Duckling.Ordinal.Types as TOrdinal
  import qualified Duckling.Time.Types as TTime
  import qualified Duckling.TimeGrain.Types as TG
  
  ruleDaysOfWeek :: [Rule]
  ruleDaysOfWeek = mkRuleDaysOfWeek
    [ ( "Monday"   , "ponedjelja?ka?|pon\\.?"                )
    , ( "Tuesday"  , "utora?ka?|uto?\\.?"                    )
    , ( "Wednesday", "srijed(a|e|u)|sri\\.?"                 )
    , ( "Thursday" , "(č|c)etvrta?ka?|(č|c)et\\.?" )
    , ( "Friday"   , "peta?ka?|pet\\.?"                      )
    , ( "Saturday" , "subot(a|e|u)|sub?\\.?"                 )
    , ( "Sunday"   , "nedjelj(a|e|u)|ned\\.?"                )
    ]
  
  ruleMonths :: [Rule]
  ruleMonths = mkRuleMonths
    [ ( "January"  , "sije(c|č)a?nj(a|u)?|januar(a|u)?|jan\\.?|sij?\\.?|prv(i|a|o(ga?)?)"      )
    , ( "February" , "(ve)?lja(c|č)(a|e|i)|februar(a|u)?|feb\\.?|ve(lj)?\\.?|drug(i|a|o(ga?)?)")
    , ( "March"    , "o(z|ž)uja?k(a|u)?|mart(a|u)?|mar\\.?|o(z|ž)u?\\.?|tre(c|ć)(i|a|e(ga?)?)" )
    , ( "April"    , "trava?nj(a|u)?|april(a|u)?|apr\\.?|tra\\.?|(č|c)etvrt(i|a|o(ga?)?)"      )
    , ( "May"      , "sviba?nj(a|u)?|maj|svi\\.?|pet(i|a|o(ga?)?)"                             )
    , ( "June"     , "lipa?nj(a|u)?|jun(i|u|a)?|jun\\.?|lip?\\.?|(š|s)est(i|a|o(ga?)?)"        )
    , ( "July"     , "srpa?nj(a|u)?|jul(i|u|a)?|jul\\.?|srp\\.?|sedm(i|a|o(ga?)?)"             )
    , ( "August"   , "kolovoz(a|u)?|august(a|u)?|aug\\.?|kol\\.?|osm(i|a|o(ga?)?)"             )
    , ( "September", "ruja?n(a|u)?|septemba?r(a|u)?|sept?\\.?|ruj\\.?|devet(i|a|o(ga?)?)"      )
    , ( "October"  , "listopad(a|u)?|oktobar(a|u)?|okt\\.?|lis\\.?|deset(i|a|o(ga?)?)"         )
    , ( "November" , "studen(i|oga?|om)|novemba?r(a|u)?|nov\\.?|stu\\.?|jedanaest(i|a|o(ga?)?)")
    , ( "December" , "prosina?c(a|u)?|decemba?r(a|u)?|dec\\.?|pros?\\.?|dvanaest(i|a|o(ga?)?)" )
    ]
  
  ruleHolidays :: [Rule]
  ruleHolidays = mkRuleHolidays
    [ ( "new year's eve" , "star(a|u|e) godin(a|e|u)"             , monthDay 12 31     )
    , ( "new year's day" , "nov(a|u|e) godin(a|e|u)"              , monthDay 1 1       )
    , ( "christmas eve"  , "badnjaka?"                            , monthDay 12 24     )
    , ( "christmas"      , "(zi(c|ć)bo|bo(z|ž)i(c|ć))(a|u|ni|na)?", monthDay 12 25     )
    , ( "halloween day"  , "no(c|ć) vje(s|š)tica"                 , monthDay 10 31     )
    , ( "Mother's Day"   , "maj(c|č)in dan"                       , nthDOWOfMonth 2 7 5)
    , ( "Father's Day"   , "dan (o(c|č)eva|tata)"                 , nthDOWOfMonth 3 7 6)
    , ( "valentine's day", "valentinov(og?|a|)"                   , monthDay 2 14      )
    ]
  
  ruleSeasons :: [Rule]
  ruleSeasons = mkRuleSeasons
    [ ("summer", "ljet(os?|a)"     , monthDay 6 21 , monthDay 9 23 )
    , ("fall"  , "jesen(i|as)?"    , monthDay 9 23 , monthDay 12 21)
    , ("winter", "zim(a|e|us)?"    , monthDay 12 21, monthDay 3 20 )
    , ("spring", "prolje(c|ć)(e|a)", monthDay 3 20 , monthDay 6 21 )
    ]
  
  ruleHalfIntegerHrStyleHourofday :: Rule
  ruleHalfIntegerHrStyleHourofday = Rule
    { name = "half <integer> (HR style hour-of-day)"
    , pattern =
      [ regex "pol?a?"
      , Predicate isAnHourOfDay
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> Token Time <$> minutesBefore 30 td
        _ -> Nothing
    }
  
  ruleNumeralTotillbeforeIntegerHourofday :: Rule
  ruleNumeralTotillbeforeIntegerHourofday = Rule
    { name = "numeral to|till|before <integer> (hour-of-day)"
    , pattern =
      [ Predicate $ isIntegerBetween 1 59
      , regex "do"
      , 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 "(kvarata?|(c|č)etvrt|frtalj)\\s+do"
      , 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 "di pol?a?\\s+do"
      , Predicate isAnHourOfDay
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> Token Time <$> minutesBefore 30 td
        _ -> Nothing
    }
  
  ruleNumeralAfterpastHourofday :: Rule
  ruleNumeralAfterpastHourofday = Rule
    { name = "numeral after|past (hour-of-day)"
    , pattern =
      [ Predicate $ isIntegerBetween 1 59
      , regex "poslije|nakon"
      , Predicate isAnHourOfDay
      ]
    , prod = \tokens -> case tokens of
        (token:_:Token Time td:_) -> do
          n <- getIntValue token
          Token Time <$> minutesAfter n td
        _ -> Nothing
    }
  
  ruleQuarterAfterpastHourofday :: Rule
  ruleQuarterAfterpastHourofday = Rule
    { name = "quarter after|past (hour-of-day)"
    , pattern =
      [ regex "(kvarata?|(c|č)etvrt|frtalj)\\s+(poslije|nakon)"
      , Predicate isAnHourOfDay
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> Token Time <$> minutesAfter 15 td
        _ -> Nothing
    }
  
  ruleHalfAfterpastHourofday :: Rule
  ruleHalfAfterpastHourofday = Rule
    { name = "half after|past (hour-of-day)"
    , pattern =
      [ regex "(i pol?a?)\\s+(poslije|nakon)"
      , Predicate isAnHourOfDay
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> Token Time <$> minutesAfter 30 td
        _ -> Nothing
    }
  
  ruleHourofdayNumeral :: Rule
  ruleHourofdayNumeral = 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"
    , pattern =
      [ Predicate isAnHourOfDay
      , regex "kvarata?|(c|č)etvrt|frtalj"
      ]
    , 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"
    , pattern =
      [ Predicate isAnHourOfDay
      , regex "i pol?a?"
      ]
    , prod = \tokens -> case tokens of
        (Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) is12H)}:_) ->
          tt $ hourMinute is12H hours 30
        _ -> Nothing
    }
  
  ruleZaNumeralHourofday :: Rule
  ruleZaNumeralHourofday = Rule
    { name = "za <integer> (hour-of-day)"
    , pattern =
      [ regex "za"
      , Predicate $ isIntegerBetween 1 59
      , 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
    }
  
  ruleZaQuarterHourofday :: Rule
  ruleZaQuarterHourofday = Rule
    { name = "za quarter (hour-of-day)"
    , pattern =
      [ regex "za\\s+(kvarata?|(c|č)etvrt|frtalj)"
      , Predicate isAnHourOfDay
      ]
    , prod = \tokens -> case tokens of
        (_:
         Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) is12H)}:
         _) -> tt $ hourMinute is12H hours 15
        _ -> Nothing
    }
  
  ruleZaHalfHourofday :: Rule
  ruleZaHalfHourofday = Rule
    { name = "za half (hour-of-day)"
    , pattern =
      [ regex "za\\s+(i pol?a?)"
      , Predicate isAnHourOfDay
      ]
    , prod = \tokens -> case tokens of
        (_:
         Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) is12H)}:
         _) -> tt $ hourMinute is12H hours 30
        _ -> Nothing
    }
  
  ruleSinceTimeofday :: Rule
  ruleSinceTimeofday = Rule
    { name = "since <time-of-day>"
    , pattern =
      [ regex "od"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> tt $ withDirection TTime.After td
        _ -> Nothing
    }
  
  ruleLastTime :: Rule
  ruleLastTime = Rule
    { name = "last <time>"
    , pattern =
      [ regex "(prethodn(i|u|a|e|o(ga?)?)|pro(s|š)l(ih?|u|a|e|o(ga?)?))"
      , 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 "\\-"
      , Predicate isNotLatent
      ]
    , prod = \tokens -> case tokens of
        (Token Time td1:_:Token Time td2:_) ->
          Token Time <$> interval TTime.Closed td1 td2
        _ -> Nothing
    }
  
  ruleCycleAfterTime :: Rule
  ruleCycleAfterTime = Rule
    { name = "<cycle> after <time>"
    , pattern =
      [ dimension TimeGrain
      , regex "nakon"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (Token TimeGrain grain:_:Token Time td:_) ->
          tt $ cycleNthAfter False grain 1 td
        _ -> Nothing
    }
  
  ruleInDuration :: Rule
  ruleInDuration = Rule
    { name = "in <duration>"
    , pattern =
      [ regex "za"
      , dimension Duration
      ]
    , prod = \tokens -> case tokens of
        (_:Token Duration dd:_) -> tt $ inDuration dd
        _ -> Nothing
    }
  
  ruleNow :: Rule
  ruleNow = Rule
    { name = "now"
    , pattern =
      [ regex "((upravo|ov(aj?|og?|e|i))? ?sada?|trenutak|upravo|trenutno)|(ov(aj|og) trena?)"
      ]
    , prod = \_ -> tt now
    }
  
  ruleLastCycleOfTime :: Rule
  ruleLastCycleOfTime = Rule
    { name = "last <cycle> of <time>"
    , pattern =
      [ regex "zadnj(ih?|a|e(ga?)?)"
      , dimension TimeGrain
      , regex "u"
      , 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 "od|izme(dj|đ)u"
      , dimension Time
      , regex "\\-"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td1:_:Token Time td2:_) ->
          Token Time <$> interval TTime.Closed td1 td2
        _ -> Nothing
    }
  
  ruleMonthDdddInterval :: Rule
  ruleMonthDdddInterval = Rule
    { name = "<month> dd-dd (interval)"
    , pattern =
      [ Predicate isAMonth
      , regex "(3[01]|[12]\\d|0?[1-9])"
      , regex "\\-|do"
      , regex "(3[01]|[12]\\d|0?[1-9])"
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:
         Token RegexMatch (GroupMatch (m1:_)):
         _:
         Token RegexMatch (GroupMatch (m2:_)):
         _) -> do
          d1 <- parseInt m1
          d2 <- parseInt m2
          from <- intersect (dayOfMonth d1) td
          to <- intersect (dayOfMonth d2) td
          Token Time <$> interval TTime.Closed from to
        _ -> Nothing
    }
  
  ruleYearLatent2 :: Rule
  ruleYearLatent2 = Rule
    { name = "year (latent)"
    , pattern =
      [ Predicate $ isIntegerBetween 2101 10000
      ]
    , prod = \tokens -> case tokens of
        (token:_) -> do
          n <- getIntValue token
          tt . mkLatent $ year n
        _ -> Nothing
    }
  
  ruleTimeAfterNext :: Rule
  ruleTimeAfterNext = Rule
    { name = "<time> after next"
    , pattern =
      [ dimension Time
      , regex "nakon sljede(ć|c)(i|e|a)(ga?)?"
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:_) -> tt $ predNth 1 True td
        _ -> Nothing
    }
  
  ruleNoon :: Rule
  ruleNoon = Rule
    { name = "noon"
    , pattern =
      [ regex "(u )?podne(va)?"
      ]
    , prod = \_ -> tt $ hour False 12
    }
  
  ruleEarlyMorning :: Rule
  ruleEarlyMorning = Rule
    { name = "early morning"
    , pattern =
      [ regex "(rano( (u )?u?jutros?)?)|(u ran(im|e) jutarnj(im|e) sat(ima|e))"
      ]
    , prod = \_ -> Token Time . partOfDay . mkLatent <$>
        interval TTime.Open (hour False 3) (hour False 9)
    }
  
  ruleToday :: Rule
  ruleToday = Rule
    { name = "today"
    , pattern =
      [ regex "danas?|(dana(s|š)nj(i|eg) dana?) "
      ]
    , prod = \_ -> tt today
    }
  
  ruleThisnextDayofweek :: Rule
  ruleThisnextDayofweek = Rule
    { name = "this|next <day-of-week>"
    , pattern =
      [ regex "ov(aj?|og?|e)|sljede(c|ć)(i|u|a|e(ga?)?)"
      , 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 "od|izme(dj|đ)u"
      , Predicate isATimeOfDay
      , regex "do|i"
      , 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 "sljede(c|ć)(i|a|u|e(ga?)?)"
      , dimension TimeGrain
      ]
    , prod = \tokens -> case tokens of
        (_:Token TimeGrain grain:_) -> tt $ cycleNth grain 1
        _ -> Nothing
    }
  
  ruleTimeofdayApproximately :: Rule
  ruleTimeofdayApproximately = Rule
    { name = "<time-of-day> approximately"
    , pattern =
      [ Predicate isATimeOfDay
      , regex "(-?ak)"
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:_) -> tt $ notLatent td
        _ -> Nothing
    }
  
  ruleForDuration :: Rule
  ruleForDuration = Rule
    { name = "for <duration>"
    , pattern =
      [ regex "za( jo(s|š))?|u"
      , dimension Duration
      ]
    , prod = \tokens -> case tokens of
        (_:Token Duration dd:_) -> tt $ inDuration dd
        _ -> Nothing
    }
  
  ruleLastDayofweekTime :: Rule
  ruleLastDayofweekTime = Rule
    { name = "last <day-of-week> <time>"
    , pattern =
      [ regex "zadnj(ih?|a|e(ga?)?)"
      , Predicate isADayOfWeek
      , regex "u"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td1:_:Token Time td2:_) ->
          tt $ predLastOf td1 td2
        _ -> Nothing
    }
  
  ruleDurationFromNow :: Rule
  ruleDurationFromNow = Rule
    { name = "<duration> from now"
    , pattern =
      [ dimension Duration
      , regex "od (sada?|ovog trenutka|dana(s|š)nj(i|eg) dana?)"
      ]
    , prod = \tokens -> case tokens of
        (Token Duration dd:_) -> tt $ inDuration dd
        _ -> Nothing
    }
  
  ruleLunch :: Rule
  ruleLunch = Rule
    { name = "lunch"
    , pattern =
      [ regex "(((za )|(u vrijeme )) )?ru(c|č)a?k(a|om)?"
      ]
    , prod = \_ -> Token Time . partOfDay . mkLatent <$>
        interval TTime.Open (hour False 12) (hour False 14)
    }
  
  ruleLastCycle :: Rule
  ruleLastCycle = Rule
    { name = "last <cycle>"
    , pattern =
      [ regex "prethodn(i|a|e|u)|pro(s|š)l(i|a|e|u|o(ga?)?)"
      , dimension TimeGrain
      ]
    , prod = \tokens -> case tokens of
        (_:Token TimeGrain grain:_) -> tt . cycleNth grain $ - 1
        _ -> Nothing
    }
  
  ruleDdmm :: Rule
  ruleDdmm = Rule
    { name = "dd.mm"
    , pattern =
      [ regex "(3[01]|[12]\\d|0?[1-9])\\/(0?[1-9]|1[0-2])"
      ]
    , prod = \tokens -> case tokens of
        (Token RegexMatch (GroupMatch (dd:mm:_)):_) -> do
          d <- parseInt dd
          m <- parseInt mm
          tt $ monthDay m d
        _ -> Nothing
    }
  
  ruleAfternoon :: Rule
  ruleAfternoon = Rule
    { name = "afternoon"
    , pattern =
      [ regex "po(slije)?podne"
      ]
    , prod = \_ -> Token Time . partOfDay . mkLatent <$>
        interval TTime.Open (hour False 12) (hour False 20)
    }
  
  ruleTimeBeforeLast :: Rule
  ruleTimeBeforeLast = Rule
    { name = "<time> before last"
    , pattern =
      [ dimension Time
      , regex "prije (prethodn(e|o(ga?)?)|pro(s|š)l(ih?|u|a|e|o(ga?)?))"
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:_) ->
          tt $ predNth (-2) False td
        _ -> Nothing
    }
  
  ruleLateNight :: Rule
  ruleLateNight = Rule
    { name = "late night"
    , pattern =
      [ regex "(((u|po)\\s)?no(c|ć)(i|as|u)?|u?jutros?)"
      ]
    , prod = \_ -> Token Time . partOfDay . mkLatent <$>
        interval TTime.Open (hour False 0) (hour False 4)
    }
  
  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 "(u|tokom)"
      , Predicate isAPartOfDay
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> tt $ notLatent td
        _ -> 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 ","
      , 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 "poslije|nakon|iza"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (Token Ordinal od:Token Time td1:_:Token Time td2:_) ->
          tt $ predNthAfter (TOrdinal.value od - 1) td1 td2
        _ -> Nothing
    }
  
  ruleAfterDuration :: Rule
  ruleAfterDuration = Rule
    { name = "after <duration>"
    , pattern =
      [ regex "(nakon|poslije)( jo(s|š))?"
      , dimension Duration
      ]
    , prod = \tokens -> case tokens of
        (_:Token Duration dd:_) -> tt . withDirection TTime.After $ 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 True n
        _ -> Nothing
    }
  
  ruleDayofmonthOrdinalOfNamedmonth :: Rule
  ruleDayofmonthOrdinalOfNamedmonth = Rule
    { name = "<day-of-month> (ordinal) of <named-month>"
    , pattern =
      [ Predicate isDOMOrdinal
      , regex "of|in"
      , Predicate isAMonth
      ]
    , prod = \tokens -> case tokens of
        (token:_:Token Time td:_) -> Token Time <$> intersectDOM td token
        _ -> Nothing
    }
  
  ruleFromTimeofdayTimeofdayInterval :: Rule
  ruleFromTimeofdayTimeofdayInterval = Rule
    { name = "from <time-of-day> - <time-of-day> (interval)"
    , pattern =
      [ regex "od"
      , Predicate isATimeOfDay
      , regex "\\-"
      , 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 "to(c|č)no( u)?"
      , Predicate isATimeOfDay
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> tt $ notLatent td
        _ -> Nothing
    }
  
  ruleUNamedday :: Rule
  ruleUNamedday = Rule
    { name = "u <named-day>"
    , pattern =
      [ regex "u"
      , Predicate isADayOfWeek
      ]
    , prod = \tokens -> case tokens of
        (_:x:_) -> Just x
        _ -> Nothing
    }
  
  ruleBetweenDatetimeAndDatetimeInterval :: Rule
  ruleBetweenDatetimeAndDatetimeInterval = Rule
    { name = "between <datetime> and <datetime> (interval)"
    , pattern =
      [ regex "od|izme(dj|đ)u"
      , dimension Time
      , regex "do|i"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td1:_:Token Time td2:_) ->
          Token Time <$> interval TTime.Closed td1 td2
        _ -> Nothing
    }
  
  ruleByTheEndOfTime :: Rule
  ruleByTheEndOfTime = Rule
    { name = "by the end of <time>"
    , pattern =
      [ regex "(do )(kraja|isteka)? "
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> Token Time <$> interval TTime.Closed now td
        _ -> Nothing
    }
  
  ruleAfterWork :: Rule
  ruleAfterWork = Rule
    { name = "after work"
    , pattern =
      [ regex "poslije (posla|kraja radnog vremena)"
      ]
    , prod = \_ -> do
        td <- interval TTime.Open (hour False 17) (hour False 21)
        Token Time . partOfDay . notLatent <$> intersect today td
    }
  
  ruleLastDayofweekTime2 :: Rule
  ruleLastDayofweekTime2 = Rule
    { name = "last <day-of-week> <time>"
    , pattern =
      [ regex "zadnj(ih?|a|e(ga?)?)"
      , Predicate isADayOfWeek
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td1:Token Time td2:_) ->
          tt $ predLastOf td1 td2
        _ -> Nothing
    }
  
  ruleLastNCycle :: Rule
  ruleLastNCycle = Rule
    { name = "last n <cycle>"
    , pattern =
      [ regex "prethodn(ih?|a|e)|pro(s|š)l(a|e|ih?)"
      , 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
    }
  
  ruleWithinDuration :: Rule
  ruleWithinDuration = Rule
    { name = "within <duration>"
    , pattern =
      [ regex "(u|za )vrijeme"
      , 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 "(u )?pono(c|ć)i?|(the )?(EOD|((do )? kraja? dana))"
      ]
    , 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
    }
  
  ruleAboutTimeofday :: Rule
  ruleAboutTimeofday = Rule
    { name = "about <time-of-day>"
    , pattern =
      [ regex "(oko|cca|otprilike)"
      , 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 "((nekad|najkasnije) )?(prije|do)"
      , Predicate isATimeOfDay
      ]
    , 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 "u|@"
      , 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 "u"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (Token Ordinal od:Token Time td1:_:Token Time td2:_) -> Token Time .
          predNth (TOrdinal.value od - 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 td:Token Time pod:_) -> Token Time <$> intersect pod td
        _ -> Nothing
    }
  
  ruleWeekend :: Rule
  ruleWeekend = Rule
    { name = "week-end"
    , pattern =
      [ regex "(za )?vikenda?"
      ]
    , prod = \_ -> tt $ mkOkForThisNext weekend
    }
  
  rulePrijeDuration :: Rule
  rulePrijeDuration = Rule
    { name = "prije <duration>"
    , pattern =
      [ regex "prije"
      , dimension Duration
      ]
    , prod = \tokens -> case tokens of
        (_:Token Duration dd:_) -> tt $ durationAgo dd
        _ -> Nothing
    }
  
  ruleAboutDuration :: Rule
  ruleAboutDuration = Rule
    { name = "about <duration>"
    , pattern =
      [ regex "oko"
      , dimension Duration
      ]
    , prod = \tokens -> case tokens of
        (_:Token Duration dd:_) -> tt $ inDuration dd
        _ -> Nothing
    }
  
  ruleEomendOfMonth :: Rule
  ruleEomendOfMonth = Rule
    { name = "EOM|End of month"
    , pattern =
      [ regex "(the )?(EOM|(do )? kraja mjeseca)"
      ]
    , prod = \_ -> tt $ cycleNth TG.Month 1
    }
  
  ruleNameddayDayofmonthOrdinal :: Rule
  ruleNameddayDayofmonthOrdinal = Rule
    { name = "<named-day> <day-of-month> (ordinal)"
    , pattern =
      [ Predicate isADayOfWeek
      , Predicate isDOMOrdinal
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:token:_) -> Token Time <$> intersectDOM td token
        _ -> Nothing
    }
  
  ruleNextTime :: Rule
  ruleNextTime = Rule
    { name = "next <time>"
    , pattern =
      [ regex "sljede(c|ć)(i|u|a|e(ga?)?)"
      , 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:_:Token Time td:_) -> do
          n <- getIntValue token
          tt $ cycleNthAfter False TG.Quarter (n - 1) td
        _ -> Nothing
    }
  
  ruleYyyymmdd :: Rule
  ruleYyyymmdd = Rule
    { name = "yyyy-mm-dd"
    , pattern =
      [ regex "(\\d{2,4})-(0?[1-9]|1[0-2])-(3[01]|[12]\\d|0?[1-9])"
      ]
    , prod = \tokens -> case tokens of
        (Token RegexMatch (GroupMatch (yy:mm:dd:_)):_) -> do
          y <- parseInt yy
          m <- parseInt mm
          d <- parseInt dd
          tt $ yearMonthDay y m d
        _ -> Nothing
    }
  
  ruleNextNCycle :: Rule
  ruleNextNCycle = Rule
    { name = "next n <cycle>"
    , pattern =
      [ regex "(u )?(sljede(c|ć)(ih?|a|eg?))"
      , 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
    }
  
  ruleMorning :: Rule
  ruleMorning = Rule
    { name = "morning"
    , pattern =
      [ regex "(u )?u?jutros?"
      ]
    , prod = \_ -> Token Time . partOfDay . mkLatent <$>
        interval TTime.Open (hour False 4) (hour False 12)
    }
  
  ruleThisPartofday :: Rule
  ruleThisPartofday = Rule
    { name = "this <part-of-day>"
    , pattern =
      [ regex "ov(aj?|og?|i|e)"
      , Predicate isAPartOfDay
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> Token Time . partOfDay . notLatent <$>
          intersect today td
        _ -> Nothing
    }
  
  ruleThisCycle :: Rule
  ruleThisCycle = Rule
    { name = "this <cycle>"
    , pattern =
      [ regex "ov(aj?|og?|e|i)"
      , dimension TimeGrain
      ]
    , prod = \tokens -> case tokens of
        (_:Token TimeGrain grain:_) -> tt $ cycleNth grain 0
        _ -> Nothing
    }
  
  ruleUNamedmonth :: Rule
  ruleUNamedmonth = Rule
    { name = "u <named-month>"
    , pattern =
      [ regex "u"
      , Predicate isAMonth
      ]
    , prod = \tokens -> case tokens of
        (_:x:_) -> Just x
        _ -> Nothing
    }
  
  ruleThisTime :: Rule
  ruleThisTime = Rule
    { name = "this <time>"
    , pattern =
      [ regex "ov(aj?|og?|e)(sad)?"
      , Predicate isOkWithThisNext
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> tt $ predNth 0 False td
        _ -> Nothing
    }
  
  ruleDayofmonthNonOrdinalOfNamedmonth :: Rule
  ruleDayofmonthNonOrdinalOfNamedmonth = Rule
    { name = "<day-of-month> (non ordinal) of <named-month>"
    , pattern =
      [ Predicate isDOMInteger
      , regex "of|in"
      , Predicate isAMonth
      ]
    , prod = \tokens -> case tokens of
        (token:_:Token Time td:_) -> Token Time <$> intersectDOM td token
        _ -> Nothing
    }
  
  ruleHhmmMilitaryPrefixedWithMToAvoidAmbiguityWithYears :: Rule
  ruleHhmmMilitaryPrefixedWithMToAvoidAmbiguityWithYears = Rule
    { name = "hhmm (military, prefixed with m to avoid ambiguity with years)"
    , pattern =
      [ regex "m((?:[01]?\\d)|(?:2[0-3]))([0-5]\\d)"
      ]
    , prod = \tokens -> case tokens of
        (Token RegexMatch (GroupMatch (hh:mm:_)):_) -> do
          h <- parseInt hh
          m <- parseInt mm
          tt . timeOfDayAMPM False $ hourMinute False h m
        _ -> Nothing
    }
  
  ruleDayBeforeYesterday :: Rule
  ruleDayBeforeYesterday = Rule
    { name = "day before yesterday"
    , pattern =
      [ regex "(prekju(c|č)er)"
      ]
    , prod = \_ -> tt . cycleNth TG.Day $ - 2
    }
  
  ruleAfterLunch :: Rule
  ruleAfterLunch = Rule
    { name = "after lunch"
    , pattern =
      [ regex "poslije ru(c|č)ka"
      ]
    , prod = \_ -> do
        td <- interval TTime.Open (hour False 13) (hour False 17)
        Token Time . partOfDay . notLatent <$> intersect today td
    }
  
  ruleYearLatent :: Rule
  ruleYearLatent = Rule
    { name = "year (latent)"
    , pattern =
      [ Predicate $ isIntegerBetween 25 999
      ]
    , prod = \tokens -> case tokens of
       (token:_) -> do
         n <- getIntValue token
         tt . mkLatent $ year n
       _ -> Nothing
    }
  
  ruleYesterday :: Rule
  ruleYesterday = Rule
    { name = "yesterday"
    , pattern =
      [ regex "(ju(c|č)er)"
      ]
    , prod = \_ -> tt . cycleNth TG.Day $ - 1
    }
  
  ruleAfterTimeofday :: Rule
  ruleAfterTimeofday = Rule
    { name = "after <time-of-day>"
    , pattern =
      [ regex "(nekad |najranije )?(prije|od)"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> tt $ withDirection TTime.After td
        _ -> Nothing
    }
  
  ruleDayofmonthOrdinal :: Rule
  ruleDayofmonthOrdinal = Rule
    { name = "<day-of-month>. (ordinal)"
    , pattern =
      [ Predicate isDOMOrdinal
      , regex "\\.|i|og"
      ]
    , prod = \tokens -> case tokens of
        (token:_) -> do
          v <- getIntValue token
          tt $ dayOfMonth v
        _ -> Nothing
    }
  
  ruleOrdinalCycleAfterTime :: Rule
  ruleOrdinalCycleAfterTime = Rule
    { name = "<ordinal> <cycle> after <time>"
    , pattern =
      [ dimension Ordinal
      , dimension TimeGrain
      , regex "nakon"
      , 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 "od|u"
      , 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 "nakon sljede(ć|c)(i|e|a)(ga?)?"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> tt $ predNth 1 True td
        _ -> Nothing
    }
  
  ruleDayAfterTomorrow :: Rule
  ruleDayAfterTomorrow = Rule
    { name = "day after tomorrow"
    , pattern =
      [ regex "(preko?sutra)"
      ]
    , prod = \_ -> tt $ cycleNth TG.Day 2
    }
  
  ruleHhmm :: Rule
  ruleHhmm = Rule
    { name = "hh:mm"
    , pattern =
      [ regex "((?:[01]?\\d)|(?:2[0-3]))[(:.\\si]+([0-5]\\d)"
      ]
    , prod = \tokens -> case tokens of
        (Token RegexMatch (GroupMatch (hh:mm:_)):_) -> do
          h <- parseInt hh
          m <- parseInt mm
          tt $ hourMinute True h m
        _ -> Nothing
    }
  
  ruleTonight :: Rule
  ruleTonight = Rule
    { name = "tonight"
    , pattern =
      [ regex "(na)?ve(c|č)er(as)?"
      ]
    , prod = \_ -> do
        evening <- interval TTime.Open (hour False 18) (hour False 0)
        Token Time . partOfDay . notLatent <$> intersect today evening
    }
  
  ruleBeforeLasttime :: Rule
  ruleBeforeLasttime = Rule
    { name = "before last<time>"
    , pattern =
      [ regex "prije (prethodn(i|u|a|e|o(ga?)?)|pro(s|š)l(ih?|a|e|o(ga?)?))"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> tt $ predNth (-2) False td
        _ -> Nothing
    }
  
  ruleTimezone :: Rule
  ruleTimezone = Rule
    { name = "<time> timezone"
    , pattern =
      [ Predicate $ and . sequence [isNotLatent, isGrainFinerThan TG.Day]
      , 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
    }
  
  ruleYear :: Rule
  ruleYear = Rule
    { name = "year"
    , pattern =
      [ Predicate $ isIntegerBetween 1000 2100
      ]
    , prod = \tokens -> case tokens of
        (token:_) -> do
          n <- getIntValue token
          tt $ year n
        _ -> 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
    }
  
  ruleAbsorptionOfAfterNamedDay :: Rule
  ruleAbsorptionOfAfterNamedDay = Rule
    { name = "absorption of , after named day"
    , pattern =
      [ Predicate isADayOfWeek
      , regex ","
      ]
    , prod = \tokens -> case tokens of
        (x:_) -> Just x
        _ -> Nothing
    }
  
  ruleByTime :: Rule
  ruleByTime = Rule
    { name = "by <time>"
    , pattern =
      [ regex "do"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> Token Time <$> interval TTime.Open now td
        _ -> Nothing
    }
  
  ruleCycleBeforeTime :: Rule
  ruleCycleBeforeTime = Rule
    { name = "<cycle> before <time>"
    , pattern =
      [ dimension TimeGrain
      , regex "prije"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (Token TimeGrain grain:_:Token Time td:_) ->
          tt $ cycleNthAfter False grain (-1) td
        _ -> Nothing
    }
  
  ruleDdmmyyyy :: Rule
  ruleDdmmyyyy = Rule
    { name = "dd/mm/yyyy"
    , pattern =
      [ regex "(3[01]|[12]\\d|0?[1-9])[\\/](0?[1-9]|1[0-2])[\\/](\\d{2,4})"
      ]
    , prod = \tokens -> case tokens of
        (Token RegexMatch (GroupMatch (dd:mm:yy:_)):_) -> do
          y <- parseInt yy
          m <- parseInt mm
          d <- parseInt dd
          tt $ yearMonthDay y m d
        _ -> Nothing
    }
  
  ruleTimeofdayTimeofdayInterval :: Rule
  ruleTimeofdayTimeofdayInterval = Rule
    { name = "<time-of-day> - <time-of-day> (interval)"
    , pattern =
      [ Predicate $ and . sequence [isNotLatent, isATimeOfDay]
      , regex "\\-|:"
      , Predicate 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 "nakon|poslije|od"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (Token Duration dd:_:Token Time td:_) ->
          tt $ durationAfter dd td
        _ -> Nothing
    }
  
  ruleEveningnight :: Rule
  ruleEveningnight = Rule
    { name = "evening|night"
    , pattern =
      [ regex "(na)?ve(c|č)er(i|as)?"
      ]
    , prod = \_ -> Token Time . mkOkForThisNext . partOfDay . mkLatent <$>
        interval TTime.Open (hour False 18) (hour False 0)
    }
  
  ruleOrdinalQuarter :: Rule
  ruleOrdinalQuarter = Rule
    { name = "<ordinal> quarter"
    , pattern =
      [ dimension Ordinal
      , Predicate $ isGrain TG.Quarter
      ]
    , prod = \tokens -> case tokens of
        (token:_) -> do
          n <- getIntValue token
          tt . cycleNthAfter True TG.Quarter (n - 1) $
            cycleNth TG.Year 0
        _ -> Nothing
    }
  
  ruleDayBeforeDayBeforeYesterday :: Rule
  ruleDayBeforeDayBeforeYesterday = Rule
    { name = "day before day before yesterday"
    , pattern =
      [ regex "(prek\\s?prekju(c|č)er)"
      ]
    , prod = \_ -> tt . cycleNth TG.Day $ - 3
    }
  
  ruleDurationBeforeTime :: Rule
  ruleDurationBeforeTime = Rule
    { name = "<duration> before <time>"
    , pattern =
      [ dimension Duration
      , regex "prije"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (Token Duration dd:_:Token Time td:_) ->
          tt $ durationBefore dd td
        _ -> Nothing
    }
  
  ruleEoyendOfYear :: Rule
  ruleEoyendOfYear = Rule
    { name = "EOY|End of year"
    , pattern =
      [ regex "(the )?(EOY|(do )? kraja? godine)"
      ]
    , prod = \_ -> tt $ cycleNth TG.Year 1
    }
  
  ruleTomorrow :: Rule
  ruleTomorrow = Rule
    { name = "tomorrow"
    , pattern =
      [ regex "(sutra(dan)?)"
      ]
    , prod = \_ -> tt $ cycleNth TG.Day 1
    }
  
  ruleTimeofdayOclock :: Rule
  ruleTimeofdayOclock = Rule
    { name = "<time-of-day> o'clock"
    , pattern =
      [ Predicate isATimeOfDay
      , regex "sat(i|a)?"
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:_) -> tt $ notLatent td
        _ -> Nothing
    }
  
  ruleYear2 :: Rule
  ruleYear2 = Rule
    { name = "year"
    , pattern =
      [ Predicate $ isOrdinalBetween 1000 2100
      , regex "\\.|e"
      ]
    , prod = \tokens -> case tokens of
        (token:_) -> do
          v <- getIntValue token
          tt $ year v
        _ -> 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
          intVal <- parseInt match
          dom <- intersectDOM td token
          Token Time <$> intersect dom (year intVal)
        _ -> Nothing
    }
  
  ruleHhmmss :: Rule
  ruleHhmmss = Rule
    { name = "hh:mm:ss"
    , pattern =
      [ regex "((?:[01]?\\d)|(?:2[0-3]))[:.]([0-5]\\d)[:.]([0-5]\\d)"
      ]
    , prod = \tokens -> case tokens of
        (Token RegexMatch (GroupMatch (hh:mm:ss:_)):_) -> do
          h <- parseInt hh
          m <- parseInt mm
          s <- parseInt ss
          tt $ hourMinuteSecond True h m s
        _ -> Nothing
    }
  
  rules :: [Rule]
  rules =
    [ ruleAboutDuration
    , ruleAboutTimeofday
    , ruleAbsorptionOfAfterNamedDay
    , ruleAfterDuration
    , ruleAfterLunch
    , ruleAfterNextTime
    , ruleAfterTimeofday
    , ruleAfterWork
    , ruleAfternoon
    , ruleAtTimeofday
    , ruleBeforeLasttime
    , ruleBetweenDatetimeAndDatetimeInterval
    , ruleBetweenTimeofdayAndTimeofdayInterval
    , ruleByTheEndOfTime
    , ruleByTime
    , ruleCycleAfterTime
    , ruleCycleBeforeTime
    , ruleDatetimeDatetimeInterval
    , ruleDayAfterTomorrow
    , ruleDayBeforeDayBeforeYesterday
    , ruleDayBeforeYesterday
    , ruleDayofmonthNonOrdinalNamedmonth
    , ruleDayofmonthNonOrdinalOfNamedmonth
    , ruleDayofmonthOrdinal
    , ruleDayofmonthOrdinalOfNamedmonth
    , ruleDayofmonthordinalNamedmonth
    , ruleDayofmonthordinalNamedmonthYear
    , ruleDdmm
    , ruleDdmmyyyy
    , ruleDurationAfterTime
    , ruleDurationBeforeTime
    , ruleDurationFromNow
    , ruleEarlyMorning
    , ruleEomendOfMonth
    , ruleEoyendOfYear
    , ruleEveningnight
    , ruleExactlyTimeofday
    , ruleForDuration
    , ruleFromDatetimeDatetimeInterval
    , ruleFromTimeofdayTimeofdayInterval
    , ruleHalfIntegerHrStyleHourofday
    , ruleHhmm
    , ruleHhmmMilitaryPrefixedWithMToAvoidAmbiguityWithYears
    , ruleHhmmss
    , ruleInDuration
    , ruleInduringThePartofday
    , ruleIntersect
    , ruleIntersectBy
    , ruleLastCycle
    , ruleLastCycleOfTime
    , ruleLastDayofweekTime
    , ruleLastDayofweekTime2
    , ruleLastNCycle
    , ruleLastTime
    , ruleLateNight
    , ruleLunch
    , ruleMidnighteodendOfDay
    , ruleMonthDdddInterval
    , ruleMorning
    , ruleNameddayDayofmonthOrdinal
    , ruleNamedmonthDayofmonthNonOrdinal
    , ruleNamedmonthDayofmonthOrdinal
    , ruleNextCycle
    , ruleNextNCycle
    , ruleNextTime
    , ruleNoon
    , ruleNow
    , ruleNthTimeAfterTime
    , ruleNthTimeOfTime
    , ruleOrdinalCycleAfterTime
    , ruleOrdinalCycleOfTime
    , ruleOrdinalQuarter
    , ruleOrdinalQuarterYear
    , rulePrijeDuration
    , ruleNumeralAfterpastHourofday
    , ruleQuarterAfterpastHourofday
    , ruleHalfAfterpastHourofday
    , ruleNumeralTotillbeforeIntegerHourofday
    , ruleQuarterTotillbeforeIntegerHourofday
    , ruleHalfTotillbeforeIntegerHourofday
    , ruleHourofdayNumeral
    , ruleHourofdayQuarter
    , ruleHourofdayHalf
    , ruleSinceTimeofday
    , ruleThisCycle
    , ruleThisPartofday
    , ruleThisTime
    , ruleThisnextDayofweek
    , ruleTimeAfterNext
    , ruleTimeBeforeLast
    , ruleTimePartofday
    , ruleTimezone
    , ruleTimeofdayApproximately
    , ruleTimeofdayLatent
    , ruleTimeofdayOclock
    , ruleTimeofdayTimeofdayInterval
    , ruleToday
    , ruleTomorrow
    , ruleTonight
    , ruleUNamedday
    , ruleUNamedmonth
    , ruleUntilTimeofday
    , ruleWeekend
    , ruleWithinDuration
    , ruleYear
    , ruleYear2
    , ruleYearLatent
    , ruleYearLatent2
    , ruleYesterday
    , ruleYyyymmdd
    , ruleZaNumeralHourofday
    , ruleZaQuarterHourofday
    , ruleZaHalfHourofday
    ]
    ++ ruleDaysOfWeek
    ++ ruleMonths
    ++ ruleHolidays
    ++ ruleSeasons
  