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 NoRebindableSyntax #-}
  {-# LANGUAGE OverloadedStrings #-}
  
  module Duckling.Time.NB.Rules
    ( rules
    ) where
  
  import Data.Text (Text)
  import Prelude
  
  import Duckling.Dimensions.Types
  import Duckling.Duration.Helpers (isGrain)
  import Duckling.Numeral.Helpers (parseInt)
  import Duckling.Ordinal.Types (OrdinalData(..))
  import Duckling.Regex.Types
  import Duckling.Time.Helpers
  import Duckling.Time.Types (TimeData(..))
  import Duckling.Types
  import qualified Data.Text as Text
  import qualified Duckling.Ordinal.Types as TOrdinal
  import qualified Duckling.Time.Types as TTime
  import qualified Duckling.TimeGrain.Types as TG
  
  ruleTheDayAfterTomorrow :: Rule
  ruleTheDayAfterTomorrow = Rule
    { name = "the day after tomorrow"
    , pattern =
      [ regex "i overimorgen|i overimorra"
      ]
    , prod = \_ -> tt $ cycleNth TG.Day 2
    }
  
  ruleRelativeMinutesTotillbeforeIntegerHourofday :: Rule
  ruleRelativeMinutesTotillbeforeIntegerHourofday = Rule
    { name = "relative minutes to|till|before <integer> (hour-of-day)"
    , pattern =
      [ Predicate $ isIntegerBetween 1 59
      , regex "på"
      , Predicate isAnHourOfDay
      ]
    , prod = \tokens -> case tokens of
        (token:_:Token Time td:_) -> do
          n <- getIntValue token
          Token Time <$> minutesBefore n td
        _ -> Nothing
    }
  
  ruleRelativeMinutesAfterpastIntegerHourofday :: Rule
  ruleRelativeMinutesAfterpastIntegerHourofday = Rule
    { name = "relative minutes after|past <integer> (hour-of-day)"
    , pattern =
      [ Predicate $ isIntegerBetween 1 59
      , regex "over"
      , Predicate isAnHourOfDay
      ]
    , prod = \tokens -> case tokens of
        (token:
         _:
         Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) _)}:
         _) -> do
          n <- getIntValue token
          tt $ hourMinute True hours n
        _ -> 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) _)}:
         token:
         _) -> do
          n <- getIntValue token
          tt $ hourMinute True hours n
        _ -> Nothing
    }
  
  ruleQuarterTotillbeforeIntegerHourofday :: Rule
  ruleQuarterTotillbeforeIntegerHourofday = Rule
    { name = "quarter to|till|before <integer> (hour-of-day)"
    , pattern =
      [ regex "(et)? ?(kvart)(er)? *på"
      , Predicate isAnHourOfDay
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> Token Time <$> minutesBefore 15 td
        _ -> Nothing
    }
  
  ruleQuarterAfterpastIntegerHourofday :: Rule
  ruleQuarterAfterpastIntegerHourofday = Rule
    { name = "quarter after|past <integer> (hour-of-day)"
    , pattern =
      [ regex "(et)? ?(kvart)(er)? *over"
      , Predicate isAnHourOfDay
      ]
    , prod = \tokens -> case tokens of
        (_:
         Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) _)}:
         _) -> tt $ hourMinute True hours 15
        _ -> Nothing
    }
  
  ruleHourofdayQuarter :: Rule
  ruleHourofdayQuarter = Rule
    { name = "<hour-of-day> quarter (as relative minutes)"
    , pattern =
      [ Predicate isAnHourOfDay
      , regex "(et)? ?(kvart)(er)?"
      ]
    , prod = \tokens -> case tokens of
        (Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) _)}:_) ->
          tt $ hourMinute True hours 15
        _ -> Nothing
    }
  
  ruleHalfTotillbeforeIntegerHourofday :: Rule
  ruleHalfTotillbeforeIntegerHourofday = Rule
    { name = "half to|till|before <integer> (hour-of-day)"
    , pattern =
      [ regex "halv time *på|halvtime *på|halv"
      , Predicate isAnHourOfDay
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> Token Time <$> minutesBefore 30 td
        _ -> Nothing
    }
  
  ruleHalfAfterpastIntegerHourofday :: Rule
  ruleHalfAfterpastIntegerHourofday = Rule
    { name = "half after|past <integer> (hour-of-day)"
    , pattern =
      [ regex "halv time *over"
      , Predicate isAnHourOfDay
      ]
    , prod = \tokens -> case tokens of
        (_:
         Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) _)}:
         _) -> tt $ hourMinute True hours 30
        _ -> Nothing
    }
  
  ruleHourofdayHalf :: Rule
  ruleHourofdayHalf = Rule
    { name = "<hour-of-day> half (as relative minutes)"
    , pattern =
      [ Predicate isAnHourOfDay
      , regex "halv time"
      ]
    , prod = \tokens -> case tokens of
        (Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) _)}:_) ->
          tt $ hourMinute True hours 30
        _ -> Nothing
    }
  
  ruleConstitutionDay :: Rule
  ruleConstitutionDay = Rule
    { name = "constitution day"
    , pattern =
      [ regex "grunnlovsdag(en)"
      ]
    , prod = \_ -> tt $ monthDay 5 17
    }
  
  ruleValentinesDay :: Rule
  ruleValentinesDay = Rule
    { name = "valentine's day"
    , pattern =
      [ regex "valentine'?s?( dag)?"
      ]
    , prod = \_ -> tt $ monthDay 2 14
    }
  
  ruleTheOrdinalCycleOfTime :: Rule
  ruleTheOrdinalCycleOfTime = Rule
    { name = "the <ordinal> <cycle> of <time>"
    , pattern =
      [ regex "den"
      , dimension Ordinal
      , dimension TimeGrain
      , regex "av|i|fra"
      , 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 "den"
      , dimension Ordinal
      , dimension Time
      , regex "af|i"
      , 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
    }
  
  ruleNewYearsDay :: Rule
  ruleNewYearsDay = Rule
    { name = "new year's day"
    , pattern =
      [ regex "nyttårsdag"
      ]
    , prod = \_ -> tt $ monthDay 1 1
    }
  
  ruleLastTime :: Rule
  ruleLastTime = Rule
    { name = "last <time>"
    , pattern =
      [ regex "(siste?|forrige|seneste)"
      , dimension Time
      ]
    , 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 "\\-|til|tilogmed"
      , Predicate isNotLatent
      ]
    , prod = \tokens -> case tokens of
        (Token Time td1:_:Token Time td2:_) ->
          Token Time <$> interval TTime.Closed td1 td2
        _ -> Nothing
    }
  
  ruleEvening :: Rule
  ruleEvening = Rule
    { name = "evening"
    , pattern =
      [ regex "kveld(en)?"
      ]
    , 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 "den"
      , Predicate isDOMInteger
      ]
    , prod = \tokens -> case tokens of
        (_:token:_) -> do
          v <- getIntValue token
          tt . mkLatent $ dayOfMonth v
        _ -> Nothing
    }
  
  ruleCycleAfterTime :: Rule
  ruleCycleAfterTime = Rule
    { name = "<cycle> after <time>"
    , pattern =
      [ dimension TimeGrain
      , regex "etter"
      , 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 "om"
      , dimension Duration
      ]
    , prod = \tokens -> case tokens of
        (_:Token Duration dd:_) ->
          tt $ inDuration dd
        _ -> Nothing
    }
  
  ruleNow :: Rule
  ruleNow = Rule
    { name = "now"
    , pattern =
      [ regex "akkurat nå|nå|(i )?dette øyeblikk"
      ]
    , prod = \_ -> tt now
    }
  
  ruleLastCycleOfTime :: Rule
  ruleLastCycleOfTime = Rule
    { name = "last <cycle> of <time>"
    , pattern =
      [ regex "siste"
      , dimension TimeGrain
      , regex "av|i"
      , 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 "fra"
      , dimension Time
      , regex "\\-|til|tilogmed"
      , 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 =
      [ regex "([012]?\\d|30|31)(ter|\\.)?"
      , regex "\\-|til"
      , regex "([012]?\\d|30|31)(ter|\\.)?"
      , Predicate isAMonth
      ]
    , prod = \tokens -> case tokens of
        (Token RegexMatch (GroupMatch (d1:_)):
         _:
         Token RegexMatch (GroupMatch (d2:_)):
         Token Time td:
         _) -> do
          dd1 <- parseInt d1
          dd2 <- parseInt d2
          dom1 <- intersect (dayOfMonth dd1) td
          dom2 <- intersect (dayOfMonth dd2) td
          Token Time <$> interval TTime.Closed dom1 dom2
        _ -> Nothing
    }
  
  ruleTheCycleAfterTime :: Rule
  ruleTheCycleAfterTime = Rule
    { name = "the <cycle> after <time>"
    , pattern =
      [ dimension TimeGrain
      , regex "(en|tet|et)? etter"
      , 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 =
      [ dimension TimeGrain
      , regex "(en|tet|et)? før"
      , 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
          n <- getIntValue token
          tt . mkLatent $ year n
        _ -> Nothing
    }
  
  ruleTimeAfterNext :: Rule
  ruleTimeAfterNext = Rule
    { name = "<time> after next"
    , pattern =
      [ regex "neste"
      , dimension Time
      , regex "igjen"
      ]
    , 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 "midten af"
      , 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 "middag|(kl(\\.|okken)?)? tolv"
      ]
    , prod = \_ -> tt $ hour False 12
    }
  
  ruleToday :: Rule
  ruleToday = Rule
    { name = "today"
    , pattern =
      [ regex "i dag|idag"
      ]
    , prod = \_ -> tt today
    }
  
  ruleThisnextDayofweek :: Rule
  ruleThisnextDayofweek = Rule
    { name = "this|next <day-of-week>"
    , pattern =
      [ regex "(kommende|neste)"
      , Predicate isADayOfWeek
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) ->
          tt $ predNth 0 True td
        _ -> Nothing
    }
  
  ruleTheDayBeforeYesterday :: Rule
  ruleTheDayBeforeYesterday = Rule
    { name = "the day before yesterday"
    , pattern =
      [ regex "i forigårs"
      ]
    , prod = \_ -> tt . cycleNth TG.Day $ - 2
    }
  
  ruleBetweenTimeofdayAndTimeofdayInterval :: Rule
  ruleBetweenTimeofdayAndTimeofdayInterval = Rule
    { name = "between <time-of-day> and <time-of-day> (interval)"
    , pattern =
      [ regex "mellom"
      , Predicate isATimeOfDay
      , regex "og"
      , 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 "neste|kommende"
      , dimension TimeGrain
      ]
    , prod = \tokens -> case tokens of
        (_:Token TimeGrain grain:_) ->
          tt $ cycleNth grain 1
        _ -> Nothing
    }
  
  ruleDaysOfWeek :: [Rule]
  ruleDaysOfWeek = mkRuleDaysOfWeek
    [ ( "Mandag"  , "mandag|man\\.?"    )
    , ( "Tirsdag" , "tirsdag|tirs?\\.?" )
    , ( "Onsdag"  , "onsdag|ons\\.?"    )
    , ( "Torsdag" , "torsdag|tors?\\.?" )
    , ( "Fredag"  , "fredag|fre\\.?"    )
    , ( "Lørdag"  , "lørdag|lør\\.?"    )
    , ( "Søndag"  , "søndag|søn\\.?"    )
    ]
  
  ruleMonths :: [Rule]
  ruleMonths = mkRuleMonths
    [ ( "Januar"   , "januar|jan\\.?"      )
    , ( "Februar"  , "februar|feb\\.?"     )
    , ( "Mars"     , "mars|mar\\.?"        )
    , ( "April"    , "april|apr\\.?"       )
    , ( "Mai"      , "mai"                 )
    , ( "Juni"     , "juni|jun\\.?"        )
    , ( "Juli"     , "juli|jul\\.?"        )
    , ( "August"   , "august|aug\\.?"      )
    , ( "September", "september|sept?\\.?" )
    , ( "Oktober"  , "oktober|okt\\.?"     )
    , ( "November" , "november|nov\\.?"    )
    , ( "Desember" , "desember|des\\.?"    )
    ]
  
  ruleTheCycleOfTime :: Rule
  ruleTheCycleOfTime = Rule
    { name = "the <cycle> of <time>"
    , pattern =
      [ dimension TimeGrain
      , regex "(en|tet|et)? av"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (Token TimeGrain grain:_:Token Time td:_) ->
          tt $ cycleNthAfter True grain 0 td
        _ -> Nothing
    }
  
  ruleTimeofdayApproximately :: Rule
  ruleTimeofdayApproximately = Rule
    { name = "<time-of-day> approximately"
    , pattern =
      [ Predicate isATimeOfDay
      , regex "(cirka|ca\\.|-?ish)"
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:_) -> tt $ notLatent td
        _ -> Nothing
    }
  
  ruleOnDate :: Rule
  ruleOnDate = Rule
    { name = "on <date>"
    , pattern =
      [ regex "den|på"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> tt $ notLatent td
        _ -> Nothing
    }
  
  ruleDurationFromNow :: Rule
  ruleDurationFromNow = Rule
    { name = "<duration> from now"
    , pattern =
      [ dimension Duration
      , regex "fra (i dag|idag|nå)"
      ]
    , prod = \tokens -> case tokens of
        (Token Duration dd:_) ->
          tt $ inDuration dd
        _ -> Nothing
    }
  
  ruleLunch :: Rule
  ruleLunch = Rule
    { name = "lunch"
    , pattern =
      [ regex "(til )?middag"
      ]
    , 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 "siste?|seneste|forrige"
      , 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]\\b|1[0-2])"
      ]
    , prod = \tokens -> case tokens of
        (Token RegexMatch (GroupMatch (dd:mm:_)):_) -> do
          m <- parseInt mm
          d <- parseInt dd
          tt $ monthDay m d
        _ -> Nothing
    }
  
  ruleAfternoon :: Rule
  ruleAfternoon = Rule
    { name = "afternoon"
    , pattern =
      [ regex "ettermiddag(en)?"
      ]
    , 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 "siste"
      , dimension Time
      , regex "igjen"
      ]
    , 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
    }
  
  ruleChristmasEve :: Rule
  ruleChristmasEve = Rule
    { name = "christmas eve"
    , pattern =
      [ regex "julaften?"
      ]
    , prod = \_ -> tt $ monthDay 12 24
    }
  
  ruleInduringThePartofday :: Rule
  ruleInduringThePartofday = Rule
    { name = "in|during the <part-of-day>"
    , pattern =
      [ regex "om|i"
      , 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 "etter"
      , 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 "etter"
      , 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
          v <- getIntValue token
          tt . mkLatent $ hour False v
        _ -> Nothing
    }
  
  ruleDayofmonthOrdinalOfNamedmonth :: Rule
  ruleDayofmonthOrdinalOfNamedmonth = Rule
    { name = "<day-of-month> (ordinal) of <named-month>"
    , pattern =
      [ Predicate isDOMOrdinal
      , regex "av|i"
      , 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 "(etter|fra)"
      , Predicate isATimeOfDay
      , regex "((men )?før)|\\-|tilogmed|til"
      , 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 "presis( kl.| klokken | klokka)?"
      , Predicate isATimeOfDay
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> tt $ notLatent td
        _ -> Nothing
    }
  
  ruleInduringThePartofday2 :: Rule
  ruleInduringThePartofday2 = Rule
    { name = "in|during the <part-of-day>"
    , pattern =
      [ regex "om|i"
      , Predicate isAPartOfDay
      , regex "en|ten"
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) ->
          tt $ notLatent td
        _ -> Nothing
    }
  
  ruleSeason :: Rule
  ruleSeason = Rule
    { name = "season"
    , pattern =
      [ regex "sommer(en)"
      ]
    , prod = \_ ->
        let from = monthDay 6 21
            to = monthDay 9 23
        in Token Time <$> interval TTime.Open from to
    }
  
  ruleBetweenDatetimeAndDatetimeInterval :: Rule
  ruleBetweenDatetimeAndDatetimeInterval = Rule
    { name = "between <datetime> and <datetime> (interval)"
    , pattern =
      [ regex "mellom"
      , dimension Time
      , regex "og"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td1:_:Token Time td2:_) ->
          Token Time <$> interval TTime.Closed td1 td2
        _ -> Nothing
    }
  
  ruleNewYearsEve :: Rule
  ruleNewYearsEve = Rule
    { name = "new year's eve"
    , pattern =
      [ regex "nyttårsaften?"
      ]
    , prod = \_ -> tt $ monthDay 12 31
    }
  
  ruleDurationAgo :: Rule
  ruleDurationAgo = Rule
    { name = "<duration> ago"
    , pattern =
      [ dimension Duration
      , regex "siden"
      ]
    , prod = \tokens -> case tokens of
        (Token Duration dd:_) ->
          tt $ durationAgo dd
        _ -> Nothing
    }
  
  ruleByTheEndOfTime :: Rule
  ruleByTheEndOfTime = Rule
    { name = "by the end of <time>"
    , pattern =
      [ regex "i slutten av"
      , 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 "etter jobb"
      ]
    , 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 "siste?|seneste|forrige"
      , 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
    }
  
  ruleChristmasDays :: Rule
  ruleChristmasDays = Rule
    { name = "christmas days"
    , pattern =
      [ regex "romjul(a|en)"
      ]
    , prod = \_ ->
        Token Time <$> interval TTime.Open (monthDay 12 24) (monthDay 12 30)
    }
  
  ruleTimeofdaySharp :: Rule
  ruleTimeofdaySharp = Rule
    { name = "<time-of-day> sharp"
    , pattern =
      [ Predicate isATimeOfDay
      , regex "(sharp|presis)"
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:_) -> tt $ notLatent td
        _ -> Nothing
    }
  
  ruleWithinDuration :: Rule
  ruleWithinDuration = Rule
    { name = "within <duration>"
    , pattern =
      [ regex "innenfor"
      , 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 "midnatt|EOD"
      ]
    , 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 "(omkring|cirka|ca\\.)( kl\\.| klokken | klokka)?"
      , 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 "(engang )?innen|før|opptil"
      , dimension Time
      ]
    , 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 "klokken|klokka|kl.|@"
      , 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 "av|i"
      , 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 td1:Token Time td2:_) ->
          Token Time <$> intersect td1 td2
        _ -> Nothing
    }
  
  ruleWeekend :: Rule
  ruleWeekend = Rule
    { name = "week-end"
    , pattern =
      [ regex "((week(\\s|-)?end)|helg)(en|a)?"
      ]
    , prod = \_ -> tt weekend
    }
  
  ruleEomendOfMonth :: Rule
  ruleEomendOfMonth = Rule
    { name = "EOM|End of month"
    , pattern =
      [ regex "EOM"
      ]
    , prod = \_ -> tt $ cycleNth TG.Month 1
    }
  
  ruleLastYear :: Rule
  ruleLastYear = Rule
    { name = "Last year"
    , pattern =
      [ regex "i fjor"
      ]
    , prod = \_ -> tt . cycleNth TG.Year $ - 1
    }
  
  ruleNthTimeAfterTime2 :: Rule
  ruleNthTimeAfterTime2 = Rule
    { name = "nth <time> after <time>"
    , pattern =
      [ regex "den"
      , dimension Ordinal
      , dimension Time
      , regex "etter"
      , 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
    }
  
  ruleNextTime :: Rule
  ruleNextTime = Rule
    { name = "next <time>"
    , pattern =
      [ regex "neste|kommende"
      , Predicate isNotLatent
      ]
    , 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{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
    }
  
  ruleTheOrdinalCycleAfterTime :: Rule
  ruleTheOrdinalCycleAfterTime = Rule
    { name = "the <ordinal> <cycle> after <time>"
    , pattern =
      [ regex "den"
      , dimension Ordinal
      , dimension TimeGrain
      , regex "etter"
      , 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 "i"
      , 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 "neste"
      , 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 "(om )?en|ett|én|et"
      , dimension Duration
      ]
    , prod = \tokens -> case tokens of
        (_:Token Duration dd:_) ->
          tt $ inDuration dd
        _ -> Nothing
    }
  
  ruleMorning :: Rule
  ruleMorning = Rule
    { name = "morning"
    , pattern =
      [ regex "morgen(en)?|morran"
      ]
    , prod = \_ ->
        let from = hour False 4
            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 "i|denne"
      , 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 "denne|dette|i|nåværende"
      , dimension TimeGrain
      ]
    , prod = \tokens -> case tokens of
        (_:Token TimeGrain grain:_) ->
          tt $ cycleNth grain 0
        _ -> Nothing
    }
  
  ruleThisTime :: Rule
  ruleThisTime = Rule
    { name = "this <time>"
    , pattern =
      [ regex "(denne|dette|i|den her)"
      , dimension Time
      ]
    , 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 "av|i"
      , 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 "etter (frokost|middag|lunsj|lunch)"
      ]
    , 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 "på en"
      , 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
          n <- getIntValue token
          tt . mkLatent $ year n
        _ -> Nothing
    }
  
  ruleYesterday :: Rule
  ruleYesterday = Rule
    { name = "yesterday"
    , pattern =
      [ regex "i går|igår"
      ]
    , prod = \_ -> tt . cycleNth TG.Day $ - 1
    }
  
  ruleSeason2 :: Rule
  ruleSeason2 = Rule
    { name = "season"
    , pattern =
      [ regex "vinter(en)"
      ]
    , prod = \_ ->
        let from = monthDay 12 21
            to = monthDay 3 20
        in Token Time <$> interval TTime.Open from to
    }
  
  ruleAfterTimeofday :: Rule
  ruleAfterTimeofday = Rule
    { name = "after <time-of-day>"
    , pattern =
      [ regex "(engang )?etter"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) ->
          tt $ withDirection TTime.After td
        _ -> Nothing
    }
  
  ruleChristmas :: Rule
  ruleChristmas = Rule
    { name = "christmas"
    , pattern =
      [ regex "((1\\.?)|første)? ?juledag"
      ]
    , prod = \_ -> tt $ monthDay 12 25
    }
  
  ruleNight :: Rule
  ruleNight = Rule
    { name = "night"
    , pattern =
      [ regex "natt(en)?"
      ]
    , 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 . mkLatent $ dayOfMonth v
        _ -> Nothing
    }
  
  ruleOrdinalCycleAfterTime :: Rule
  ruleOrdinalCycleAfterTime = Rule
    { name = "<ordinal> <cycle> after <time>"
    , pattern =
      [ dimension Ordinal
      , dimension TimeGrain
      , regex "etter"
      , 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 "av|i|fra"
      , 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
    }
  
  ruleHhmm :: Rule
  ruleHhmm = Rule
    { name = "hh:mm"
    , pattern =
      [ regex "((?:[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 $ hourMinute False h m
        _ -> Nothing
    }
  
  rule2400 :: Rule
  rule2400 = Rule
    { name = "24:00"
    , pattern =
      [ regex "24[.:]00"
      ]
    , prod = \_ -> tt $ hourMinute False 24 00
    }
  
  ruleDmm :: Rule
  ruleDmm = Rule
    { name = "d/mm"
    , pattern =
      [ regex "([1-9])[\\/\\.\\-]([1-9]\\b|1[0-2]\\b)"
      ]
    , prod = \tokens -> case tokens of
        (Token RegexMatch (GroupMatch (dd:mm:_)):_) -> do
          d <- parseInt dd
          m <- parseInt mm
          tt $ monthDay m d
        _ -> Nothing
    }
  
  ruleDDm :: Rule
  ruleDDm = Rule
    { name = "dd/m"
    , pattern =
      [ regex "([0-2]\\d|3[01])[\\/\\.\\-]([1-9])"
      ]
    , prod = \tokens -> case tokens of
        (Token RegexMatch (GroupMatch (dd:mm:_)):_) -> do
          d <- parseInt dd
          m <- parseInt mm
          tt $ monthDay m d
        _ -> Nothing
    }
  
  ruleDdDotMm :: Rule
  ruleDdDotMm = Rule
    { name = "dd.mm"
    , pattern =
      [ regex "(2[5-9]|3[01])\\.(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
    }
  
  ruleTonight :: Rule
  ruleTonight = Rule
    { name = "tonight"
    , pattern =
      [ regex "i kveld"
      ]
    , 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
          n <- getIntValue token
          tt $ year n
        _ -> Nothing
    }
  
  ruleHalloweenDay :: Rule
  ruleHalloweenDay = Rule
    { name = "halloween day"
    , pattern =
      [ regex "hall?owe?en"
      ]
    , prod = \_ -> tt $ monthDay 10 31
    }
  
  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
    }
  
  ruleLastDayofweekOfTime :: Rule
  ruleLastDayofweekOfTime = Rule
    { name = "last <day-of-week> of <time>"
    , pattern =
      [ regex "siste"
      , Predicate isADayOfWeek
      , regex "av|i"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td1:_:Token Time td2:_) ->
          tt $ predLastOf td1 td2
        _ -> Nothing
    }
  
  ruleFathersDay :: Rule
  ruleFathersDay = Rule
    { name = "Father's Day"
    , pattern =
      [ regex "farsdag"
      ]
    , prod = \_ -> tt $ nthDOWOfMonth 2 7 11
    }
  
  ruleCycleBeforeTime :: Rule
  ruleCycleBeforeTime = Rule
    { name = "<cycle> before <time>"
    , pattern =
      [ dimension TimeGrain
      , regex "før"
      , 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 "\\-|tilogmed|til"
      , 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 "etter"
      , 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:_) -> do
          n <- getIntValue token
          tt . cycleNthAfter True TG.Quarter (n - 1) $
            cycleNth TG.Year 0
        _ -> Nothing
    }
  
  ruleTheDayofmonthOrdinal :: Rule
  ruleTheDayofmonthOrdinal = Rule
    { name = "the <day-of-month> (ordinal)"
    , pattern =
      [ regex "den"
      , 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 "før"
      , 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 "(en |ten )?den"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (Token Time td1:_:Token Time td2:_) ->
          Token Time <$> intersect td1 td2
        _ -> Nothing
    }
  
  ruleEoyendOfYear :: Rule
  ruleEoyendOfYear = Rule
    { name = "EOY|End of year"
    , pattern =
      [ regex "EOY"
      ]
    , prod = \_ -> tt $ cycleNth TG.Year 1
    }
  
  ruleTomorrow :: Rule
  ruleTomorrow = Rule
    { name = "tomorrow"
    , pattern =
      [ regex "i morgen|imorgen|i morra|imorra"
      ]
    , prod = \_ -> tt $ cycleNth TG.Day 1
    }
  
  ruleMothersDay :: Rule
  ruleMothersDay = Rule
    { name = "Mother's Day"
    , pattern =
      [ regex "morsdag"
      ]
    , prod = \_ -> tt $ nthDOWOfMonth 2 7 2
    }
  
  ruleTimeofdayOclock :: Rule
  ruleTimeofdayOclock = Rule
    { name = "<time-of-day> o'clock"
    , pattern =
      [ Predicate isATimeOfDay
      , regex "h"
      ]
    , 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
           y <- parseInt match
           dom <- intersectDOM td token
           Token Time <$> intersect dom (year y)
        _ -> 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 False h m s
        _ -> 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
    , ruleAfterTimeofday
    , ruleAfterWork
    , ruleAfternoon
    , ruleAtTimeofday
    , ruleBetweenDatetimeAndDatetimeInterval
    , ruleBetweenTimeofdayAndTimeofdayInterval
    , ruleByTheEndOfTime
    , ruleChristmas
    , ruleChristmasEve
    , ruleChristmasDays
    , ruleConstitutionDay
    , ruleCycleAfterTime
    , ruleCycleBeforeTime
    , ruleDatetimeDatetimeInterval
    , ruleDayofmonthNonOrdinalNamedmonth
    , ruleDayofmonthNonOrdinalOfNamedmonth
    , ruleDayofmonthOrdinal
    , ruleDayofmonthOrdinalOfNamedmonth
    , ruleDayofmonthordinalNamedmonth
    , ruleDayofmonthordinalNamedmonthYear
    , ruleDdmm
    , ruleDdmmyyyy
    , ruleDurationAfterTime
    , ruleDurationAgo
    , ruleDurationBeforeTime
    , ruleDurationFromNow
    , ruleEomendOfMonth
    , ruleEoyendOfYear
    , ruleEvening
    , ruleExactlyTimeofday
    , ruleFathersDay
    , ruleFromDatetimeDatetimeInterval
    , ruleFromTimeofdayTimeofdayInterval
    , ruleHalloweenDay
    , ruleHhmm
    , ruleHhmmss
    , ruleHourofdayIntegerAsRelativeMinutes
    , ruleHourofdayQuarter
    , ruleHourofdayHalf
    , ruleInDuration
    , ruleInduringThePartofday
    , ruleInduringThePartofday2
    , ruleIntersect
    , ruleIntersectBy
    , ruleIntersectByOfFromS
    , ruleLastCycle
    , ruleLastCycleOfTime
    , ruleLastDayofweekOfTime
    , ruleLastNCycle
    , ruleLastTime
    , ruleLastYear
    , ruleLunch
    , ruleMidnighteodendOfDay
    , ruleMonthDdddInterval
    , ruleMorning
    , ruleMothersDay
    , ruleNamedmonthDayofmonthNonOrdinal
    , ruleNamedmonthDayofmonthOrdinal
    , ruleNewYearsDay
    , ruleNewYearsEve
    , ruleNextCycle
    , ruleNextNCycle
    , ruleNextTime
    , ruleNight
    , ruleNoon
    , ruleNow
    , ruleNthTimeAfterTime
    , ruleNthTimeAfterTime2
    , ruleNthTimeOfTime
    , ruleNthTimeOfTime2
    , ruleOnANamedday
    , ruleOnDate
    , ruleOrdinalCycleAfterTime
    , ruleOrdinalCycleOfTime
    , ruleOrdinalQuarter
    , ruleOrdinalQuarterYear
    , rulePartofdayOfTime
    , ruleRelativeMinutesAfterpastIntegerHourofday
    , ruleRelativeMinutesTotillbeforeIntegerHourofday
    , ruleHalfAfterpastIntegerHourofday
    , ruleHalfTotillbeforeIntegerHourofday
    , ruleQuarterAfterpastIntegerHourofday
    , ruleQuarterTotillbeforeIntegerHourofday
    , ruleSeason
    , ruleSeason2
    , ruleTheCycleAfterTime
    , ruleTheCycleBeforeTime
    , ruleTheCycleOfTime
    , ruleTheDayAfterTomorrow
    , ruleTheDayBeforeYesterday
    , ruleTheDayofmonthNonOrdinal
    , ruleTheDayofmonthOrdinal
    , ruleTheIdesOfNamedmonth
    , ruleTheOrdinalCycleAfterTime
    , ruleTheOrdinalCycleOfTime
    , ruleThisCycle
    , ruleThisPartofday
    , ruleThisTime
    , ruleThisnextDayofweek
    , ruleTimeAfterNext
    , ruleTimeBeforeLast
    , ruleTimePartofday
    , ruleTimeofdayApproximately
    , ruleTimeofdayLatent
    , ruleTimeofdayOclock
    , ruleTimeofdaySharp
    , ruleTimeofdayTimeofdayInterval
    , ruleToday
    , ruleTomorrow
    , ruleTonight
    , ruleUntilTimeofday
    , ruleValentinesDay
    , ruleWeekend
    , ruleWithinDuration
    , ruleYear
    , ruleYearLatent
    , ruleYearLatent2
    , ruleYesterday
    , ruleYyyymmdd
    , ruleTimezone
    , rule2400
    , ruleDmm
    , ruleDDm
    , ruleDdDotMm
    ]
    ++ ruleDaysOfWeek
    ++ ruleMonths
  