-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}

module Duckling.Temperature.FR.Rules
  ( rules ) where

import Prelude
import Data.String

import Duckling.Dimensions.Types
import Duckling.Temperature.Helpers
import Duckling.Temperature.Types (TemperatureData (..))
import qualified Duckling.Temperature.Types as TTemperature
import Duckling.Types

ruleLatentTempDegrees :: Rule
ruleLatentTempDegrees = Rule
  { name = "<latent temp> degrees"
  , pattern =
    [ Predicate $ isValueOnly False
    , regex "(deg(r(Ã©|e|Ã¨))?s?\\.?)|Â°"
    ]
  , prod = \case
      (Token Temperature td:_) -> Just . Token Temperature $
        withUnit TTemperature.Degree td
      _ -> Nothing
  }

ruleTempCelsius :: Rule
ruleTempCelsius = Rule
  { name = "<temp> Celsius"
  , pattern =
    [ Predicate $ isValueOnly True
    , regex "c(el[cs]?(ius)?)?\\.?"
    ]
  , prod = \case
      (Token Temperature td:_) -> Just . Token Temperature $
        withUnit TTemperature.Celsius td
      _ -> Nothing
  }

ruleTempFahrenheit :: Rule
ruleTempFahrenheit = Rule
  { name = "<temp> Fahrenheit"
  , pattern =
    [ Predicate $ isValueOnly True
    , regex "f(ah?reh?n(h?eit)?)?\\.?"
    ]
  , prod = \case
      (Token Temperature td:_) -> Just . Token Temperature $
        withUnit TTemperature.Fahrenheit td
      _ -> Nothing
  }

ruleLatentTempEnDessousDeZero :: Rule
ruleLatentTempEnDessousDeZero = Rule
  { name = "<latent temp> en dessous de zero"
  , pattern =
    [ Predicate $ isValueOnly True
    , regex "en dessous de (0|z(Ã©|e)ro)"
    ]
  , prod = \case
      (Token Temperature td@TemperatureData{TTemperature.value = Just v}:_) ->
        case TTemperature.unit td of
          Nothing -> Just . Token Temperature . withUnit TTemperature.Degree $
            td {TTemperature.value = Just (- v)}
          _ -> Just . Token Temperature $ td {TTemperature.value = Just (- v)}
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleLatentTempDegrees
  , ruleLatentTempEnDessousDeZero
  , ruleTempCelsius
  , ruleTempFahrenheit
  ]


-- 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 OverloadedStrings #-}

module Duckling.Temperature.GA.Corpus
  ( corpus ) where

import Prelude
import Data.String

import Duckling.Locale
import Duckling.Resolve
import Duckling.Temperature.Types
import Duckling.Testing.Types

corpus :: Corpus
corpus = (testContext {locale = makeLocale GA Nothing}, testOptions, allExamples)

allExamples :: [Example]
allExamples = concat
  [ examples (simple Celsius 37)
             [ "37Â°C"
             , "37 Â° celsius"
             , "37 cÃ©imeanna Celsius"
             , "37 cÃ©imeanna C"
             , "37 cÃ©imeanna ceinteagrÃ¡d"
             ]
  , examples (simple Fahrenheit 70)
             [ "70Â°F"
             , "70 Â° Fahrenheit"
             , "70 cÃ©imeanna F"
             ]
  , examples (simple Degree 45)
             [ "45Â°"
             , "45 cÃ©imeanna"
             ]
  , examples (simple Degree (-10))
             [ "-10Â°"
             , "- 10 cÃ©imeanna"
             , "10 cÃ©imeanna faoi bhun nÃ¡id"
             , "10 faoi bhun nÃ¡id"
             ]
  ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}

module Duckling.Temperature.GA.Rules
  ( rules ) where

import Prelude
import Data.String

import Duckling.Dimensions.Types
import Duckling.Temperature.Helpers
import Duckling.Temperature.Types (TemperatureData (..))
import qualified Duckling.Temperature.Types as TTemperature
import Duckling.Types

ruleLatentTempCim :: Rule
ruleLatentTempCim = Rule
  { name = "<latent temp> cÃ©im"
  , pattern =
    [ Predicate $ isValueOnly False
    , regex "g?ch?(Ã©|e)im(e(anna)?)?|Â°"
    ]
  , prod = \case
      (Token Temperature td:_) -> Just . Token Temperature $
        withUnit TTemperature.Degree td
      _ -> Nothing
  }

ruleTempCelsius :: Rule
ruleTempCelsius = Rule
  { name = "<temp> Celsius"
  , pattern =
    [ Predicate $ isValueOnly True
    , regex "ceinteagr(Ã¡|a)d|c(el[cs]?(ius)?)?\\.?"
    ]
  , prod = \case
      (Token Temperature td:_) -> Just . Token Temperature $
        withUnit TTemperature.Celsius td
      _ -> Nothing
  }

ruleTempFahrenheit :: Rule
ruleTempFahrenheit = Rule
  { name = "<temp> Fahrenheit"
  , pattern =
    [ Predicate $ isValueOnly True
    , regex "f(ah?reh?n(h?eit)?)?\\.?"
    ]
  , prod = \case
      (Token Temperature td:_) -> Just . Token Temperature $
        withUnit TTemperature.Fahrenheit td
      _ -> Nothing
  }

ruleLatentTempFaoiBhunNid :: Rule
ruleLatentTempFaoiBhunNid = Rule
  { name = "<latent temp> faoi bhun nÃ¡id"
  , pattern =
    [ Predicate $ isValueOnly True
    , regex "faoi bhun (0|n(a|Ã¡)id)"
    ]
  , prod = \case
      (Token Temperature td@TemperatureData{TTemperature.value = Just v}:_) ->
        case TTemperature.unit td of
          Nothing -> Just . Token Temperature . withUnit TTemperature.Degree $
            td {TTemperature.value = Just (- v)}
          _ -> Just . Token Temperature $ td {TTemperature.value = Just (- v)}
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleLatentTempCim
  , ruleLatentTempFaoiBhunNid
  , ruleTempCelsius
  , ruleTempFahrenheit
  ]


-- 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 OverloadedStrings #-}

module Duckling.Temperature.HR.Corpus
  ( corpus ) where

import Prelude
import Data.String

import Duckling.Locale
import Duckling.Resolve
import Duckling.Temperature.Types
import Duckling.Testing.Types

corpus :: Corpus
corpus = (testContext {locale = makeLocale HR Nothing}, testOptions, allExamples)

allExamples :: [Example]
allExamples = concat
  [ examples (simple Celsius 37)
             [ "37Â°C"
             , "37 Â° celzija"
             , "37 stupnjeva Celzija"
             , "trideset i sedam celzija"
             , "37 stupnjeva Celzija"
             , "trideset sedam celzija"
             ]
  , examples (simple Fahrenheit 70)
             [ "70Â°F"
             , "70 Â° Fahrenheit"
             , "70 stupnjeva F"
             , "sedamdeset Fahrenheit"
             ]
  , examples (simple Degree 45)
             [ "45Â°"
             , "45 stupnjeva"
             , "45 deg."
             ]
  ]


-- 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 #-}

module Duckling.Temperature.HR.Rules
  ( rules ) where

import Data.String
import Prelude

import Duckling.Dimensions.Types
import Duckling.Temperature.Helpers
import Duckling.Temperature.Types (TemperatureData (..))
import Duckling.Types
import qualified Duckling.Temperature.Types as TTemperature

ruleLatentTempStupnjevi :: Rule
ruleLatentTempStupnjevi = Rule
  { name = "<latent temp> stupnjevi"
  , pattern =
    [ Predicate $ isValueOnly False
    , regex "deg\\.?|stupa?nj((ev)?a)?|Â°"
    ]
  , prod = \tokens -> case tokens of
      (Token Temperature td:_) -> Just . Token Temperature $
        withUnit TTemperature.Degree td
      _ -> Nothing
  }

ruleTempCelzij :: Rule
ruleTempCelzij = Rule
  { name = "<temp> Celzij"
  , pattern =
    [ Predicate $ isValueOnly True
    , regex "c(elz?(ija?)?)?\\.?"
    ]
  , prod = \tokens -> case tokens of
      (Token Temperature td:_) -> Just . Token Temperature $
        withUnit TTemperature.Celsius td
      _ -> Nothing
  }

ruleTempFahrenheit :: Rule
ruleTempFahrenheit = Rule
  { name = "<temp> Fahrenheit"
  , pattern =
    [ Predicate $ isValueOnly True
    , regex "f(ah?rh?eh?n(h?eit)?)?\\.?"
    ]
  , prod = \tokens -> case tokens of
      (Token Temperature td:_) -> Just . Token Temperature $
        withUnit TTemperature.Fahrenheit td
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleLatentTempStupnjevi
  , ruleTempCelzij
  , ruleTempFahrenheit
  ]


-- 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 OverloadedStrings #-}

module Duckling.Temperature.IT.Corpus
  ( corpus ) where

import Data.String
import Prelude

import Duckling.Locale
import Duckling.Resolve
import Duckling.Temperature.Types
import Duckling.Testing.Types

corpus :: Corpus
corpus = (testContext {locale = makeLocale IT Nothing}, testOptions, allExamples)

allExamples :: [Example]
allExamples = concat
  [ examples (simple Celsius 37)
             [ "37Â°C"
             , "37 Â° celsius"
             , "37 Â° centigradi"
             , "37 gradi Celsius"
             , "37 gradi Centigradi"
             , "trentasette celsius"
             , "trentasette gradi centigradi"
             ]
  , examples (simple Celsius 1)
             [ "1 grado centigrado"
             ]
  , examples (simple Fahrenheit 70)
             [ "70Â°F"
             , "70 Â° Fahrenheit"
             , "70 gradi F"
             , "70 gradi Fahreneit"
             , "settanta Fahrenheit"
             ]
  , examples (simple Degree 45)
             [ "45Â°"
             , "45 gradi"
             ]
  , examples (simple Degree 1)
             [ "1 grado"
             ]
  ]


-- 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 #-}

module Duckling.Temperature.IT.Rules
  ( rules ) where

import Data.String
import Prelude

import Duckling.Dimensions.Types
import Duckling.Temperature.Helpers
import Duckling.Temperature.Types (TemperatureData (..))
import Duckling.Types
import qualified Duckling.Temperature.Types as TTemperature

ruleLatentTempDegrees :: Rule
ruleLatentTempDegrees = Rule
  { name = "<latent temp> degrees"
  , pattern =
    [ Predicate $ isValueOnly False
    , regex "(grad[io]?\\.?)|Â°"
    ]
  , prod = \tokens -> case tokens of
      (Token Temperature td:_) -> Just . Token Temperature $
        withUnit TTemperature.Degree td
      _ -> Nothing
  }

ruleTempCelcius :: Rule
ruleTempCelcius = Rule
  { name = "<temp> Celcius"
  , pattern =
    [ Predicate $ isValueOnly True
    , regex "(c((el[cs]?(ius)?)|(entigrad[io]))?\\.?)"
    ]
  , prod = \tokens -> case tokens of
      (Token Temperature td:_) -> Just . Token Temperature $
        withUnit TTemperature.Celsius td
      _ -> Nothing
  }

ruleTempFahrenheit :: Rule
ruleTempFahrenheit = Rule
  { name = "<temp> Fahrenheit"
  , pattern =
    [ Predicate $ isValueOnly True
    , regex "f(ah?rh?eh?n(h?eit)?)?\\.?"
    ]
  , prod = \tokens -> case tokens of
      (Token Temperature td:_) -> Just . Token Temperature $
        withUnit TTemperature.Fahrenheit td
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleLatentTempDegrees
  , ruleTempCelcius
  , ruleTempFahrenheit
  ]


-- 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 OverloadedStrings #-}

module Duckling.Temperature.PT.Corpus
  ( corpus ) where

import Prelude
import Data.String

import Duckling.Locale
import Duckling.Resolve
import Duckling.Temperature.Types
import Duckling.Testing.Types

corpus :: Corpus
corpus = (testContext {locale = makeLocale PT Nothing}, testOptions, allExamples)

allExamples :: [Example]
allExamples = concat
  [ examples (simple Celsius 37)
             [ "37Â°C"
             , "37 Â° celsius"
             , "37 graus Celsius"
             , "37 graus C"
             , "trinta e sete celsius"
             , "37 centÃgrados"
             , "37 graus centigrados"
             ]
  , examples (simple Fahrenheit 70)
             [ "70Â°F"
             , "70 Â° Fahrenheit"
             , "70 graus F"
             , "setenta Fahrenheit"
             ]
  , examples (simple Degree 45)
             [ "45Â°"
             , "45 graus"
             ]
  , examples (simple Degree (-10))
             [ "-10Â°"
             , "- dez graus"
             , "10 abaixo de zero"
             ]
  ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}

module Duckling.Temperature.PT.Rules
  ( rules ) where

import Prelude
import Data.String

import Duckling.Dimensions.Types
import Duckling.Temperature.Helpers
import Duckling.Temperature.Types (TemperatureData (..))
import qualified Duckling.Temperature.Types as TTemperature
import Duckling.Types

ruleLatentTempTemp :: Rule
ruleLatentTempTemp = Rule
  { name = "<latent temp> temp"
  , pattern =
    [ Predicate $ isValueOnly False
    , regex "(graus?)|Â°"
    ]
  , prod = \case
      (Token Temperature td:_) -> Just . Token Temperature $
        withUnit TTemperature.Degree td
      _ -> Nothing
  }

ruleTempCelsius :: Rule
ruleTempCelsius = Rule
  { name = "<temp> Celsius"
  , pattern =
    [ Predicate $ isValueOnly True
    , regex "(cent(i|Ã)grados?|c(el[cs]?(ius)?)?\\.?)"
    ]
  , prod = \case
      (Token Temperature td:_) -> Just . Token Temperature $
        withUnit TTemperature.Celsius td
      _ -> Nothing
  }

ruleTempFahrenheit :: Rule
ruleTempFahrenheit = Rule
  { name = "<temp> Fahrenheit"
  , pattern =
    [ Predicate $ isValueOnly True
    , regex "f(ah?reh?n(h?eit)?)?\\.?"
    ]
  , prod = \case
      (Token Temperature td:_) -> Just . Token Temperature $
        withUnit TTemperature.Fahrenheit td
      _ -> Nothing
  }

ruleLatentTempTempAbaixoDeZero :: Rule
ruleLatentTempTempAbaixoDeZero = Rule
  { name = "<latent temp> temp abaixo de zero"
  , pattern =
    [ Predicate $ isValueOnly True
    , regex "((graus?)|Â°)?( abaixo (de)? zero)"
    ]
  , prod = \case
      (Token Temperature td@TemperatureData{TTemperature.value = Just v}:_) ->
        case TTemperature.unit td of
          Nothing -> Just . Token Temperature . withUnit TTemperature.Degree $
            td {TTemperature.value = Just (- v)}
          _ -> Just . Token Temperature $ td {TTemperature.value = Just (- v)}
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleLatentTempTemp
  , ruleLatentTempTempAbaixoDeZero
  , ruleTempCelsius
  , ruleTempFahrenheit
  ]


-- 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 OverloadedStrings #-}

module Duckling.Temperature.RO.Corpus
  ( corpus
  ) where

import Data.String
import Prelude

import Duckling.Locale
import Duckling.Resolve
import Duckling.Temperature.Types
import Duckling.Testing.Types

corpus :: Corpus
corpus = (testContext {locale = makeLocale RO Nothing}, testOptions, allExamples)

allExamples :: [Example]
allExamples = concat
  [ examples (simple Celsius 37)
             [ "37Â°C"
             , "37 Â° celsius"
             , "37 grade Celsius"
             , "treizeci si sapte celsius"
             , "37 grade Celsius"
             , "37 de grade de Celsius"
             , "treizeci si sapte celsius"
             , "treizeci si sapte de celsius"
             ]
  , examples (simple Fahrenheit 70)
             [ "70Â°F"
             , "70 Â° Fahrenheit"
             , "70 grade F"
             , "saptezeci Fahrenheit"
             ]
  , examples (simple Degree 45)
             [ "45Â°"
             , "45 grade"
             , "45 de grade"
             ]
  ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}

module Duckling.Temperature.RO.Rules
  ( rules
  ) where

import Data.String
import Prelude

import Duckling.Dimensions.Types
import Duckling.Temperature.Helpers
import Duckling.Types
import qualified Duckling.Temperature.Types as TTemperature

ruleLatentTempGrade :: Rule
ruleLatentTempGrade = Rule
  { name = "<latent temp> grade"
  , pattern =
    [ Predicate $ isValueOnly False
    , regex "(de )?(grade)|Â°"
    ]
  , prod = \case
      (Token Temperature td:_) -> Just . Token Temperature $
        withUnit TTemperature.Degree td
      _ -> Nothing
  }

ruleTempCelcius :: Rule
ruleTempCelcius = Rule
  { name = "<temp> Celcius"
  , pattern =
    [ Predicate $ isValueOnly True
    , regex "(de )?c(el[cs]?(ius)?)?\\.?"
    ]
  , prod = \case
      (Token Temperature td:_) -> Just . Token Temperature $
        withUnit TTemperature.Celsius td
      _ -> Nothing
  }

ruleTempFahrenheit :: Rule
ruleTempFahrenheit = Rule
  { name = "<temp> Fahrenheit"
  , pattern =
    [ Predicate $ isValueOnly True
    , regex "(de )?f(ah?rh?eh?n(h?eit)?)?\\.?"
    ]
  , prod = \case
      (Token Temperature td:_) -> Just . Token Temperature $
        withUnit TTemperature.Fahrenheit td
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleLatentTempGrade
  , ruleTempCelcius
  , ruleTempFahrenheit
  ]


-- 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 OverloadedStrings #-}

module Duckling.Temperature.TR.Corpus
  ( corpus ) where

import Prelude
import Data.String
import Duckling.Locale
import Duckling.Resolve
import Duckling.Temperature.Types
import Duckling.Testing.Types

corpus :: Corpus
corpus = (testContext {locale = makeLocale TR Nothing}, testOptions, allExamples)

allExamples :: [Example]
allExamples = concat
  [ examples (simple Celsius 37)
             [ "37Â°C"
             , "37 Â° santigrat"
             , "37 derece C"
             , "37 C"
             , "37 derece santigrat"
             , "otuz yedi santigrat"
             ]
  , examples (simple Fahrenheit 70)
             [ "70Â°F"
             , "70 Â° Fahrenhayt"
             , "70 Â° Fahrenayt"
             , "70 derece fahrenhayt"
             , "70 derece F"
             , "70 F"
             , "yetmiÅŸ fahrenayt"
             ]
  , examples (simple Degree 45)
             [ "45Â°"
             , "45 derece"
             , "kÄ±rk beÅŸ derece"
             ]
  , examples (simple Degree (-2))
             [ "-2Â°"
             , "- 2 derece"
             , "sÄ±fÄ±rÄ±n altÄ±nda 2Â°"
             , "sÄ±fÄ±rÄ±n altÄ±nda 2 derece"
             , "2Â° sÄ±fÄ±rÄ±n altÄ±nda"
             , "2 derece sÄ±fÄ±rÄ±n altÄ±nda"
             ]
  ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}

module Duckling.Temperature.TR.Rules
  ( rules ) where

import Data.Maybe
import Prelude
import Data.String

import Duckling.Dimensions.Types
import Duckling.Temperature.Helpers
import qualified Duckling.Temperature.Types as TTemperature
import Duckling.Temperature.Types (TemperatureData(..))
import Duckling.Types

ruleTemperatureDegrees :: Rule
ruleTemperatureDegrees = Rule
  { name = "<latent temp> degrees"
  , pattern =
    [ Predicate $ isValueOnly False
    , regex "derece|Â°"
    ]
  , prod = \case
    (Token Temperature td:_) -> Just . Token Temperature $
      withUnit TTemperature.Degree td
    _ -> Nothing
  }

ruleTemperatureCelsius :: Rule
ruleTemperatureCelsius = Rule
  { name = "<temp> Celsius"
  , pattern =
    [ Predicate $ isValueOnly True
    , regex "c|santigrat"
    ]
  , prod = \case
    (Token Temperature td:_) -> Just . Token Temperature $
      withUnit TTemperature.Celsius td
    _ -> Nothing
  }

ruleTemperatureFahrenheit :: Rule
ruleTemperatureFahrenheit = Rule
  { name = "<temp> Fahrenheit"
  , pattern =
    [ Predicate $ isValueOnly True
    , regex "f(ahrenh?ayt)?"
    ]
  , prod = \case
    (Token Temperature td:_) -> Just . Token Temperature $
      withUnit TTemperature.Fahrenheit td
    _ -> Nothing
  }

ruleTemperatureBelowZero :: Rule
ruleTemperatureBelowZero = Rule
  { name = "below zero <temp>"
  , pattern =
    [ regex "s\305f\305r\305n alt\305nda"
    , Predicate $ isValueOnly True
    ]
  , prod = \case
      (_:Token Temperature td@TemperatureData{TTemperature.value = Just v}:_) ->
        case TTemperature.unit td of
          Nothing -> Just . Token Temperature . withUnit TTemperature.Degree $
            td {TTemperature.value = Just (- v)}
          _ -> Just . Token Temperature $ td {TTemperature.value = Just (- v)}
      _ -> Nothing
  }

ruleTemperatureBelowZeroReverse :: Rule
ruleTemperatureBelowZeroReverse = Rule
  { name = "<temp> below zero"
  , pattern =
    [ Predicate $ isValueOnly True
    , regex "s\305f\305r\305n alt\305nda"
    ]
  , prod = \case
      (Token Temperature td@TemperatureData{TTemperature.value = Just v}:_) ->
        case TTemperature.unit td of
          Nothing -> Just . Token Temperature . withUnit TTemperature.Degree $
            td {TTemperature.value = Just (- v)}
          _ -> Just . Token Temperature $ td {TTemperature.value = Just (- v)}
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleTemperatureDegrees
  , ruleTemperatureCelsius
  , ruleTemperatureFahrenheit
  , ruleTemperatureBelowZero
  , ruleTemperatureBelowZeroReverse
  ]


-- 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 NoRebindableSyntax #-}
{-# LANGUAGE TupleSections #-}

module Duckling.Testing.Types
  ( Corpus
  , Datetime
  , Example
  , NegativeCorpus
  , TestPredicate
  , dt
  , examples
  , examplesCustom
  , parserCheck
  , refTime
  , simpleCheck
  , testContext
  , testOptions
  , withLocale
  , zTime
  ) where

import Data.Aeson (toJSON, ToJSON, Value)
import Data.Fixed (Pico)
import Data.Text (Text)
import Prelude
import qualified Data.Time as Time

import Duckling.Core
import Duckling.Resolve
import Duckling.Types

type TestPredicate = Context -> ResolvedToken -> Bool
type Example = (Text, TestPredicate)
type Corpus = (Context, Options, [Example])
type NegativeCorpus = (Context, Options, [Text])

examplesCustom :: TestPredicate -> [Text] -> [Example]
examplesCustom check = map (, check)

simpleCheck :: ToJSON a => a -> TestPredicate
simpleCheck json _ Resolved{rval = RVal _ v} = toJSON json == toJSON v

parserCheck :: Eq a => a -> (Value -> Maybe a) -> TestPredicate
parserCheck expected parseValue _ Resolved{rval = RVal _ v} =
  Just expected == parseValue (toJSON v)

examples :: ToJSON a => a -> [Text] -> [Example]
examples output = examplesCustom (simpleCheck output)

type Datetime = (Integer, Int, Int, Int, Int, Pico)

dt :: Datetime -> Time.UTCTime
dt (year, month, days, hours, minutes, seconds) = Time.UTCTime day diffTime
  where
    day = Time.fromGregorian year month days
    diffTime = Time.timeOfDayToTime $ Time.TimeOfDay hours minutes seconds

zTime :: Datetime -> Int -> Time.ZonedTime
zTime datetime offset = fromUTC (dt datetime) $ Time.hoursToTimeZone offset

refTime :: Datetime -> Int -> DucklingTime
refTime datetime offset = fromZonedTime $ zTime datetime offset

-- Tuesday Feb 12, 2013 at 4:30am is the "now" for the tests
testContext :: Context
testContext = Context
  { locale = makeLocale EN Nothing
  , referenceTime = refTime (2013, 2, 12, 4, 30, 0) (-2)
  }

testOptions :: Options
testOptions = Options
  { withLatent = False
  }

withLocale :: (Context, Options, [a]) -> Locale -> [a]
  -> (Context, Options, [a])
withLocale (langContext, options, langXs) locale localeXs
  = (langContext {locale = locale}, options, langXs ++ localeXs)


-- 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.

module Duckling.Time.Computed
  ( chanukah, chineseNewYear, dhanteras, easterSunday, eidalAdha, eidalFitr
  , globalYouthServiceDay, guruGobindSinghJayanti, karvaChauth
  , krishnaJanmashtami, lagBaOmer, mawlid, muharram, navaratri, orthodoxEaster
  , passover, rajab, rakshaBandhan, mahavirJayanti, mahaShivaRatri
  , ramadan, rathaYatra, roshHashana, thaiPongal, thiruOnam, superTuesday
  , tishaBAv, tuBishvat, vasantPanchami, vesak
  , yomHaatzmaut, vaisakhi, purim, saraswatiJayanti, rabindraJayanti
  , ravidassJayanti, pargatDiwas, ugadi, ganeshChaturthi, ramaNavami
  ) where

import Data.Maybe
import Prelude

import Duckling.Time.Helpers (timeComputed, toTimeObjectM)
import Duckling.Time.Types (TimeData(..), TimeObject(..), timedata')
import qualified Duckling.TimeGrain.Types as TG

computedDays :: [TimeObject] -> TimeData
computedDays xs = timedata'
  { timePred = timeComputed xs
  , timeGrain = TG.Day
  }

chanukah :: TimeData
chanukah = computedDays chanukah'

chanukah' :: [TimeObject]
chanukah' = mapMaybe toTimeObjectM
  [ (1950, 12, 3)
  , (1951, 12, 23)
  , (1952, 12, 12)
  , (1953, 12, 1)
  , (1954, 12, 19)
  , (1955, 12, 9)
  , (1956, 11, 28)
  , (1957, 12, 17)
  , (1958, 12, 6)
  , (1959, 12, 25)
  , (1960, 12, 13)
  , (1961, 12, 2)
  , (1962, 12, 21)
  , (1963, 12, 10)
  , (1964, 11, 29)
  , (1965, 12, 18)
  , (1966, 12, 7)
  , (1967, 12, 26)
  , (1968, 12, 15)
  , (1969, 12, 4)
  , (1970, 12, 22)
  , (1971, 12, 12)
  , (1972, 11, 30)
  , (1973, 12, 19)
  , (1974, 12, 8)
  , (1975, 11, 28)
  , (1976, 12, 16)
  , (1977, 12, 4)
  , (1978, 12, 24)
  , (1979, 12, 14)
  , (1980, 12, 2)
  , (1981, 12, 20)
  , (1982, 12, 10)
  , (1983, 11, 30)
  , (1984, 12, 18)
  , (1985, 12, 7)
  , (1986, 12, 26)
  , (1987, 12, 15)
  , (1988, 12, 3)
  , (1989, 12, 22)
  , (1990, 12, 11)
  , (1991, 12, 1)
  , (1992, 12, 19)
  , (1993, 12, 8)
  , (1994, 11, 27)
  , (1995, 12, 17)
  , (1996, 12, 5)
  , (1997, 12, 23)
  , (1998, 12, 13)
  , (1999, 12, 3)
  , (2000, 12, 21)
  , (2001, 12, 9)
  , (2002, 11, 29)
  , (2003, 12, 19)
  , (2004, 12, 7)
  , (2005, 12, 25)
  , (2006, 12, 15)
  , (2007, 12, 4)
  , (2008, 12, 21)
  , (2009, 12, 11)
  , (2010, 12, 1)
  , (2011, 12, 20)
  , (2012, 12, 8)
  , (2013, 11, 27)
  , (2014, 12, 16)
  , (2015, 12, 6)
  , (2016, 12, 24)
  , (2017, 12, 12)
  , (2018, 12, 2)
  , (2019, 12, 22)
  , (2020, 12, 10)
  , (2021, 11, 28)
  , (2022, 12, 18)
  , (2023, 12, 7)
  , (2024, 12, 25)
  , (2025, 12, 14)
  , (2026, 12, 4)
  , (2027, 12, 24)
  , (2028, 12, 12)
  , (2029, 12, 1)
  , (2030, 12, 20)
  , (2031, 12, 9)
  , (2032, 11, 27)
  , (2033, 12, 16)
  , (2034, 12, 6)
  , (2035, 12, 25)
  , (2036, 12, 13)
  , (2037, 12, 2)
  , (2038, 12, 21)
  , (2039, 12, 11)
  , (2040, 11, 29)
  , (2041, 12, 17)
  , (2042, 12, 7)
  , (2043, 12, 26)
  , (2044, 12, 14)
  , (2045, 12, 3)
  , (2046, 12, 23)
  , (2047, 12, 12)
  , (2048, 11, 29)
  , (2049, 12, 19)
  , (2050, 12, 9)
  ]

chineseNewYear :: TimeData
chineseNewYear = computedDays chineseNewYear'

chineseNewYear' :: [TimeObject]
chineseNewYear' = mapMaybe toTimeObjectM
  [ (1950, 2, 17)
  , (1951, 2, 6)
  , (1952, 1, 27)
  , (1953, 2, 14)
  , (1954, 2, 3)
  , (1955, 1, 24)
  , (1956, 2, 12)
  , (1957, 1, 31)
  , (1958, 2, 18)
  , (1959, 2, 8)
  , (1960, 1, 28)
  , (1961, 2, 15)
  , (1962, 2, 5)
  , (1963, 1, 25)
  , (1964, 2, 13)
  , (1965, 2, 2)
  , (1966, 1, 21)
  , (1967, 2, 9)
  , (1968, 1, 30)
  , (1969, 2, 17)
  , (1970, 2, 6)
  , (1971, 1, 27)
  , (1972, 2, 15)
  , (1973, 2, 3)
  , (1974, 1, 23)
  , (1975, 2, 11)
  , (1976, 1, 31)
  , (1977, 2, 18)
  , (1978, 2, 7)
  , (1979, 1, 28)
  , (1980, 2, 16)
  , (1981, 2, 5)
  , (1982, 1, 25)
  , (1983, 2, 13)
  , (1984, 2, 2)
  , (1985, 2, 20)
  , (1986, 2, 9)
  , (1987, 1, 29)
  , (1988, 2, 17)
  , (1989, 2, 6)
  , (1990, 1, 27)
  , (1991, 2, 15)
  , (1992, 2, 4)
  , (1993, 1, 23)
  , (1994, 2, 10)
  , (1995, 1, 31)
  , (1996, 2, 19)
  , (1997, 2, 7)
  , (1998, 1, 28)
  , (1999, 2, 16)
  , (2000, 2, 5)
  , (2001, 1, 24)
  , (2002, 2, 12)
  , (2003, 2, 1)
  , (2004, 1, 22)
  , (2005, 2, 9)
  , (2006, 1, 29)
  , (2007, 2, 18)
  , (2008, 2, 7)
  , (2009, 1, 26)
  , (2010, 2, 14)
  , (2011, 2, 3)
  , (2012, 1, 23)
  , (2013, 2, 10)
  , (2014, 1, 31)
  , (2015, 2, 19)
  , (2016, 2, 8)
  , (2017, 1, 28)
  , (2018, 2, 16)
  , (2019, 2, 5)
  , (2020, 1, 25)
  , (2021, 2, 12)
  , (2022, 2, 1)
  , (2023, 1, 22)
  , (2024, 2, 10)
  , (2025, 1, 29)
  , (2026, 2, 17)
  , (2027, 2, 6)
  , (2028, 1, 26)
  , (2029, 2, 13)
  , (2030, 2, 3)
  , (2031, 1, 23)
  , (2032, 2, 11)
  , (2033, 1, 31)
  , (2034, 2, 19)
  , (2035, 2, 8)
  , (2036, 1, 28)
  , (2037, 2, 15)
  , (2038, 2, 4)
  , (2039, 1, 24)
  , (2040, 2, 12)
  , (2041, 2, 1)
  , (2042, 1, 22)
  , (2043, 2, 10)
  , (2044, 1, 30)
  , (2045, 2, 17)
  , (2046, 2, 6)
  , (2047, 1, 26)
  , (2048, 2, 14)
  , (2049, 2, 2)
  , (2050, 1, 23)
  ]

easterSunday :: TimeData
easterSunday = computedDays easterSunday'

easterSunday' :: [TimeObject]
easterSunday' = mapMaybe toTimeObjectM
  [ (1950, 4, 9)
  , (1951, 3, 25)
  , (1952, 4, 13)
  , (1953, 4, 5)
  , (1954, 4, 18)
  , (1955, 4, 10)
  , (1956, 4, 1)
  , (1957, 4, 21)
  , (1958, 4, 6)
  , (1959, 3, 29)
  , (1960, 4, 17)
  , (1961, 4, 2)
  , (1962, 4, 22)
  , (1963, 4, 14)
  , (1964, 3, 29)
  , (1965, 4, 18)
  , (1966, 4, 10)
  , (1967, 3, 26)
  , (1968, 4, 14)
  , (1969, 4, 6)
  , (1970, 3, 29)
  , (1971, 4, 11)
  , (1972, 4, 2)
  , (1973, 4, 22)
  , (1974, 4, 14)
  , (1975, 3, 30)
  , (1976, 4, 18)
  , (1977, 4, 10)
  , (1978, 3, 26)
  , (1979, 4, 15)
  , (1980, 4, 6)
  , (1981, 4, 19)
  , (1982, 4, 11)
  , (1983, 4, 3)
  , (1984, 4, 22)
  , (1985, 4, 7)
  , (1986, 3, 30)
  , (1987, 4, 19)
  , (1988, 4, 3)
  , (1989, 3, 26)
  , (1990, 4, 15)
  , (1991, 3, 31)
  , (1992, 4, 19)
  , (1993, 4, 11)
  , (1994, 4, 3)
  , (1995, 4, 16)
  , (1996, 4, 7)
  , (1997, 3, 30)
  , (1998, 4, 12)
  , (1999, 4, 4)
  , (2000, 4, 23)
  , (2001, 4, 15)
  , (2002, 3, 31)
  , (2003, 4, 20)
  , (2004, 4, 11)
  , (2005, 3, 27)
  , (2006, 4, 16)
  , (2007, 4, 8)
  , (2008, 3, 23)
  , (2009, 4, 12)
  , (2010, 4, 4)
  , (2011, 4, 24)
  , (2012, 4, 8)
  , (2013, 3, 31)
  , (2014, 4, 20)
  , (2015, 4, 5)
  , (2016, 3, 27)
  , (2017, 4, 16)
  , (2018, 4, 1)
  , (2019, 4, 21)
  , (2020, 4, 12)
  , (2021, 4, 4)
  , (2022, 4, 17)
  , (2023, 4, 9)
  , (2024, 3, 31)
  , (2025, 4, 20)
  , (2026, 4, 5)
  , (2027, 3, 28)
  , (2028, 4, 16)
  , (2029, 4, 1)
  , (2030, 4, 21)
  , (2031, 4, 13)
  , (2032, 3, 28)
  , (2033, 4, 17)
  , (2034, 4, 9)
  , (2035, 3, 25)
  , (2036, 4, 13)
  , (2037, 4, 5)
  , (2038, 4, 25)
  , (2039, 4, 10)
  , (2040, 4, 1)
  , (2041, 4, 21)
  , (2042, 4, 6)
  , (2043, 3, 29)
  , (2044, 4, 17)
  , (2045, 4, 9)
  , (2046, 3, 25)
  , (2047, 4, 14)
  , (2048, 4, 5)
  , (2049, 4, 18)
  , (2050, 4, 10)
  ]

lagBaOmer :: TimeData
lagBaOmer = computedDays lagBaOmer'

lagBaOmer' :: [TimeObject]
lagBaOmer' = mapMaybe toTimeObjectM
  [ (1950, 5, 4)
  , (1951, 5, 23)
  , (1952, 5, 12)
  , (1953, 5, 2)
  , (1954, 5, 20)
  , (1955, 5, 9)
  , (1956, 4, 28)
  , (1957, 5, 18)
  , (1958, 5, 7)
  , (1959, 5, 25)
  , (1960, 5, 14)
  , (1961, 5, 3)
  , (1962, 5, 21)
  , (1963, 5, 11)
  , (1964, 4, 29)
  , (1965, 5, 19)
  , (1966, 5, 7)
  , (1967, 5, 27)
  , (1968, 5, 15)
  , (1969, 5, 5)
  , (1970, 5, 23)
  , (1971, 5, 12)
  , (1972, 5, 1)
  , (1973, 5, 19)
  , (1974, 5, 9)
  , (1975, 4, 28)
  , (1976, 5, 17)
  , (1977, 5, 5)
  , (1978, 5, 24)
  , (1979, 5, 14)
  , (1980, 5, 3)
  , (1981, 5, 21)
  , (1982, 5, 10)
  , (1983, 4, 30)
  , (1984, 5, 19)
  , (1985, 5, 8)
  , (1986, 5, 26)
  , (1987, 5, 16)
  , (1988, 5, 4)
  , (1989, 5, 22)
  , (1990, 5, 12)
  , (1991, 5, 1)
  , (1992, 5, 20)
  , (1993, 5, 8)
  , (1994, 4, 28)
  , (1995, 5, 17)
  , (1996, 5, 6)
  , (1997, 5, 24)
  , (1998, 5, 13)
  , (1999, 5, 3)
  , (2000, 5, 22)
  , (2001, 5, 10)
  , (2002, 4, 29)
  , (2003, 5, 19)
  , (2004, 5, 8)
  , (2005, 5, 26)
  , (2006, 5, 15)
  , (2007, 5, 5)
  , (2008, 5, 22)
  , (2009, 5, 11)
  , (2010, 5, 1)
  , (2011, 5, 21)
  , (2012, 5, 9)
  , (2013, 4, 27)
  , (2014, 5, 17)
  , (2015, 5, 6)
  , (2016, 5, 25)
  , (2017, 5, 13)
  , (2018, 5, 2)
  , (2019, 5, 22)
  , (2020, 5, 11)
  , (2021, 4, 29)
  , (2022, 5, 18)
  , (2023, 5, 8)
  , (2024, 5, 25)
  , (2025, 5, 15)
  , (2026, 5, 4)
  , (2027, 5, 24)
  , (2028, 5, 13)
  , (2029, 5, 2)
  , (2030, 5, 20)
  , (2031, 5, 10)
  , (2032, 4, 28)
  , (2033, 5, 16)
  , (2034, 5, 6)
  , (2035, 5, 26)
  , (2036, 5, 14)
  , (2037, 5, 2)
  , (2038, 5, 22)
  , (2039, 5, 11)
  , (2040, 4, 30)
  , (2041, 5, 18)
  , (2042, 5, 7)
  , (2043, 5, 27)
  , (2044, 5, 14)
  , (2045, 5, 4)
  , (2046, 5, 23)
  , (2047, 5, 13)
  , (2048, 4, 30)
  , (2049, 5, 19)
  , (2050, 5, 9)
  ]

orthodoxEaster :: TimeData
orthodoxEaster = computedDays orthodoxEaster'

orthodoxEaster' :: [TimeObject]
orthodoxEaster' = mapMaybe toTimeObjectM
  [ (1950, 4, 9)
  , (1951, 4, 29)
  , (1952, 4, 20)
  , (1953, 4, 5)
  , (1954, 4, 25)
  , (1955, 4, 17)
  , (1956, 5, 6)
  , (1957, 4, 21)
  , (1958, 4, 13)
  , (1959, 5, 3)
  , (1960, 4, 17)
  , (1961, 4, 9)
  , (1962, 4, 29)
  , (1963, 4, 14)
  , (1964, 5, 3)
  , (1965, 4, 25)
  , (1966, 4, 10)
  , (1967, 4, 30)
  , (1968, 4, 21)
  , (1969, 4, 13)
  , (1970, 4, 26)
  , (1971, 4, 18)
  , (1972, 4, 9)
  , (1973, 4, 29)
  , (1974, 4, 14)
  , (1975, 5, 4)
  , (1976, 4, 25)
  , (1977, 4, 10)
  , (1978, 4, 30)
  , (1979, 4, 22)
  , (1980, 4, 6)
  , (1981, 4, 26)
  , (1982, 4, 18)
  , (1983, 5, 8)
  , (1984, 4, 22)
  , (1985, 4, 14)
  , (1986, 5, 4)
  , (1987, 4, 19)
  , (1988, 4, 10)
  , (1989, 4, 30)
  , (1990, 4, 15)
  , (1991, 4, 7)
  , (1992, 4, 26)
  , (1993, 4, 18)
  , (1994, 5, 1)
  , (1995, 4, 23)
  , (1996, 4, 14)
  , (1997, 4, 27)
  , (1998, 4, 19)
  , (1999, 4, 11)
  , (2000, 4, 30)
  , (2001, 4, 15)
  , (2002, 5, 5)
  , (2003, 4, 27)
  , (2004, 4, 11)
  , (2005, 5, 1)
  , (2006, 4, 23)
  , (2007, 4, 8)
  , (2008, 4, 27)
  , (2009, 4, 19)
  , (2010, 4, 4)
  , (2011, 4, 24)
  , (2012, 4, 15)
  , (2013, 5, 5)
  , (2014, 4, 20)
  , (2015, 4, 12)
  , (2016, 5, 1)
  , (2017, 4, 16)
  , (2018, 4, 8)
  , (2019, 4, 28)
  , (2020, 4, 19)
  , (2021, 5, 2)
  , (2022, 4, 24)
  , (2023, 4, 16)
  , (2024, 5, 5)
  , (2025, 4, 20)
  , (2026, 4, 12)
  , (2027, 5, 2)
  , (2028, 4, 16)
  , (2029, 4, 8)
  , (2030, 4, 28)
  , (2031, 4, 13)
  , (2032, 5, 2)
  , (2033, 4, 24)
  , (2034, 4, 9)
  , (2035, 4, 29)
  , (2036, 4, 20)
  , (2037, 4, 5)
  , (2038, 4, 25)
  , (2039, 4, 17)
  , (2040, 5, 6)
  , (2041, 4, 21)
  , (2042, 4, 13)
  , (2043, 5, 3)
  , (2044, 4, 24)
  , (2045, 4, 9)
  , (2046, 4, 29)
  , (2047, 4, 21)
  , (2048, 4, 5)
  , (2049, 4, 25)
  , (2050, 4, 17)
  ]

passover :: TimeData
passover = computedDays passover'

passover' :: [TimeObject]
passover' = mapMaybe toTimeObjectM
  [ (1950, 4, 1)
  , (1951, 4, 20)
  , (1952, 4, 9)
  , (1953, 3, 30)
  , (1954, 4, 17)
  , (1955, 4, 6)
  , (1956, 3, 26)
  , (1957, 4, 15)
  , (1958, 4, 4)
  , (1959, 4, 22)
  , (1960, 4, 11)
  , (1961, 3, 31)
  , (1962, 4, 18)
  , (1963, 4, 8)
  , (1964, 3, 27)
  , (1965, 4, 16)
  , (1966, 4, 4)
  , (1967, 4, 24)
  , (1968, 4, 12)
  , (1969, 4, 2)
  , (1970, 4, 20)
  , (1971, 4, 9)
  , (1972, 3, 29)
  , (1973, 4, 16)
  , (1974, 4, 6)
  , (1975, 3, 26)
  , (1976, 4, 14)
  , (1977, 4, 2)
  , (1978, 4, 21)
  , (1979, 4, 11)
  , (1980, 3, 31)
  , (1981, 4, 18)
  , (1982, 4, 7)
  , (1983, 3, 28)
  , (1984, 4, 16)
  , (1985, 4, 5)
  , (1986, 4, 23)
  , (1987, 4, 13)
  , (1988, 4, 1)
  , (1989, 4, 19)
  , (1990, 4, 9)
  , (1991, 3, 29)
  , (1992, 4, 17)
  , (1993, 4, 5)
  , (1994, 3, 26)
  , (1995, 4, 14)
  , (1996, 4, 3)
  , (1997, 4, 21)
  , (1998, 4, 10)
  , (1999, 3, 31)
  , (2000, 4, 19)
  , (2001, 4, 7)
  , (2002, 3, 27)
  , (2003, 4, 16)
  , (2004, 4, 5)
  , (2005, 4, 23)
  , (2006, 4, 12)
  , (2007, 4, 2)
  , (2008, 4, 19)
  , (2009, 4, 8)
  , (2010, 3, 29)
  , (2011, 4, 18)
  , (2012, 4, 6)
  , (2013, 3, 25)
  , (2014, 4, 14)
  , (2015, 4, 3)
  , (2016, 4, 22)
  , (2017, 4, 10)
  , (2018, 3, 30)
  , (2019, 4, 19)
  , (2020, 4, 8)
  , (2021, 3, 27)
  , (2022, 4, 15)
  , (2023, 4, 5)
  , (2024, 4, 22)
  , (2025, 4, 12)
  , (2026, 4, 1)
  , (2027, 4, 21)
  , (2028, 4, 10)
  , (2029, 3, 30)
  , (2030, 4, 17)
  , (2031, 4, 7)
  , (2032, 3, 26)
  , (2033, 4, 13)
  , (2034, 4, 3)
  , (2035, 4, 23)
  , (2036, 4, 11)
  , (2037, 3, 30)
  , (2038, 4, 19)
  , (2039, 4, 8)
  , (2040, 3, 28)
  , (2041, 4, 15)
  , (2042, 4, 4)
  , (2043, 4, 24)
  , (2044, 4, 11)
  , (2045, 4, 1)
  , (2046, 4, 20)
  , (2047, 4, 10)
  , (2048, 3, 28)
  , (2049, 4, 16)
  , (2050, 4, 6)
  ]

roshHashana :: TimeData
roshHashana = computedDays roshHashana'

roshHashana' :: [TimeObject]
roshHashana' = mapMaybe toTimeObjectM
  [ (1950, 9, 11)
  , (1951, 9, 30)
  , (1952, 9, 19)
  , (1953, 9, 9)
  , (1954, 9, 27)
  , (1955, 9, 16)
  , (1956, 9, 5)
  , (1957, 9, 25)
  , (1958, 9, 14)
  , (1959, 10, 2)
  , (1960, 9, 21)
  , (1961, 9, 10)
  , (1962, 9, 28)
  , (1963, 9, 18)
  , (1964, 9, 6)
  , (1965, 9, 26)
  , (1966, 9, 14)
  , (1967, 10, 4)
  , (1968, 9, 22)
  , (1969, 9, 12)
  , (1970, 9, 30)
  , (1971, 9, 19)
  , (1972, 9, 8)
  , (1973, 9, 26)
  , (1974, 9, 16)
  , (1975, 9, 5)
  , (1976, 9, 24)
  , (1977, 9, 12)
  , (1978, 10, 1)
  , (1979, 9, 21)
  , (1980, 9, 10)
  , (1981, 9, 28)
  , (1982, 9, 17)
  , (1983, 9, 7)
  , (1984, 9, 26)
  , (1985, 9, 15)
  , (1986, 10, 3)
  , (1987, 9, 23)
  , (1988, 9, 11)
  , (1989, 9, 29)
  , (1990, 9, 19)
  , (1991, 9, 8)
  , (1992, 9, 27)
  , (1993, 9, 15)
  , (1994, 9, 5)
  , (1995, 9, 24)
  , (1996, 9, 13)
  , (1997, 10, 1)
  , (1998, 9, 20)
  , (1999, 9, 10)
  , (2000, 9, 29)
  , (2001, 9, 17)
  , (2002, 9, 6)
  , (2003, 9, 26)
  , (2004, 9, 15)
  , (2005, 10, 3)
  , (2006, 9, 22)
  , (2007, 9, 12)
  , (2008, 9, 29)
  , (2009, 9, 18)
  , (2010, 9, 8)
  , (2011, 9, 28)
  , (2012, 9, 18)
  , (2013, 9, 4)
  , (2014, 9, 24)
  , (2015, 9, 13)
  , (2016, 10, 2)
  , (2017, 9, 20)
  , (2018, 9, 9)
  , (2019, 9, 29)
  , (2020, 9, 18)
  , (2021, 9, 6)
  , (2022, 9, 25)
  , (2023, 9, 15)
  , (2024, 10, 2)
  , (2025, 9, 22)
  , (2026, 9, 11)
  , (2027, 10, 1)
  , (2028, 9, 20)
  , (2029, 9, 9)
  , (2030, 9, 27)
  , (2031, 9, 17)
  , (2032, 9, 5)
  , (2033, 9, 23)
  , (2034, 9, 13)
  , (2035, 10, 3)
  , (2036, 9, 21)
  , (2037, 9, 9)
  , (2038, 9, 29)
  , (2039, 9, 18)
  , (2040, 9, 7)
  , (2041, 9, 25)
  , (2042, 9, 14)
  , (2043, 10, 4)
  , (2044, 9, 21)
  , (2045, 9, 11)
  , (2046, 9, 30)
  , (2047, 9, 20)
  , (2048, 9, 7)
  , (2049, 9, 26)
  , (2050, 9, 16)
  ]

tishaBAv :: TimeData
tishaBAv = computedDays tishaBAv'

tishaBAv' :: [TimeObject]
tishaBAv' = mapMaybe toTimeObjectM
  [ (1950, 7, 22)
  , (1951, 8, 11)
  , (1952, 7, 30)
  , (1953, 7, 20)
  , (1954, 8, 7)
  , (1955, 7, 27)
  , (1956, 7, 16)
  , (1957, 8, 5)
  , (1958, 7, 26)
  , (1959, 8, 12)
  , (1960, 8, 1)
  , (1961, 7, 22)
  , (1962, 8, 8)
  , (1963, 7, 29)
  , (1964, 7, 18)
  , (1965, 8, 7)
  , (1966, 7, 25)
  , (1967, 8, 14)
  , (1968, 8, 3)
  , (1969, 7, 23)
  , (1970, 8, 10)
  , (1971, 7, 31)
  , (1972, 7, 19)
  , (1973, 8, 6)
  , (1974, 7, 27)
  , (1975, 7, 16)
  , (1976, 8, 4)
  , (1977, 7, 23)
  , (1978, 8, 12)
  , (1979, 8, 1)
  , (1980, 7, 21)
  , (1981, 8, 8)
  , (1982, 7, 28)
  , (1983, 7, 18)
  , (1984, 8, 6)
  , (1985, 7, 27)
  , (1986, 8, 13)
  , (1987, 8, 3)
  , (1988, 7, 23)
  , (1989, 8, 9)
  , (1990, 7, 30)
  , (1991, 7, 20)
  , (1992, 8, 8)
  , (1993, 7, 26)
  , (1994, 7, 16)
  , (1995, 8, 5)
  , (1996, 7, 24)
  , (1997, 8, 11)
  , (1998, 8, 1)
  , (1999, 7, 21)
  , (2000, 8, 9)
  , (2001, 7, 28)
  , (2002, 7, 17)
  , (2003, 8, 6)
  , (2004, 7, 26)
  , (2005, 8, 13)
  , (2006, 8, 2)
  , (2007, 7, 23)
  , (2008, 8, 9)
  , (2009, 7, 29)
  , (2010, 7, 19)
  , (2011, 8, 8)
  , (2012, 7, 28)
  , (2013, 7, 15)
  , (2014, 8, 4)
  , (2015, 7, 25)
  , (2016, 8, 13)
  , (2017, 7, 31)
  , (2018, 7, 21)
  , (2019, 8, 10)
  , (2020, 7, 29)
  , (2021, 7, 17)
  , (2022, 8, 6)
  , (2023, 7, 26)
  , (2024, 8, 12)
  , (2025, 8, 2)
  , (2026, 7, 22)
  , (2027, 8, 11)
  , (2028, 7, 31)
  , (2029, 7, 21)
  , (2030, 8, 7)
  , (2031, 7, 28)
  , (2032, 7, 17)
  , (2033, 8, 3)
  , (2034, 7, 24)
  , (2035, 8, 13)
  , (2036, 8, 2)
  , (2037, 7, 20)
  , (2038, 8, 9)
  , (2039, 7, 30)
  , (2040, 7, 18)
  , (2041, 8, 5)
  , (2042, 7, 26)
  , (2043, 8, 15)
  , (2044, 8, 1)
  , (2045, 7, 22)
  , (2046, 8, 11)
  , (2047, 7, 31)
  , (2048, 7, 18)
  , (2049, 8, 7)
  , (2050, 7, 27)
  ]

yomHaatzmaut :: TimeData
yomHaatzmaut = computedDays yomHaatzmaut'

yomHaatzmaut' :: [TimeObject]
yomHaatzmaut' = mapMaybe toTimeObjectM
  [ (1950, 4, 19)
  , (1951, 5, 9)
  , (1952, 4, 29)
  , (1953, 4, 19)
  , (1954, 5, 5)
  , (1955, 4, 26)
  , (1956, 4, 15)
  , (1957, 5, 5)
  , (1958, 4, 23)
  , (1959, 5, 12)
  , (1960, 5, 1)
  , (1961, 4, 19)
  , (1962, 5, 8)
  , (1963, 4, 28)
  , (1964, 4, 15)
  , (1965, 5, 5)
  , (1966, 4, 24)
  , (1967, 5, 14)
  , (1968, 5, 1)
  , (1969, 4, 22)
  , (1970, 5, 10)
  , (1971, 4, 28)
  , (1972, 4, 18)
  , (1973, 5, 6)
  , (1974, 4, 24)
  , (1975, 4, 15)
  , (1976, 5, 4)
  , (1977, 4, 20)
  , (1978, 5, 10)
  , (1979, 5, 1)
  , (1980, 4, 20)
  , (1981, 5, 6)
  , (1982, 4, 27)
  , (1983, 4, 17)
  , (1984, 5, 6)
  , (1985, 4, 24)
  , (1986, 5, 13)
  , (1987, 5, 3)
  , (1988, 4, 20)
  , (1989, 5, 9)
  , (1990, 4, 29)
  , (1991, 4, 17)
  , (1992, 5, 6)
  , (1993, 4, 25)
  , (1994, 4, 13)
  , (1995, 5, 3)
  , (1996, 4, 23)
  , (1997, 5, 11)
  , (1998, 4, 29)
  , (1999, 4, 20)
  , (2000, 5, 9)
  , (2001, 4, 25)
  , (2002, 4, 16)
  , (2003, 5, 6)
  , (2004, 4, 26)
  , (2005, 5, 11)
  , (2006, 5, 2)
  , (2007, 4, 23)
  , (2008, 5, 7)
  , (2009, 4, 28)
  , (2010, 4, 19)
  , (2011, 5, 9)
  , (2012, 4, 25)
  , (2013, 4, 15)
  , (2014, 5, 5)
  , (2015, 4, 22)
  , (2016, 5, 11)
  , (2017, 5, 1)
  , (2018, 4, 18)
  , (2019, 5, 8)
  , (2020, 4, 28)
  , (2021, 4, 14)
  , (2022, 5, 4)
  , (2023, 4, 25)
  , (2024, 5, 13)
  , (2025, 4, 30)
  , (2026, 4, 21)
  , (2027, 5, 11)
  , (2028, 5, 1)
  , (2029, 4, 18)
  , (2030, 5, 7)
  , (2031, 4, 28)
  , (2032, 4, 14)
  , (2033, 5, 3)
  , (2034, 4, 24)
  , (2035, 5, 14)
  , (2036, 4, 30)
  , (2037, 4, 20)
  , (2038, 5, 10)
  , (2039, 4, 27)
  , (2040, 4, 17)
  , (2041, 5, 6)
  , (2042, 4, 23)
  , (2043, 5, 13)
  , (2044, 5, 2)
  , (2045, 4, 19)
  , (2046, 5, 9)
  , (2047, 4, 30)
  , (2048, 4, 15)
  , (2049, 5, 5)
  , (2050, 4, 26)
  ]

eidalAdha :: TimeData
eidalAdha = computedDays eidalAdha'

eidalAdha' :: [TimeObject]
eidalAdha' = mapMaybe toTimeObjectM
  [ (1950, 9, 23)
  , (1951, 9, 12)
  , (1952, 8, 31)
  , (1953, 8, 20)
  , (1954, 8, 9)
  , (1955, 7, 30)
  , (1956, 7, 19)
  , (1957, 7, 8)
  , (1958, 6, 27)
  , (1959, 6, 17)
  , (1960, 6, 4)
  , (1961, 5, 25)
  , (1962, 5, 14)
  , (1963, 5, 3)
  , (1964, 4, 22)
  , (1965, 4, 11)
  , (1966, 4, 1)
  , (1967, 3, 21)
  , (1968, 3, 9)
  , (1969, 2, 27)
  , (1970, 2, 16)
  , (1971, 2, 6)
  , (1972, 1, 26)
  , (1973, 1, 14)
  , (1974, 1, 3)
  , (1974, 12, 24)
  , (1975, 12, 13)
  , (1976, 12, 1)
  , (1977, 11, 21)
  , (1978, 11, 10)
  , (1979, 10, 31)
  , (1980, 10, 19)
  , (1981, 10, 8)
  , (1982, 9, 27)
  , (1983, 9, 17)
  , (1984, 9, 5)
  , (1985, 8, 26)
  , (1986, 8, 15)
  , (1987, 8, 4)
  , (1988, 7, 23)
  , (1989, 7, 13)
  , (1990, 7, 2)
  , (1991, 6, 22)
  , (1992, 6, 11)
  , (1993, 5, 31)
  , (1994, 5, 20)
  , (1995, 5, 9)
  , (1996, 4, 27)
  , (1997, 4, 17)
  , (1998, 4, 7)
  , (1999, 3, 27)
  , (2000, 3, 16)
  , (2001, 3, 5)
  , (2002, 2, 22)
  , (2003, 2, 11)
  , (2004, 2, 1)
  , (2005, 1, 21)
  , (2006, 1, 10)
  , (2006, 12, 31)
  , (2007, 12, 20)
  , (2008, 12, 8)
  , (2009, 11, 27)
  , (2011, 11, 6)
  , (2012, 10, 26)
  , (2013, 10, 15)
  , (2014, 10, 4)
  , (2015, 8, 23)
  , (2016, 9, 11)
  , (2017, 9, 1)
  , (2018, 8, 21)
  , (2019, 8, 11)
  , (2020, 7, 31)
  , (2021, 7, 20)
  , (2022, 7, 9)
  , (2023, 6, 28)
  , (2024, 6, 16)
  , (2025, 6, 6)
  , (2026, 5, 27)
  , (2027, 5, 16)
  , (2028, 5, 5)
  , (2029, 4, 24)
  ]

eidalFitr :: TimeData
eidalFitr = computedDays eidalFitr'

eidalFitr' :: [TimeObject]
eidalFitr' = mapMaybe toTimeObjectM
  [ (1950, 7, 16)
  , (1951, 7, 6)
  , (1952, 6, 23)
  , (1953, 6, 13)
  , (1954, 6, 2)
  , (1955, 5, 23)
  , (1956, 5, 11)
  , (1957, 5, 1)
  , (1958, 4, 20)
  , (1959, 4, 10)
  , (1960, 3, 28)
  , (1961, 3, 18)
  , (1962, 3, 7)
  , (1963, 2, 24)
  , (1964, 2, 14)
  , (1965, 2, 2)
  , (1966, 1, 22)
  , (1967, 1, 12)
  , (1968, 1, 1)
  , (1968, 12, 21)
  , (1969, 12, 10)
  , (1970, 11, 30)
  , (1971, 11, 19)
  , (1972, 11, 7)
  , (1973, 10, 27)
  , (1974, 10, 16)
  , (1975, 10, 6)
  , (1976, 9, 24)
  , (1977, 9, 14)
  , (1978, 9, 3)
  , (1979, 8, 23)
  , (1980, 8, 12)
  , (1981, 8, 1)
  , (1982, 7, 21)
  , (1983, 7, 11)
  , (1984, 6, 30)
  , (1985, 6, 19)
  , (1986, 6, 8)
  , (1987, 5, 28)
  , (1988, 5, 16)
  , (1989, 5, 6)
  , (1990, 4, 26)
  , (1991, 4, 15)
  , (1992, 4, 4)
  , (1993, 3, 24)
  , (1994, 3, 13)
  , (1995, 3, 2)
  , (1996, 2, 19)
  , (1997, 2, 8)
  , (1998, 1, 29)
  , (1999, 1, 18)
  , (2000, 1, 8)
  , (2000, 12, 27)
  , (2001, 12, 16)
  , (2002, 12, 5)
  , (2003, 11, 25)
  , (2004, 11, 14)
  , (2005, 11, 3)
  , (2006, 10, 23)
  , (2007, 10, 13)
  , (2008, 10, 1)
  , (2009, 9, 20)
  , (2010, 9, 10)
  , (2011, 8, 30)
  , (2012, 8, 19)
  , (2013, 8, 8)
  , (2014, 7, 28)
  , (2015, 7, 17)
  , (2016, 7, 6)
  , (2017, 6, 25)
  , (2018, 6, 15)
  , (2019, 6, 4)
  , (2020, 5, 24)
  , (2021, 5, 13)
  , (2022, 5, 2)
  , (2023, 4, 21)
  , (2024, 4, 10)
  , (2025, 3, 30)
  , (2026, 3, 20)
  , (2027, 3, 9)
  , (2028, 2, 26)
  , (2029, 2, 14)
  , (2030, 2, 05)
  , (2031, 1, 25)
  , (2032, 1, 14)
  , (2033, 1, 3)
  , (2033, 12, 23)
  , (2034, 12, 12)
  , (2035, 12, 2)
  , (2036, 11, 20)
  , (2037, 11, 10)
  , (2038, 10, 30)
  , (2039, 10, 19)
  , (2040, 10, 8)
  , (2041, 9, 27)
  , (2042, 9, 16)
  , (2043, 9, 6)
  , (2044, 8, 25)
  , (2045, 8, 15)
  , (2046, 8, 4)
  , (2047, 7, 24)
  , (2048, 7, 13)
  , (2049, 7, 2)
  , (2050, 6, 21)
  ]

rajab :: TimeData
rajab = computedDays rajab'

rajab' :: [TimeObject]
rajab' = mapMaybe toTimeObjectM
  [ (1999, 10, 10)
  , (2000, 9, 28)
  , (2001, 9, 18)
  , (2002, 9, 8)
  , (2003, 8, 29)
  , (2004, 8, 17)
  , (2005, 8, 6)
  , (2006, 7, 26)
  , (2007, 7, 15)
  , (2008, 7, 4)
  , (2009, 6, 24)
  , (2010, 6, 13)
  , (2011, 6, 3)
  , (2012, 5, 22)
  , (2013, 5, 11)
  , (2014, 4, 30)
  , (2015, 4, 20)
  , (2016, 4, 8)
  , (2017, 3, 29)
  , (2018, 3, 18)
  , (2019, 3, 8)
  , (2020, 2, 25)
  , (2021, 2, 13)
  , (2022, 2, 2)
  , (2023, 1, 23)
  , (2024, 1, 13)
  , (2025, 1, 1)
  , (2025, 12, 21)
  , (2026, 12, 10)
  , (2027, 11, 29)
  , (2028, 11, 18)
  ]

muharram :: TimeData
muharram = computedDays muharram'

muharram' :: [TimeObject]
muharram' = mapMaybe toTimeObjectM
  [ (1998, 4, 27)
  , (1999, 4, 17)
  , (2000, 4, 6)
  , (2001, 3, 26)
  , (2002, 3, 15)
  , (2003, 4, 4)
  , (2004, 2, 21)
  , (2005, 2, 10)
  , (2006, 1, 31)
  , (2007, 1, 20)
  , (2008, 1, 10)
  , (2009, 12, 18)
  , (2010, 12, 7)
  , (2011, 11, 26)
  , (2012, 11, 15)
  , (2013, 11, 4)
  , (2014, 10, 25)
  , (2015, 10, 14)
  , (2016, 10, 2)
  , (2017, 9, 21)
  , (2018, 9, 11)
  , (2019, 8, 31)
  , (2020, 8, 20)
  , (2021, 8, 9)
  , (2022, 7, 30)
  , (2023, 7, 19)
  , (2024, 7, 7)
  , (2025, 6, 26)
  , (2026, 6, 16)
  , (2027, 6, 6)
  , (2028, 5, 25)
  ]

ramadan :: TimeData
ramadan = computedDays ramadan'

ramadan' :: [TimeObject]
ramadan' = mapMaybe toTimeObjectM
  [ (1950, 6, 17)
  , (1951, 6, 6)
  , (1952, 5, 25)
  , (1953, 5, 14)
  , (1954, 5, 4)
  , (1955, 4, 24)
  , (1956, 4, 12)
  , (1957, 4, 1)
  , (1958, 3, 21)
  , (1959, 3, 11)
  , (1960, 2, 28)
  , (1961, 2, 16)
  , (1962, 2, 5)
  , (1963, 1, 26)
  , (1964, 1, 15)
  , (1965, 1, 3)
  , (1965, 12, 23)
  , (1966, 12, 13)
  , (1967, 12, 2)
  , (1968, 11, 21)
  , (1969, 11, 10)
  , (1970, 11, 1)
  , (1971, 10, 20)
  , (1972, 10, 8)
  , (1973, 9, 27)
  , (1974, 9, 17)
  , (1975, 9, 6)
  , (1976, 8, 26)
  , (1977, 8, 15)
  , (1978, 8, 5)
  , (1979, 7, 25)
  , (1980, 7, 13)
  , (1981, 7, 2)
  , (1982, 6, 22)
  , (1983, 6, 12)
  , (1984, 5, 31)
  , (1985, 5, 20)
  , (1986, 5, 9)
  , (1987, 4, 29)
  , (1988, 4, 17)
  , (1989, 4, 7)
  , (1990, 3, 27)
  , (1991, 3, 17)
  , (1992, 3, 5)
  , (1993, 2, 22)
  , (1994, 2, 11)
  , (1995, 1, 31)
  , (1996, 1, 21)
  , (1997, 1, 10)
  , (1997, 12, 30)
  , (1998, 12, 19)
  , (1999, 12, 9)
  , (2000, 11, 27)
  , (2001, 11, 16)
  , (2002, 11, 6)
  , (2003, 10, 26)
  , (2004, 10, 15)
  , (2005, 10, 4)
  , (2006, 9, 24)
  , (2007, 9, 13)
  , (2008, 9, 1)
  , (2009, 8, 22)
  , (2010, 8, 11)
  , (2011, 8, 1)
  , (2012, 7, 20)
  , (2013, 7, 9)
  , (2014, 6, 28)
  , (2015, 6, 18)
  , (2016, 6, 6)
  , (2017, 5, 27)
  , (2018, 5, 16)
  , (2019, 5, 6)
  , (2020, 4, 24)
  , (2021, 4, 13)
  , (2022, 4, 2)
  , (2023, 3, 23)
  , (2024, 3, 11)
  , (2025, 3, 1)
  , (2026, 2, 18)
  , (2027, 2, 8)
  , (2028, 1, 28)
  , (2029, 1, 16)
  , (2030, 1, 6)
  , (2030, 12, 26)
  , (2031, 12, 15)
  , (2032, 12, 4)
  , (2033, 11, 23)
  , (2034, 11, 12)
  , (2035, 11, 2)
  , (2036, 10, 21)
  , (2037, 10, 11)
  , (2038, 9, 30)
  , (2039, 9, 19)
  , (2040, 9, 8)
  , (2041, 8, 28)
  , (2042, 8, 17)
  , (2043, 8, 7)
  , (2044, 7, 26)
  , (2045, 7, 16)
  , (2046, 7, 5)
  , (2047, 6, 24)
  , (2048, 6, 13)
  , (2049, 6, 2)
  , (2050, 5, 22)
  ]

mawlid :: TimeData
mawlid = computedDays mawlid'

mawlid' :: [TimeObject]
mawlid' = mapMaybe toTimeObjectM
  [ (1950, 1, 1)
  , (1950, 12, 22)
  , (1951, 12, 11)
  , (1952, 11, 30)
  , (1953, 11, 19)
  , (1954, 11, 8)
  , (1955, 10, 29)
  , (1956, 10, 17)
  , (1957, 10, 6)
  , (1958, 9, 26)
  , (1959, 9, 15)
  , (1960, 9, 3)
  , (1961, 8, 23)
  , (1962, 8, 12)
  , (1963, 8, 2)
  , (1964, 7, 21)
  , (1965, 7, 10)
  , (1966, 7, 1)
  , (1967, 6, 19)
  , (1968, 6, 8)
  , (1969, 5, 28)
  , (1970, 5, 18)
  , (1971, 5, 7)
  , (1972, 4, 25)
  , (1973, 4, 15)
  , (1974, 4, 4)
  , (1975, 3, 24)
  , (1976, 3, 12)
  , (1977, 3, 2)
  , (1978, 2, 19)
  , (1979, 2, 9)
  , (1980, 1, 30)
  , (1981, 1, 18)
  , (1982, 1, 7)
  , (1982, 12, 27)
  , (1983, 12, 16)
  , (1984, 12, 4)
  , (1985, 11, 24)
  , (1986, 11, 14)
  , (1987, 11, 3)
  , (1988, 10, 22)
  , (1989, 10, 11)
  , (1990, 10, 1)
  , (1991, 9, 20)
  , (1992, 9, 9)
  , (1993, 8, 29)
  , (1994, 8, 19)
  , (1995, 8, 8)
  , (1996, 7, 27)
  , (1997, 7, 16)
  , (1998, 7, 6)
  , (1999, 6, 26)
  , (2000, 6, 14)
  , (2001, 6, 4)
  , (2002, 5, 24)
  , (2003, 5, 13)
  , (2004, 5, 1)
  , (2005, 4, 21)
  , (2006, 4, 10)
  , (2007, 3, 31)
  , (2008, 3, 20)
  , (2009, 3, 9)
  , (2010, 2, 26)
  , (2011, 2, 15)
  , (2012, 2, 4)
  , (2013, 1, 24)
  , (2014, 1, 13)
  , (2015, 1, 3)
  , (2015, 12, 23)
  , (2016, 12, 11)
  , (2017, 11, 30)
  , (2018, 11, 20)
  , (2019, 11, 9)
  , (2020, 10, 29)
  , (2021, 10, 18)
  , (2022, 10, 8)
  , (2023, 9, 27)
  , (2024, 9, 15)
  , (2025, 9, 4)
  , (2026, 8, 25)
  , (2027, 8, 14)
  , (2028, 8, 3)
  ]

tuBishvat :: TimeData
tuBishvat = computedDays tuBishvat'

tuBishvat' :: [TimeObject]
tuBishvat' = mapMaybe toTimeObjectM
  [ (1950, 2, 1)
  , (1951, 1, 21)
  , (1952, 2, 10)
  , (1953, 1, 30)
  , (1954, 1, 18)
  , (1955, 2, 6)
  , (1956, 1, 27)
  , (1957, 1, 16)
  , (1958, 2, 4)
  , (1959, 1, 23)
  , (1960, 2, 12)
  , (1961, 1, 31)
  , (1962, 1, 19)
  , (1963, 2, 8)
  , (1964, 1, 28)
  , (1965, 1, 17)
  , (1966, 2, 4)
  , (1967, 1, 25)
  , (1968, 2, 13)
  , (1969, 2, 2)
  , (1970, 1, 21)
  , (1971, 2, 9)
  , (1972, 1, 30)
  , (1973, 1, 17)
  , (1974, 2, 6)
  , (1975, 1, 26)
  , (1976, 1, 16)
  , (1977, 2, 2)
  , (1978, 1, 22)
  , (1979, 2, 11)
  , (1980, 2, 1)
  , (1981, 1, 19)
  , (1982, 2, 7)
  , (1983, 1, 28)
  , (1984, 1, 18)
  , (1985, 2, 5)
  , (1986, 1, 24)
  , (1987, 2, 13)
  , (1988, 2, 2)
  , (1989, 1, 20)
  , (1990, 2, 9)
  , (1991, 1, 29)
  , (1992, 1, 19)
  , (1993, 2, 5)
  , (1994, 1, 26)
  , (1995, 1, 15)
  , (1996, 2, 4)
  , (1997, 1, 22)
  , (1998, 2, 10)
  , (1999, 1, 31)
  , (2000, 1, 21)
  , (2001, 2, 7)
  , (2002, 1, 27)
  , (2003, 1, 17)
  , (2004, 2, 6)
  , (2005, 1, 24)
  , (2006, 2, 12)
  , (2007, 2, 2)
  , (2008, 1, 21)
  , (2009, 2, 8)
  , (2010, 1, 29)
  , (2011, 1, 19)
  , (2012, 2, 7)
  , (2013, 1, 25)
  , (2014, 1, 15)
  , (2015, 2, 3)
  , (2016, 1, 24)
  , (2017, 2, 10)
  , (2018, 1, 30)
  , (2019, 1, 20)
  , (2020, 2, 9)
  , (2021, 1, 27)
  , (2022, 1, 16)
  , (2023, 2, 5)
  , (2024, 1, 24)
  , (2025, 2, 12)
  , (2026, 2, 1)
  , (2027, 1, 22)
  , (2028, 2, 11)
  , (2029, 1, 30)
  , (2030, 1, 18)
  , (2031, 2, 7)
  , (2032, 1, 27)
  , (2033, 1, 14)
  , (2034, 2, 3)
  , (2035, 1, 24)
  , (2036, 2, 12)
  , (2037, 1, 30)
  , (2038, 1, 20)
  , (2039, 2, 8)
  , (2040, 1, 29)
  , (2041, 1, 16)
  , (2042, 2, 4)
  , (2043, 1, 25)
  , (2044, 2, 12)
  , (2045, 2, 1)
  , (2046, 1, 21)
  , (2047, 2, 10)
  , (2048, 1, 29)
  , (2049, 1, 17)
  , (2050, 2, 6)
  ]

dhanteras :: TimeData
dhanteras = computedDays dhanteras'

dhanteras' :: [TimeObject]
dhanteras' = mapMaybe toTimeObjectM
  [ (2000, 10, 24)
  , (2001, 11, 12)
  , (2002, 11, 2)
  , (2003, 10, 23)
  , (2004, 11, 10)
  , (2005, 10, 30)
  , (2006, 10, 19)
  , (2007, 11, 7)
  , (2008, 10, 26)
  , (2009, 10, 15)
  , (2010, 11, 3)
  , (2011, 10, 24)
  , (2012, 11, 11)
  , (2013, 11, 1)
  , (2014, 10, 21)
  , (2015, 11, 9)
  , (2016, 10, 28)
  , (2017, 10, 17)
  , (2018, 11, 5)
  , (2019, 10, 25)
  , (2020, 11, 13)
  , (2021, 11, 2)
  , (2022, 10, 22)
  , (2023, 11, 10)
  , (2024, 10, 29)
  , (2025, 10, 18)
  , (2026, 11, 6)
  , (2027, 10, 27)
  , (2028, 10, 15)
  , (2029, 11, 4)
  , (2030, 10, 24)
  ]

navaratri :: TimeData
navaratri = computedDays navaratri'

navaratri' :: [TimeObject]
navaratri' = mapMaybe toTimeObjectM
  [ (2000, 9, 28)
  , (2001, 10, 17)
  , (2002, 10, 7)
  , (2003, 9, 26)
  , (2004, 10, 14)
  , (2005, 10, 4)
  , (2006, 9, 23)
  , (2007, 10, 12)
  , (2008, 9, 30)
  , (2009, 9, 19)
  , (2010, 10, 8)
  , (2011, 9, 28)
  , (2012, 10, 16)
  , (2013, 10, 5)
  , (2014, 9, 25)
  , (2015, 10, 13)
  , (2016, 10, 1)
  , (2017, 9, 21)
  , (2018, 10, 9)
  , (2019, 9, 29)
  , (2020, 10, 17)
  , (2021, 10, 6)
  , (2022, 9, 26)
  , (2023, 10, 15)
  , (2024, 10, 3)
  , (2025, 9, 22)
  , (2026, 10, 11)
  , (2027, 9, 30)
  , (2028, 9, 19)
  , (2029, 10, 8)
  , (2030, 9, 27)
  ]

rakshaBandhan :: TimeData
rakshaBandhan = computedDays rakshaBandhan'

rakshaBandhan' :: [TimeObject]
rakshaBandhan' = mapMaybe toTimeObjectM
  [ (2000, 8, 15)
  , (2001, 8, 4)
  , (2002, 8, 22)
  , (2003, 8, 12)
  , (2004, 8, 29)
  , (2005, 8, 19)
  , (2006, 8, 9)
  , (2007, 8, 28)
  , (2008, 8, 16)
  , (2009, 8, 5)
  , (2010, 8, 24)
  , (2011, 8, 13)
  , (2012, 8, 2)
  , (2013, 8, 20)
  , (2014, 8, 10)
  , (2015, 8, 29)
  , (2016, 8, 18)
  , (2017, 8, 7)
  , (2018, 8, 26)
  , (2019, 8, 15)
  , (2020, 8, 3)
  , (2021, 8, 22)
  , (2022, 8, 11)
  , (2023, 8, 30)
  , (2024, 8, 19)
  , (2025, 8, 9)
  , (2026, 8, 28)
  , (2027, 8, 17)
  , (2028, 8, 5)
  , (2029, 8, 23)
  , (2030, 8, 13)
  ]

ganeshChaturthi :: TimeData
ganeshChaturthi = computedDays ganeshChaturthi'

ganeshChaturthi' :: [TimeObject]
ganeshChaturthi' = mapMaybe toTimeObjectM
  [ (2000, 9, 1)
  , (2001, 8, 22)
  , (2002, 9, 10)
  , (2003, 8, 31)
  , (2004, 9, 18)
  , (2005, 9, 7)
  , (2006, 8, 27)
  , (2007, 9, 15)
  , (2008, 9, 3)
  , (2009, 8, 23)
  , (2010, 9, 11)
  , (2011, 9, 1)
  , (2012, 9, 19)
  , (2013, 9, 9)
  , (2014, 8, 29)
  , (2015, 9, 17)
  , (2016, 9, 5)
  , (2017, 8, 25)
  , (2018, 9, 13)
  , (2019, 9, 2)
  , (2020, 8, 22)
  , (2021, 9, 9)
  , (2022, 8, 30)
  , (2023, 9, 18)
  , (2024, 9, 6)
  , (2025, 8, 26)
  , (2026, 9, 14)
  , (2027, 9, 3)
  , (2028, 8, 23)
  , (2029, 9, 11)
  , (2030, 9, 1)
  ]

mahavirJayanti :: TimeData
mahavirJayanti = computedDays mahavirJayanti'

mahavirJayanti' :: [TimeObject]
mahavirJayanti' = mapMaybe toTimeObjectM
  [ (2000, 4, 16)
  , (2001, 4, 6)
  , (2002, 4, 25)
  , (2003, 4, 15)
  , (2004, 4, 3)
  , (2005, 4, 22)
  , (2006, 4, 11)
  , (2007, 3, 31)
  , (2008, 4, 18)
  , (2009, 4, 7)
  , (2010, 3, 28)
  , (2011, 4, 16)
  , (2012, 4, 5)
  , (2013, 4, 24)
  , (2014, 4, 13)
  , (2015, 4, 2)
  , (2016, 4, 19)
  , (2017, 4, 9)
  , (2018, 3, 29)
  , (2019, 4, 17)
  , (2020, 4, 6)
  , (2021, 4, 25)
  , (2022, 4, 14)
  , (2023, 4, 4)
  , (2024, 4, 21)
  , (2025, 4, 10)
  , (2026, 3, 31)
  , (2027, 4, 19)
  , (2028, 4, 7)
  , (2029, 4, 26)
  , (2030, 4, 16)
  ]

thaiPongal :: TimeData
thaiPongal = computedDays thaiPongal'

thaiPongal' :: [TimeObject]
thaiPongal' = mapMaybe toTimeObjectM
  [ (2000, 1, 15)
  , (2001, 1, 14)
  , (2002, 1, 14)
  , (2003, 1, 14)
  , (2004, 1, 15)
  , (2005, 1, 14)
  , (2006, 1, 14)
  , (2007, 1, 15)
  , (2008, 1, 15)
  , (2009, 1, 14)
  , (2010, 1, 14)
  , (2011, 1, 15)
  , (2012, 1, 15)
  , (2013, 1, 14)
  , (2014, 1, 14)
  , (2015, 1, 15)
  , (2016, 1, 15)
  , (2017, 1, 14)
  , (2018, 1, 14)
  , (2019, 1, 15)
  , (2020, 1, 15)
  , (2021, 1, 14)
  , (2022, 1, 14)
  , (2023, 1, 15)
  , (2024, 1, 15)
  , (2025, 1, 14)
  , (2026, 1, 14)
  , (2027, 1, 15)
  , (2028, 1, 15)
  , (2029, 1, 14)
  , (2030, 1, 14)
  ]

mahaShivaRatri :: TimeData
mahaShivaRatri = computedDays mahaShivaRatri'

mahaShivaRatri' :: [TimeObject]
mahaShivaRatri' = mapMaybe toTimeObjectM
   [  (2000, 3, 4)
   ,  (2001, 2, 21)
   ,  (2002, 3, 12)
   ,  (2003, 3, 1)
   ,  (2004, 2, 18)
   ,  (2005, 3, 8)
   ,  (2006, 2, 26)
   ,  (2007, 2, 16)
   ,  (2008, 3, 6)
   ,  (2009, 2, 23)
   ,  (2010, 2, 12)
   ,  (2011, 3, 2)
   ,  (2012, 2, 20)
   ,  (2013, 3, 10)
   ,  (2014, 2, 27)
   ,  (2015, 2, 17)
   ,  (2016, 3, 7)
   ,  (2017, 2, 24)
   ,  (2018, 2, 13)
   ,  (2019, 3, 4)
   ,  (2020, 2, 21)
   ,  (2021, 3, 11)
   ,  (2022, 3, 1)
   ,  (2023, 2, 18)
   ,  (2024, 3, 8)
   ,  (2025, 2, 26)
   ,  (2026, 2, 15)
   ,  (2027, 3, 6)
   ,  (2028, 2, 23)
   ,  (2029, 2, 11)
   ,  (2030, 3, 2)
   ]

thiruOnam :: TimeData
thiruOnam = computedDays thiruOnam'

thiruOnam' :: [TimeObject]
thiruOnam' = mapMaybe toTimeObjectM
  [ (2000, 9, 10)
  , (2001, 8, 31)
  , (2002, 8, 21)
  , (2003, 9, 8)
  , (2004, 8, 28)
  , (2005, 9, 15)
  , (2006, 9, 5)
  , (2007, 8, 26)
  , (2008, 9, 12)
  , (2009, 9, 2)
  , (2010, 8, 23)
  , (2011, 9, 9)
  , (2012, 8, 29)
  , (2013, 8, 20)
  , (2014, 9, 6)
  , (2015, 8, 28)
  , (2016, 9, 13)
  , (2017, 9, 4)
  , (2018, 8, 24)
  , (2019, 9, 11)
  , (2020, 8, 31)
  , (2021, 8, 21)
  , (2022, 9, 8)
  , (2023, 8, 29)
  , (2024, 9, 15)
  , (2025, 9, 5)
  , (2026, 8, 26)
  , (2027, 9, 12)
  , (2028, 9, 1)
  , (2029, 8, 22)
  , (2030, 9, 9)
  ]

rathaYatra :: TimeData
rathaYatra = computedDays rathaYatra'

rathaYatra' :: [TimeObject]
rathaYatra' = mapMaybe toTimeObjectM
  [ (2000, 7, 2)
  , (2001, 6, 22)
  , (2002, 7, 11)
  , (2003, 7, 1)
  , (2004, 6, 19)
  , (2005, 7, 8)
  , (2006, 6, 27)
  , (2007, 7, 16)
  , (2008, 7, 4)
  , (2009, 6, 24)
  , (2010, 7, 13)
  , (2011, 7, 3)
  , (2012, 6, 21)
  , (2013, 7, 10)
  , (2014, 6, 29)
  , (2015, 7, 18)
  , (2016, 7, 6)
  , (2017, 6, 25)
  , (2018, 7, 14)
  , (2019, 7, 4)
  , (2020, 6, 23)
  , (2021, 7, 11)
  , (2022, 6, 30)
  , (2023, 6, 19)
  , (2024, 7, 7)
  , (2025, 6, 26)
  , (2026, 7, 15)
  , (2027, 7, 5)
  , (2028, 6, 24)
  , (2029, 7, 13)
  , (2030, 7, 2)
  ]

vasantPanchami :: TimeData
vasantPanchami = computedDays vasantPanchami'

vasantPanchami' :: [TimeObject]
vasantPanchami' = mapMaybe toTimeObjectM
  [ (2000, 2, 10)
  , (2001, 1, 29)
  , (2002, 2, 17)
  , (2003, 2, 6)
  , (2004, 1, 26)
  , (2005, 2, 13)
  , (2006, 2, 2)
  , (2007, 1, 23)
  , (2008, 2, 11)
  , (2009, 1, 31)
  , (2010, 1, 20)
  , (2011, 2, 8)
  , (2012, 1, 28)
  , (2013, 2, 15)
  , (2014, 2, 4)
  , (2015, 1, 24)
  , (2016, 2, 12)
  , (2017, 2, 1)
  , (2018, 1, 22)
  , (2019, 2, 10)
  , (2020, 1, 29)
  , (2021, 2, 16)
  , (2022, 2, 5)
  , (2023, 1, 26)
  , (2024, 2, 14)
  , (2025, 2, 2)
  , (2026, 1, 23)
  , (2027, 2, 11)
  , (2028, 1, 31)
  , (2029, 1, 19)
  , (2030, 2, 7)
  ]

globalYouthServiceDay :: TimeData
globalYouthServiceDay = computedDays globalYouthServiceDay'

globalYouthServiceDay' :: [TimeObject]
globalYouthServiceDay' = mapMaybe toTimeObjectM
  [ (2012, 4, 20)
  , (2013, 4, 26)
  , (2014, 4, 11)
  , (2015, 4, 17)
  , (2016, 4, 15)
  , (2017, 4, 21)
  , (2018, 4, 20)
  ]

guruGobindSinghJayanti :: TimeData
guruGobindSinghJayanti = computedDays guruGobindSinghJayanti'

guruGobindSinghJayanti' :: [TimeObject]
guruGobindSinghJayanti' = mapMaybe toTimeObjectM
  [ (2000, 1, 14)
  , (2001, 1, 2)
  , (2002, 1, 21)
  , (2003, 12, 29)
  , (2004, 1, 5)
  , (2005, 1, 16)
  , (2006, 12, 27)
  , (2007, 1, 5)
  , (2008, 1, 14)
  , (2009, 12, 23)
  , (2010, 1, 5)
  , (2011, 1, 5)
  , (2012, 1, 5)
  , (2013, 1, 18)
  , (2014, 1, 7)
  , (2015, 1, 5)
  , (2016, 1, 16)
  , (2017, 12, 25)
  , (2018, 1, 5)
  , (2019, 1, 13)
  , (2020, 2, 1)
  , (2021, 1, 19)
  , (2022, 1, 8)
  , (2023, 1, 5)
  , (2024, 1, 17)
  , (2025, 1, 5)
  , (2026, 1, 5)
  , (2027, 1, 14)
  , (2028, 1, 3)
  , (2029, 1, 5)
  , (2030, 1, 10)
  ]

karvaChauth :: TimeData
karvaChauth = computedDays karvaChauth'

karvaChauth' :: [TimeObject]
karvaChauth' = mapMaybe toTimeObjectM
    [ (2000, 10, 16)
    , (2001, 10, 4)
    , (2002, 10, 24)
    , (2003, 10, 13)
    , (2004, 10, 31)
    , (2005, 10, 20)
    , (2006, 10, 9)
    , (2007, 10, 28)
    , (2008, 10, 17)
    , (2009, 10, 7)
    , (2010, 10, 26)
    , (2011, 10, 15)
    , (2012, 10, 2)
    , (2013, 10, 22)
    , (2014, 10, 11)
    , (2015, 10, 30)
    , (2016, 10, 18)
    , (2017, 10, 8)
    , (2018, 10, 27)
    , (2019, 10, 17)
    , (2020, 10, 3)
    , (2021, 10, 23)
    , (2022, 10, 12)
    , (2023, 10, 31)
    , (2024, 10, 20)
    , (2025, 10, 9)
    , (2026, 10, 28)
    , (2027, 10, 18)
    , (2028, 10, 6)
    , (2029, 10, 25)
    , (2030, 10, 14)
    ]

krishnaJanmashtami :: TimeData
krishnaJanmashtami = computedDays krishnaJanmashtami'

krishnaJanmashtami' :: [TimeObject]
krishnaJanmashtami' = mapMaybe toTimeObjectM
    [ (2000, 8, 22)
    , (2001, 8, 12)
    , (2002, 8, 31)
    , (2003, 8, 20)
    , (2004, 9, 6)
    , (2005, 8, 26)
    , (2006, 8, 15)
    , (2007, 9, 3)
    , (2008, 8, 23)
    , (2009, 8, 13)
    , (2010, 9, 1)
    , (2011, 8, 21)
    , (2012, 8, 9)
    , (2013, 8, 28)
    , (2014, 8, 17)
    , (2015, 9, 5)
    , (2016, 8, 25)
    , (2017, 8, 14)
    , (2018, 9, 3)
    , (2019, 8, 23)
    , (2020, 8, 11)
    , (2021, 8, 30)
    , (2022, 8, 19)
    , (2023, 9, 6)
    , (2024, 8, 26)
    , (2025, 8, 16)
    , (2026, 9, 4)
    , (2027, 8, 25)
    , (2028, 8, 13)
    , (2029, 9, 1)
    , (2030, 8, 21)
    ]

vesak :: TimeData
vesak = computedDays vesak'

vesak' :: [TimeObject]
vesak' = mapMaybe toTimeObjectM
  [ (2000, 5, 18)
  , (2001, 5, 7)
  , (2002, 5, 26)
  , (2003, 5, 15)
  , (2004, 5, 4)
  , (2005, 5, 23)
  , (2006, 5, 12)
  , (2007, 5, 31)
  , (2008, 5, 19)
  , (2009, 5, 8)
  , (2010, 5, 27)
  , (2011, 5, 17)
  , (2012, 5, 5)
  , (2013, 5, 24)
  , (2014, 5, 14)
  , (2015, 5, 3)
  , (2016, 5, 21)
  , (2017, 5, 10)
  , (2018, 5, 29)
  , (2019, 5, 18)
  , (2020, 5, 7)
  , (2021, 5, 26)
  , (2022, 5, 15)
  , (2023, 5, 5)
  , (2024, 5, 23)
  , (2025, 5, 12)
  , (2026, 5, 31)
  , (2027, 5, 20)
  , (2028, 5, 8)
  , (2029, 5, 27)
  , (2030, 5, 17)
  ]

vaisakhi :: TimeData
vaisakhi = computedDays vaisakhi'

vaisakhi' :: [TimeObject]
vaisakhi' = mapMaybe toTimeObjectM
  [ (2000, 4, 13)
  , (2001, 4, 13)
  , (2002, 4, 14)
  , (2003, 4, 14)
  , (2004, 4, 13)
  , (2005, 4, 13)
  , (2006, 4, 14)
  , (2007, 4, 14)
  , (2008, 4, 13)
  , (2009, 4, 14)
  , (2010, 4, 14)
  , (2011, 4, 14)
  , (2012, 4, 13)
  , (2013, 4, 14)
  , (2014, 4, 14)
  , (2015, 4, 14)
  , (2016, 4, 13)
  , (2017, 4, 14)
  , (2018, 4, 14)
  , (2019, 4, 14)
  , (2020, 4, 13)
  , (2021, 4, 14)
  , (2022, 4, 14)
  , (2023, 4, 14)
  , (2024, 4, 13)
  , (2025, 4, 14)
  , (2026, 4, 14)
  , (2027, 4, 14)
  , (2028, 4, 13)
  , (2029, 4, 14)
  , (2030, 4, 14)
  ]

purim :: TimeData
purim = computedDays purim'

purim' :: [TimeObject]
purim' = mapMaybe toTimeObjectM
  [ (1950, 3, 2)
  , (1951, 3, 21)
  , (1952, 3, 10)
  , (1953, 2, 28)
  , (1954, 3, 18)
  , (1955, 3, 7)
  , (1956, 2, 25)
  , (1957, 3, 16)
  , (1958, 3, 5)
  , (1959, 3, 23)
  , (1960, 3, 12)
  , (1961, 3, 1)
  , (1962, 3, 19)
  , (1963, 3, 9)
  , (1964, 2, 26)
  , (1965, 3, 17)
  , (1966, 3, 5)
  , (1967, 3, 25)
  , (1968, 3, 13)
  , (1969, 3, 3)
  , (1970, 3, 21)
  , (1971, 3, 10)
  , (1972, 2, 28)
  , (1973, 3, 17)
  , (1974, 3, 7)
  , (1975, 2, 24)
  , (1976, 3, 15)
  , (1977, 3, 3)
  , (1978, 3, 22)
  , (1979, 3, 12)
  , (1980, 3, 1)
  , (1981, 3, 19)
  , (1982, 3, 8)
  , (1983, 2, 26)
  , (1984, 3, 17)
  , (1985, 3, 6)
  , (1986, 3, 24)
  , (1987, 3, 14)
  , (1988, 3, 2)
  , (1989, 3, 20)
  , (1990, 3, 10)
  , (1991, 2, 27)
  , (1992, 3, 18)
  , (1993, 3, 6)
  , (1994, 2, 24)
  , (1995, 3, 15)
  , (1996, 3, 4)
  , (1997, 3, 22)
  , (1998, 3, 11)
  , (1999, 3, 1)
  , (2000, 3, 20)
  , (2001, 3, 8)
  , (2002, 2, 27)
  , (2003, 3, 19)
  , (2004, 3, 6)
  , (2005, 3, 24)
  , (2006, 3, 13)
  , (2007, 3, 3)
  , (2008, 3, 20)
  , (2009, 3, 9)
  , (2010, 2, 27)
  , (2011, 3, 19)
  , (2012, 3, 7)
  , (2013, 2, 23)
  , (2014, 3, 15)
  , (2015, 3, 4)
  , (2016, 3, 23)
  , (2017, 3, 11)
  , (2018, 2, 28)
  , (2019, 3, 20)
  , (2020, 3, 9)
  , (2021, 2, 25)
  , (2022, 3, 16)
  , (2023, 3, 6)
  , (2024, 3, 23)
  , (2025, 3, 13)
  , (2026, 3, 2)
  , (2027, 3, 22)
  , (2028, 3, 11)
  , (2029, 2, 28)
  , (2030, 3, 18)
  , (2031, 3, 8)
  , (2032, 2, 25)
  , (2033, 3, 14)
  , (2034, 3, 4)
  , (2035, 3, 24)
  , (2036, 3, 12)
  , (2037, 2, 28)
  , (2038, 3, 20)
  , (2039, 3, 9)
  , (2040, 2, 27)
  , (2041, 3, 16)
  , (2042, 3, 5)
  , (2043, 3, 25)
  , (2044, 3, 12)
  , (2045, 3, 2)
  , (2046, 3, 21)
  , (2047, 3, 11)
  , (2048, 2, 27)
  , (2049, 3, 17)
  , (2050, 3, 7)
  ]

saraswatiJayanti :: TimeData
saraswatiJayanti = computedDays saraswatiJayanti'

saraswatiJayanti' :: [TimeObject]
saraswatiJayanti' = mapMaybe toTimeObjectM
  [ (2000, 2, 29)
  , (2001, 2, 17)
  , (2002, 3, 8)
  , (2003, 2, 26)
  , (2004, 2, 15)
  , (2005, 3, 5)
  , (2006, 2, 23)
  , (2007, 2, 12)
  , (2008, 3, 2)
  , (2009, 2, 19)
  , (2010, 2, 8)
  , (2011, 2, 27)
  , (2012, 2, 16)
  , (2013, 3, 7)
  , (2014, 2, 24)
  , (2015, 2, 14)
  , (2016, 3, 4)
  , (2017, 2, 21)
  , (2018, 2, 10)
  , (2019, 2, 28)
  , (2020, 2, 18)
  , (2021, 3, 8)
  , (2022, 2, 26)
  , (2023, 2, 15)
  , (2024, 3, 5)
  , (2025, 2, 23)
  , (2026, 2, 12)
  , (2027, 3 , 2)
  , (2028, 2, 19)
  , (2029, 2, 8)
  , (2030, 2, 27)
  ]

rabindraJayanti :: TimeData
rabindraJayanti = computedDays rabindraJayanti'

rabindraJayanti' :: [TimeObject]
rabindraJayanti' = mapMaybe toTimeObjectM
  [ (2000, 5, 8)
  , (2001, 5, 9)
  , (2002, 5, 9)
  , (2003, 5, 9)
  , (2004, 5, 8)
  , (2005, 5, 9)
  , (2006, 5, 9)
  , (2007, 5, 9)
  , (2008, 5, 8)
  , (2009, 5, 9)
  , (2010, 5, 9)
  , (2011, 5, 9)
  , (2012, 5, 8)
  , (2013, 5, 9)
  , (2014, 5, 9)
  , (2015, 5, 9)
  , (2016, 5, 8)
  , (2017, 5, 9)
  , (2018, 5, 9)
  , (2019, 5, 9)
  , (2020, 5, 8)
  , (2021, 5, 9)
  , (2022, 5, 9)
  , (2023, 5, 9)
  , (2024, 5, 8)
  , (2025, 5, 9)
  , (2026, 5, 9)
  , (2027, 5 ,9)
  , (2028, 5, 9)
  , (2029, 5, 9)
  , (2030, 5, 9)
  ]

ravidassJayanti :: TimeData
ravidassJayanti = computedDays ravidassJayanti'

ravidassJayanti' :: [TimeObject]
ravidassJayanti' = mapMaybe toTimeObjectM
  [ (2000, 2, 19)
  , (2001, 2, 8)
  , (2002, 2, 27)
  , (2003, 2, 16)
  , (2004, 2, 6)
  , (2005, 2, 24)
  , (2006, 2, 13)
  , (2007, 2, 2)
  , (2008, 2, 21)
  , (2009, 2, 9)
  , (2010, 1, 30)
  , (2011, 2, 18)
  , (2012, 2, 7)
  , (2013, 2, 25)
  , (2014, 2, 14)
  , (2015, 2, 3)
  , (2016, 2, 22)
  , (2017, 2, 10)
  , (2018, 1, 31)
  , (2019, 2, 19)
  , (2020, 2, 9)
  , (2021, 2, 27)
  , (2022, 2, 16)
  , (2023, 2, 5)
  , (2024, 2, 24)
  , (2025, 2, 12)
  , (2026, 2, 1)
  , (2027, 2 ,20)
  , (2028, 2, 10)
  , (2029, 1, 30)
  , (2030, 2, 18)
  ]

pargatDiwas :: TimeData
pargatDiwas = computedDays pargatDiwas'

pargatDiwas' :: [TimeObject]
pargatDiwas' = mapMaybe toTimeObjectM
  [ (2000, 10, 12)
  , (2001, 10, 31)
  , (2002, 10, 20)
  , (2003, 10, 9)
  , (2004, 10, 27)
  , (2005, 10, 16)
  , (2006, 10, 6)
  , (2007, 10, 25)
  , (2008, 10, 14)
  , (2009, 10, 3)
  , (2010, 10, 22)
  , (2011, 10, 11)
  , (2012, 10, 29)
  , (2013, 10, 18)
  , (2014, 10, 7)
  , (2015, 10, 26)
  , (2016, 10, 15)
  , (2017, 10, 5)
  , (2018, 10, 24)
  , (2019, 10, 13)
  , (2020, 10, 30)
  , (2021, 10, 19)
  , (2022, 10, 9)
  , (2023, 10, 28)
  , (2024, 10, 16)
  , (2025, 10, 6)
  , (2026, 10, 25)
  , (2027, 10, 14)
  , (2028, 10, 2)
  , (2029, 10, 21)
  , (2030, 10, 10)
  ]

ugadi :: TimeData
ugadi = computedDays ugadi'

ugadi' :: [TimeObject]
ugadi' = mapMaybe toTimeObjectM
  [ (2000, 4, 5)
  , (2001, 3, 26)
  , (2002, 4, 13)
  , (2003, 4, 2)
  , (2004, 3, 21)
  , (2005, 4, 9)
  , (2006, 3, 30)
  , (2007, 3, 19)
  , (2008, 4, 6)
  , (2009, 3, 27)
  , (2010, 3, 16)
  , (2011, 4, 4)
  , (2012, 3, 23)
  , (2013, 4, 11)
  , (2014, 3, 31)
  , (2015, 3, 21)
  , (2016, 4, 8)
  , (2017, 3, 28)
  , (2018, 3, 18)
  , (2019, 4, 6)
  , (2020, 3, 25)
  , (2021, 4, 13)
  , (2022, 4, 2)
  , (2023, 3, 22)
  , (2024, 4, 9)
  , (2025, 3, 30)
  , (2026, 3, 19)
  , (2027, 4, 7)
  , (2028, 3, 27)
  , (2029, 4, 14)
  , (2030, 4, 3)
  ]

ramaNavami :: TimeData
ramaNavami = computedDays ramaNavami'

ramaNavami' :: [TimeObject]
ramaNavami' = mapMaybe toTimeObjectM
  [ (2000, 4, 12)
  , (2001, 4, 2)
  , (2002, 4, 21)
  , (2003, 4, 11)
  , (2004, 3, 30)
  , (2005, 4, 18)
  , (2006, 4, 6)
  , (2007, 3, 27)
  , (2008, 4, 14)
  , (2009, 4, 3)
  , (2010, 3, 24)
  , (2011, 4, 12)
  , (2012, 4, 1)
  , (2013, 4, 19)
  , (2014, 4, 8)
  , (2015, 3, 28)
  , (2016, 4, 15)
  , (2017, 4, 5)
  , (2018, 3, 25)
  , (2019, 4, 14)
  , (2020, 4, 2)
  , (2021, 4, 21)
  , (2022, 4, 10)
  , (2023, 3, 30)
  , (2024, 4, 17)
  , (2025, 4, 6)
  , (2026, 3, 27)
  , (2027, 4, 15)
  , (2028, 4, 4)
  , (2029, 4, 23)
  , (2030, 4, 12)
  ]

superTuesday :: TimeData
superTuesday = computedDays superTuesday'

superTuesday' :: [TimeObject]
superTuesday' = mapMaybe toTimeObjectM
  [ (1984, 3, 13)
  , (1988, 3, 8)
  , (1992, 3, 10)
  , (1996, 3, 12)
  , (2000, 3, 7)
  , (2004, 3, 2)
  , (2008, 2, 5)
  , (2012, 3, 6)
  , (2016, 3, 1)
  , (2020, 3, 3)
  ]


-- 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 NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}

module Duckling.Time.Corpus
  ( datetime
  , datetimeHoliday
  , datetimeInterval
  , datetimeIntervalHoliday
  , datetimeOpenInterval
  , examples
  ) where

import Data.Aeson
import qualified Data.HashMap.Strict as H
import Data.Text (Text)
import qualified Data.Time.LocalTime.TimeZone.Series as Series
import Prelude
import Data.String

import Duckling.Resolve
import Duckling.Testing.Types hiding (examples)
import Duckling.Time.Types hiding (Month)
import Duckling.TimeGrain.Types hiding (add)
import Duckling.Types hiding (Entity(..))

datetime :: Datetime -> Grain -> Context -> TimeValue
datetime d g ctx = datetimeIntervalHolidayHelper (d, Nothing) g Nothing ctx

datetimeHoliday :: Datetime -> Grain -> Text -> Context -> TimeValue
datetimeHoliday d g h ctx =
  datetimeIntervalHolidayHelper (d, Nothing) g (Just h) ctx

datetimeInterval :: (Datetime, Datetime) -> Grain -> Context -> TimeValue
datetimeInterval (d1, d2) g ctx =
  datetimeIntervalHolidayHelper (d1, Just d2) g Nothing ctx

datetimeIntervalHoliday ::
  (Datetime, Datetime) -> Grain -> Text -> Context -> TimeValue
datetimeIntervalHoliday (d1, d2) g h ctx =
  datetimeIntervalHolidayHelper (d1, Just d2) g (Just h) ctx

datetimeIntervalHolidayHelper ::
  (Datetime, Maybe Datetime) -> Grain -> Maybe Text -> Context -> TimeValue
datetimeIntervalHolidayHelper (d1, md2) g hol ctx = TimeValue tv [tv] hol
  where
    DucklingTime (Series.ZoneSeriesTime _ tzSeries) = referenceTime ctx
    tv = timeValue tzSeries TimeObject {start = dt d1, end = d, grain = g}
    d = case md2 of
      Nothing -> Nothing
      Just d2 -> Just $ dt d2

datetimeOpenInterval
  :: IntervalDirection -> Datetime -> Grain -> Context -> TimeValue
datetimeOpenInterval dir d g ctx = TimeValue tv [tv] Nothing
  where
    DucklingTime (Series.ZoneSeriesTime _ tzSeries) = referenceTime ctx
    tv = openInterval tzSeries dir TimeObject
      {start = dt d, end = Nothing, grain = g}

check :: ToJSON a => (Context -> a) -> TestPredicate
check f context Resolved{rval = RVal _ v} = case toJSON v of
  Object o -> deleteValues (toJSON (f context)) == deleteValues (Object o)
  _ -> False
  where
    deleteValues :: Value -> Value
    deleteValues (Object o) = Object $ H.delete "values" o
    deleteValues _ = Object H.empty

examples :: ToJSON a => (Context -> a) -> [Text] -> [Example]
examples f = examplesCustom (check f)


-- 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 TupleSections #-}

module Duckling.Time.Helpers
  ( -- Patterns
    hasNoDirection, isADayOfWeek, isAMonth, isAnHourOfDay, isAPartOfDay
  , isATimeOfDay, isDurationGreaterThan, isDOMInteger, isDOMOrdinal, isDOMValue
  , isGrainFinerThan, isGrainCoarserThan, isGrainOfTime
  , isIntegerBetween, isNotLatent , isOrdinalBetween, isMidnightOrNoon
  , isOkWithThisNext, sameGrain, hasTimezone, hasNoTimezone, today
    -- Production
  , cycleLastOf, cycleN, cycleNth, cycleNthAfter, dayOfMonth, dayOfWeek
  , durationAfter, durationAgo, durationBefore, mkOkForThisNext, form, hour
  , hourMinute, hourMinuteSecond, inDuration, intersect, intersectDOM, interval
  , inTimezone, longWEBefore, minute, minutesAfter, minutesBefore, mkLatent
  , month, monthDay, notLatent, now, nthDOWOfMonth, partOfDay, predLastOf
  , predNth, predNthAfter, predNthClosest, season, second, timeOfDayAMPM
  , weekday, weekend, workweek, withDirection, year, yearMonthDay, tt, durationIntervalAgo
  , inDurationInterval, intersectWithReplacement, yearADBC, yearMonth
  , predEveryNDaysFrom, timeCycle
    -- Other
  , getIntValue, timeComputed, toTimeObjectM
  -- Rule constructors
  , mkRuleInstants
  , mkRuleDaysOfWeek, mkRuleDaysOfWeekLatent
  , mkRuleMonths, mkRuleMonthsWithLatent
  , mkRuleSeasons, mkRuleHolidays, mkRuleHolidays'
  ) where

import Control.Applicative ((<|>))
import Data.Maybe
import Data.Ord (comparing)
import Data.Text (Text)
import Data.Tuple.Extra (both)
import Prelude hiding (pred)
import qualified Data.Time as Time
import qualified Data.Time.LocalTime.TimeZone.Series as Series

import Duckling.Dimensions.Types
import Duckling.Duration.Types (DurationData (DurationData))
import Duckling.Ordinal.Types (OrdinalData (OrdinalData))
import Duckling.Time.TimeZone.Parse (parseTimezone)
import Duckling.Time.Types
  ( TimeData(TimeData)
  , mkSeriesPredicate
  , mkSecondPredicate
  , mkMinutePredicate
  , mkHourPredicate
  , mkAMPMPredicate
  , mkMonthPredicate
  , mkDayOfTheWeekPredicate
  , mkDayOfTheMonthPredicate
  , mkYearPredicate
  , mkIntersectPredicate
  , mkTimeIntervalsPredicate
  , mkReplaceIntersectPredicate
  , runPredicate
  , AMPM(..)
  )
import Duckling.Types
  ( Predicate
  , Token(..)
  , regex
  , Rule
  )
import qualified Duckling.Types as Types
import qualified Duckling.Duration.Types as TDuration
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

getIntValue :: Token -> Maybe Int
getIntValue (Token Numeral nd) = TNumeral.getIntValue $ TNumeral.value nd
getIntValue (Token Ordinal OrdinalData {TOrdinal.value = x}) = Just x
getIntValue _ = Nothing

timeNegPeriod :: DurationData -> DurationData
timeNegPeriod (DurationData v g) = DurationData
  {TDuration.grain = g, TDuration.value = negate v}

timeShiftPeriod :: Int -> DurationData -> DurationData
timeShiftPeriod n dd@DurationData{TDuration.value = v} =
  dd{TDuration.value = v + n}

-- -----------------------------------------------------------------
-- Time predicates

timeComputed :: [TTime.TimeObject] -> TTime.Predicate
timeComputed xs = mkSeriesPredicate series
  where
    series t _ = (reverse start, end)
      where
        (start, end) = span (`TTime.timeBefore` t) xs

timeCycle :: TG.Grain -> TTime.Predicate
timeCycle grain = mkSeriesPredicate series
  where
  series t _ = TTime.timeSequence grain 1 $ TTime.timeRound t grain

timeSecond :: Int -> TTime.Predicate
timeSecond n = mkSecondPredicate n

timeMinute :: Int -> TTime.Predicate
timeMinute n = mkMinutePredicate n

timeHour :: Bool -> Int -> TTime.Predicate
timeHour is12H n = mkHourPredicate is12H n

timeDayOfWeek :: Int -> TTime.Predicate
timeDayOfWeek n = mkDayOfTheWeekPredicate n

timeDayOfMonth :: Int -> TTime.Predicate
timeDayOfMonth n = mkDayOfTheMonthPredicate n

timeMonth :: Int -> TTime.Predicate
timeMonth n = mkMonthPredicate n

timeYear :: Int -> TTime.Predicate
timeYear n = mkYearPredicate n

-- | Takes `n` cycles of `f`
takeN :: Int -> Bool -> TTime.Predicate -> TTime.Predicate
takeN n notImmediate f = mkSeriesPredicate series
  where
  series t context =
    case slot of
      Just nth -> if TTime.timeStartsBeforeTheEndOf t nth
        then ([], [nth])
        else ([nth], [])
      Nothing -> ([], [])
    where
      baseTime = TTime.refTime context
      (past, future) = runPredicate f baseTime context
      fut = case future of
        (ahead:rest)
          | notImmediate && isJust (TTime.timeIntersect ahead baseTime) -> rest
        _ -> future
      slot = if n >= 0
        then case fut of
          (start:_) -> case drop n fut of
            (end:_) -> Just $ TTime.timeInterval TTime.Open start end
            _ -> Nothing
          _ -> Nothing
        else case past of
          (end:_) -> case drop ((- n) - 1) past of
            (start:_) -> Just $ TTime.timeInterval TTime.Closed start end
            _ -> Nothing
          _ -> Nothing

-- | -1 is the first element in the past
-- | 0 is the first element in the future
takeNth :: Int -> Bool -> TTime.Predicate -> TTime.Predicate
takeNth n notImmediate f = mkSeriesPredicate series
  where
  series t context =
    case rest of
      [] -> ([], [])
      (nth:_) -> if TTime.timeStartsBeforeTheEndOf t nth
        then ([], [nth])
        else ([nth], [])
    where
      baseTime = TTime.refTime context
      (past, future) = runPredicate f baseTime context
      rest = if n >= 0
        then case future of
          (ahead:_) | notImmediate && isJust (TTime.timeIntersect ahead baseTime)
            -> drop (n + 1) future
          _ -> drop n future
        else drop (- (n + 1)) past

-- | Like `takeNth`, but takes the nth cyclic predicate after `basePred`
takeNthAfter
  :: Int
  -> Bool
  -> TTime.Predicate
  -> TTime.Predicate
  -> TTime.Predicate
takeNthAfter n notImmediate cyclicPred basePred =
  mkSeriesPredicate $! TTime.timeSeqMap False f basePred
  where
    f t ctx =
      let (past, future) = runPredicate cyclicPred t ctx
          rest = if n >= 0
                   then case future of
                     (ahead:_) | notImmediate && TTime.timeBefore ahead t
                       -> drop (n + 1) future
                     _ -> drop n future
                   else drop (- (n + 1)) past
      in case rest of
           [] -> Nothing
           (nth:_) -> Just nth

-- | Take the nth closest value to `basePred` among those yielded by
-- `cyclicPred`.
-- n = 0 is the closest value, n = 1 is the second closest value, etc.
-- n < 0 is treated as n = 0.
takeNthClosest :: Int -> TTime.Predicate -> TTime.Predicate -> TTime.Predicate
takeNthClosest n cyclicPred basePred =
  mkSeriesPredicate $! TTime.timeSeqMap False f basePred
  where
  f t ctx = mth (n `max` 0) past future Nothing
    where
    (past, future) = runPredicate cyclicPred t ctx
    mth m pa fu res
      | m < 0 = res
      | otherwise = case comparing against x y of
          GT -> mth (m-1) (tailSafe pa) fu x
          _ -> mth (m-1) pa (tailSafe fu) y
      where (x,y) = both listToMaybe (pa,fu)
    against :: Maybe TTime.TimeObject -> Maybe Time.NominalDiffTime
    against = fmap (negate . TTime.diffStartTime t)
    tailSafe :: [a] -> [a]
    tailSafe (_:xs) = xs
    tailSafe [] = []

-- | Takes the last occurrence of `cyclicPred` within `basePred`.
takeLastOf :: TTime.Predicate -> TTime.Predicate -> TTime.Predicate
takeLastOf cyclicPred basePred =
  mkSeriesPredicate $! TTime.timeSeqMap False f basePred
  where
    f :: TTime.TimeObject -> TTime.TimeContext -> Maybe TTime.TimeObject
    f t ctx =
      case runPredicate cyclicPred (TTime.timeStartingAtTheEndOf t) ctx of
        (nth:_, _) -> Just nth
        _ -> Nothing

-- | Assumes the grain of `pred1` is smaller than the one of `pred2`
timeCompose :: TTime.Predicate -> TTime.Predicate -> TTime.Predicate
timeCompose pred1 pred2 = mkIntersectPredicate pred1 pred2

timeComposeWithReplacement
  :: TTime.Predicate -> TTime.Predicate -> TTime.Predicate -> TTime.Predicate
timeComposeWithReplacement pred1 pred2 pred3 =
  mkReplaceIntersectPredicate pred1 pred2 pred3

addDuration :: DurationData -> TTime.TimeObject -> TTime.TimeObject
addDuration (DurationData n g) t = TTime.timePlus t g $ toInteger n

mergeDuration :: TTime.Predicate -> DurationData -> TTime.Predicate
mergeDuration pred1 dd@(DurationData _ g) =
  mkSeriesPredicate $! TTime.timeSeqMap False f pred1
  where
    f x@TTime.TimeObject{TTime.grain = tg} _ = Just $ addDuration dd t'
      where
        g' = min tg g
        t' = if g' == tg then x else TTime.timeRound x g'

shiftDuration :: TTime.Predicate -> DurationData -> TTime.Predicate
shiftDuration pred1 dd@(DurationData _ g) =
  mkSeriesPredicate $! TTime.timeSeqMap False f pred1
  where
    f x _ = Just $ addDuration dd $ TTime.timeRound x $ TG.lower g

shiftTimezone :: Series.TimeZoneSeries -> TTime.Predicate -> TTime.Predicate
shiftTimezone providedSeries pred1 =
  mkSeriesPredicate $! TTime.timeSeqMap False f pred1
  where
    f x@(TTime.TimeObject s _ _) ctx =
      let Time.TimeZone ctxOffset _ _ =
            Series.timeZoneFromSeries (TTime.tzSeries ctx) s
          Time.TimeZone providedOffset _ _ =
            Series.timeZoneFromSeries providedSeries s
      -- This forgets about TTime.end, but it's OK since we act on time-of-days.
      in Just $ TTime.timePlus x TG.Minute $ toInteger $
           ctxOffset - providedOffset

-- -----------------------------------------------------------------
-- Patterns

isGrainFinerThan :: TG.Grain -> Predicate
isGrainFinerThan value (Token Time TimeData{TTime.timeGrain = g}) = g < value
isGrainFinerThan _ _ = False

isGrainCoarserThan :: TG.Grain -> Predicate
isGrainCoarserThan value (Token Time TimeData{TTime.timeGrain = g}) = g > value
isGrainCoarserThan _ _ = False

isGrainOfTime :: TG.Grain -> Predicate
isGrainOfTime value (Token Time TimeData{TTime.timeGrain = g}) = g == value
isGrainOfTime _ _ = False

sameGrain :: TimeData -> TimeData -> Bool
sameGrain TimeData{TTime.timeGrain = g} TimeData{TTime.timeGrain = h} = g == h

hasTimezone :: Predicate
hasTimezone (Token Time TimeData{TTime.hasTimezone = tz}) = tz
hasTimezone _ = False

hasNoTimezone :: Predicate
hasNoTimezone = not . hasTimezone

isADayOfWeek :: Predicate
isADayOfWeek (Token Time td) = case TTime.form td of
  Just TTime.DayOfWeek -> True
  _ -> False
isADayOfWeek _ = False

isATimeOfDay :: Predicate
isATimeOfDay (Token Time td) = case TTime.form td of
  Just (TTime.TimeOfDay _ _) -> True
  _ -> False
isATimeOfDay _ = False

isAPartOfDay :: Predicate
isAPartOfDay (Token Time td) = case TTime.form td of
  Just TTime.PartOfDay -> True
  _ -> False
isAPartOfDay _ = False

isAMonth :: Predicate
isAMonth (Token Time td) = case TTime.form td of
  Just (TTime.Month _) -> True
  _ -> False
isAMonth _ = False

isAnHourOfDay :: Predicate
isAnHourOfDay (Token Time td) = case TTime.form td of
  Just (TTime.TimeOfDay (Just _) _) | TTime.timeGrain td > TG.Minute -> True
  _ -> False
isAnHourOfDay _ = False

isMidnightOrNoon :: Predicate
isMidnightOrNoon (Token Time td) = case TTime.form td of
  Just (TTime.TimeOfDay (Just x) _) -> x == 0 || x == 12
  _ -> False
isMidnightOrNoon _ = False

isNotLatent :: Predicate
isNotLatent (Token Time td) = not $ TTime.latent td
isNotLatent _ = False

hasNoDirection :: Predicate
hasNoDirection (Token Time td) = isNothing $ TTime.direction td
hasNoDirection _ = False

isIntegerBetween :: Int -> Int -> Predicate
isIntegerBetween low high (Token Numeral nd) = TNumeral.okForAnyTime nd
  && TNumeral.isIntegerBetween (TNumeral.value nd) low high
isIntegerBetween _ _ _ = False

isOrdinalBetween :: Int -> Int -> Predicate
isOrdinalBetween low high (Token Ordinal od) =
  TOrdinal.isBetween (TOrdinal.value od) low high
isOrdinalBetween _ _ _ = False

isDurationGreaterThan :: TG.Grain -> Predicate
isDurationGreaterThan value (Token Duration DurationData{TDuration.grain = grain}) = grain > value
isDurationGreaterThan _ _ = False

isDOMOrdinal :: Predicate
isDOMOrdinal = isOrdinalBetween 1 31

isDOMInteger :: Predicate
isDOMInteger = isIntegerBetween 1 31

isDOMValue :: Predicate
isDOMValue = or . sequence [isDOMOrdinal, isDOMInteger]

isOkWithThisNext :: Predicate
isOkWithThisNext (Token Time TimeData {TTime.okForThisNext = True}) = True
isOkWithThisNext _ = False

-- -----------------------------------------------------------------
-- Production

-- Pass the interval second
intersect :: TimeData -> TimeData -> Maybe TimeData
intersect td1 td2 =
  case intersect' (td1, td2) of
    TTime.TimeData { TTime.timePred = pred }
      | TTime.isEmptyPredicate pred -> Nothing
    res -> Just res

intersectWithReplacement :: TimeData -> TimeData -> TimeData -> Maybe TimeData
intersectWithReplacement
  (TimeData pred1 _ g1 _ _ _ _ h1 _)
  (TimeData pred2 _ g2 _ _ _ _ h2 _)
  (TimeData pred3 _ g3 _ _ _ _ h3 _)
  | g1 == g2 && g2 == g3 = Just $ TTime.timedata'
    { TTime.timePred = timeComposeWithReplacement pred1 pred2 pred3
    , TTime.timeGrain = g1
    , TTime.direction = Nothing
    , TTime.holiday = h1 <|> h2 <|> h3
    }
  | otherwise = Nothing

intersect' :: (TimeData, TimeData) -> TimeData
intersect' (TimeData pred1 _ g1 _ _ d1 _ h1 _, TimeData pred2 _ g2 _ _ d2 _ h2 _)
  | g1 < g2 = TTime.timedata'
    { TTime.timePred = timeCompose pred1 pred2
    , TTime.timeGrain = g1
    , TTime.direction = d1 <|> d2
    , TTime.holiday = h1 <|> h2
    }
  | otherwise = TTime.timedata'
    { TTime.timePred = timeCompose pred2 pred1
    , TTime.timeGrain = g2
    , TTime.direction = d1 <|> d2
    , TTime.holiday = h1 <|> h2
    }

now :: TimeData
now = td {TTime.timeGrain = TG.NoGrain}
  where
    td = cycleNth TG.Second 0

today :: TimeData
today = cycleNth TG.Day 0

hour :: Bool -> Int -> TimeData
hour is12H n = timeOfDay (Just n) is12H $ TTime.timedata'
  {TTime.timePred = timeHour is12H n, TTime.timeGrain = TG.Hour}

minute :: Int -> TimeData
minute n = TTime.timedata'
  {TTime.timePred = timeMinute n, TTime.timeGrain = TG.Minute}

second :: Int -> TimeData
second n = TTime.timedata'
  {TTime.timePred = timeSecond n, TTime.timeGrain = TG.Second}

dayOfWeek :: Int -> TimeData
dayOfWeek n = form TTime.DayOfWeek $ TTime.timedata'
  { TTime.timePred = timeDayOfWeek n
  , TTime.timeGrain = TG.Day
  , TTime.notImmediate = True
  }

dayOfMonth :: Int -> TimeData
dayOfMonth n = TTime.timedata'
  {TTime.timePred = timeDayOfMonth n, TTime.timeGrain = TG.Day}

month :: Int -> TimeData
month n = form TTime.Month {TTime.month = n} $ TTime.timedata'
  {TTime.timePred = timeMonth n, TTime.timeGrain = TG.Month}

-- | Converts 2-digits to a year between 1950 and 2050
year :: Int -> TimeData
year n = TTime.timedata'{TTime.timePred = timeYear y, TTime.timeGrain = TG.Year}
  where
    y = if n <= 99 then mod (n + 50) 100 + 2000 - 50 else n

yearADBC :: Int -> TimeData
yearADBC n =
  TTime.timedata'{TTime.timePred = timeYear n, TTime.timeGrain = TG.Year}

yearMonth :: Int -> Int -> TimeData
yearMonth y m = intersect' (year y, month m)

yearMonthDay :: Int -> Int -> Int -> TimeData
yearMonthDay y m d = intersect' (yearMonth y m, dayOfMonth d)

monthDay :: Int -> Int -> TimeData
monthDay m d = intersect' (month m, dayOfMonth d)

hourMinute :: Bool -> Int -> Int -> TimeData
hourMinute is12H h m = timeOfDay (Just h) is12H $
  intersect' (hour is12H h, minute m)

hourMinuteSecond :: Bool -> Int -> Int -> Int -> TimeData
hourMinuteSecond is12H h m s = timeOfDay (Just h) is12H $
  intersect' (intersect' (hour is12H h, minute m), second s)

season :: TimeData
season = TTime.timedata'
  { TTime.timePred = TTime.seasonPredicate
  , TTime.timeGrain = TG.Day
  }

-- | Note that this function is not the counterpart of `weekend`.
-- `weekend` returns an interval while `weekday` returns a single day.
weekday :: TimeData
weekday = TTime.timedata'
  { TTime.timePred = TTime.weekdayPredicate
  , TTime.timeGrain = TG.Day
  }

cycleN :: Bool -> TG.Grain -> Int -> TimeData
cycleN notImmediate grain n = TTime.timedata'
  { TTime.timePred = takeN n notImmediate $ timeCycle grain
  , TTime.timeGrain = grain
  }

cycleNth :: TG.Grain -> Int -> TimeData
cycleNth grain n = TTime.timedata'
  {TTime.timePred = takeNth n False $ timeCycle grain, TTime.timeGrain = grain}

cycleNthAfter :: Bool -> TG.Grain -> Int -> TimeData -> TimeData
cycleNthAfter notImmediate grain n TimeData {TTime.timePred = p} =
  TTime.timedata'
    { TTime.timePred = takeNthAfter n notImmediate (timeCycle grain) p
    , TTime.timeGrain = grain
    }

cycleLastOf :: TG.Grain -> TimeData -> TimeData
cycleLastOf grain TimeData {TTime.timePred = p} = TTime.timedata'
  { TTime.timePred = takeLastOf (timeCycle grain) p
  , TTime.timeGrain = grain
  }

-- Generalized version of cycleLastOf with custom predicate
predLastOf :: TimeData -> TimeData -> TimeData
predLastOf TimeData {TTime.timePred = cyclicPred, TTime.timeGrain = g} base =
  TTime.timedata'
    { TTime.timePred = takeLastOf cyclicPred $ TTime.timePred base
    , TTime.timeGrain = g
    }

-- Generalized version of cycleNth with custom predicate
predNth :: Int -> Bool -> TimeData -> TimeData
predNth n notImmediate TimeData
  {TTime.timePred = p, TTime.timeGrain = g, TTime.holiday = h} =
  TTime.timedata'
    {TTime.timePred = takeNth n notImmediate p
    , TTime.timeGrain = g
    , TTime.holiday = h}

-- Generalized version of `cycleNthAfter` with custom predicate
predNthAfter :: Int -> TimeData -> TimeData -> TimeData
predNthAfter n TimeData
  {TTime.timePred = p, TTime.timeGrain = g, TTime.holiday = h} base =
  TTime.timedata'
    { TTime.timePred = takeNthAfter n True p $ TTime.timePred base
    , TTime.timeGrain = g
    , TTime.holiday = h
    }

-- This function can be used to express predicates invoving "closest",
-- such as "the second closest Monday to Oct 5th"
predNthClosest :: Int -> TimeData -> TimeData -> TimeData
predNthClosest n TimeData
  {TTime.timePred = p, TTime.timeGrain = g, TTime.holiday = h} base =
  TTime.timedata'
    { TTime.timePred = takeNthClosest n p $ TTime.timePred base
    , TTime.timeGrain = g
    , TTime.holiday = h
    }

-- This function is used for periodic events, for example,
-- "every 365 days" or "every 8 years".
-- `given` is a known example of the event.
-- Do not export
predEveryFrom :: TG.Grain -> Int -> TTime.TimeObject -> TimeData
predEveryFrom periodGrain period given = TTime.timedata'
    { TTime.timePred = TTime.periodicPredicate periodGrain period given
    , TTime.timeGrain = TTime.grain given
    }

predEveryNDaysFrom :: Int -> (Integer, Int, Int) -> Maybe TimeData
predEveryNDaysFrom period given = do
  date <- toTimeObjectM given
  return $ predEveryFrom TG.Day period date

toTimeObjectM :: (Integer, Int, Int) -> Maybe TTime.TimeObject
toTimeObjectM (y, m, d) = do
  day <- Time.fromGregorianValid y m d
  return TTime.TimeObject
    { TTime.start = Time.UTCTime day 0
    , TTime.grain = TG.Day
    , TTime.end = Nothing
    }

interval' :: TTime.TimeIntervalType -> (TimeData, TimeData) -> TimeData
interval' intervalType (TimeData p1 _ g1 _ _ _ _ _ _, TimeData p2 _ g2 _ _ _ _ _ _) =
  TTime.timedata'
    { TTime.timePred = mkTimeIntervalsPredicate intervalType' p1 p2
    , TTime.timeGrain = min g1 g2
    }
    where
      intervalType'
        | g1 == g2 && g1 == TG.Day = TTime.Closed
        | otherwise = intervalType

interval :: TTime.TimeIntervalType -> TimeData -> TimeData -> Maybe TimeData
interval intervalType td1 td2 =
  case interval' intervalType (td1, td2) of
    TTime.TimeData { TTime.timePred = pred }
      | TTime.isEmptyPredicate pred -> Nothing
    res -> Just res

mkOkForThisNext :: TimeData -> TimeData
mkOkForThisNext td = td {TTime.okForThisNext = True}

durationAgo :: DurationData -> TimeData
durationAgo dd = inDuration $ timeNegPeriod dd

durationIntervalAgo :: DurationData -> TimeData
durationIntervalAgo dd = inDurationInterval $ timeNegPeriod dd

durationAfter :: DurationData -> TimeData -> TimeData
durationAfter dd TimeData {TTime.timePred = pred1, TTime.timeGrain = g} =
  TTime.timedata'
    { TTime.timePred = if g == TG.NoGrain
      then shiftDuration pred1 dd
      else mergeDuration pred1 dd
    , TTime.timeGrain = TDuration.grain dd
    }

durationBefore :: DurationData -> TimeData -> TimeData
durationBefore dd pred1 = durationAfter (timeNegPeriod dd) pred1

inDuration :: DurationData -> TimeData
inDuration dd = TTime.timedata'
  { TTime.timePred = shiftDuration t dd
  , TTime.timeGrain = TDuration.grain dd
  }
  where
    t = takeNth 0 False $ timeCycle TG.Second

inDurationInterval :: DurationData -> TimeData
inDurationInterval dd = interval' TTime.Open
  (inDuration dd, inDuration $ timeShiftPeriod 1 dd)

inTimezone :: Text -> TimeData -> Maybe TimeData
inTimezone input td@TimeData {TTime.timePred = p} = do
  tz <- parseTimezone input
  Just $ td {TTime.timePred = shiftTimezone (Series.TimeZoneSeries tz []) p, TTime.hasTimezone = True}

withHoliday :: Text -> TimeData -> TimeData
withHoliday n td = td {TTime.holiday = Just n}

mkLatent :: TimeData -> TimeData
mkLatent td = td {TTime.latent = True}

notLatent :: TimeData -> TimeData
notLatent td = td {TTime.latent = False}

form :: TTime.Form -> TimeData -> TimeData
form f td = td {TTime.form = Just f}

partOfDay :: TimeData -> TimeData
partOfDay td = form TTime.PartOfDay td

timeOfDay :: Maybe Int -> Bool -> TimeData -> TimeData
timeOfDay h is12H = form TTime.TimeOfDay {TTime.hours = h, TTime.is12H = is12H}

timeOfDayAMPM :: Bool -> TimeData -> TimeData
timeOfDayAMPM isAM tod = timeOfDay Nothing False $ intersect' (tod, ampm)
  where
    ampm = TTime.timedata'
           { TTime.timePred = ampmPred
           , TTime.timeGrain = TG.Hour
           }
    ampmPred = if isAM then mkAMPMPredicate AM else mkAMPMPredicate PM

withDirection :: TTime.IntervalDirection -> TimeData -> TimeData
withDirection dir td = td {TTime.direction = Just dir}

longWEBefore :: TimeData -> TimeData
longWEBefore monday = interval' TTime.Open (start, end)
  where
    start = intersect' (fri, hour False 18)
    end = intersect' (tue, hour False 0)
    fri = cycleNthAfter False TG.Day (- 3) monday
    tue = cycleNthAfter False TG.Day 1 monday

weekend :: TimeData
weekend = interval' TTime.Open (fri, mon)
  where
    fri = intersect' (dayOfWeek 5, hour False 18)
    mon = intersect' (dayOfWeek 1, hour False 0)

workweek :: TimeData
workweek = interval' TTime.Open (mon, fri)
  where
    mon = intersect' (dayOfWeek 1, hour False 10)
    fri = intersect' (dayOfWeek 5, hour False 18)

-- Zero-indexed weeks, Monday is 1
-- Use `predLastOf` for last day of week of month
nthDOWOfMonth :: Int -> Int -> Int -> TimeData
nthDOWOfMonth n dow m = predNthAfter (n - 1) dow_ month_
  where
    dow_ = dayOfWeek dow
    month_ = month m

intersectDOM :: TimeData -> Token -> Maybe TimeData
intersectDOM td token = do
  n <- getIntValue token
  intersect (dayOfMonth n) td

minutesBefore :: Int -> TimeData -> Maybe TimeData
minutesBefore n TimeData {TTime.form = Just (TTime.TimeOfDay (Just 0) is12H)} =
  Just $ hourMinute is12H 23 (60 - n)
minutesBefore n TimeData {TTime.form = Just (TTime.TimeOfDay (Just 1) True)} =
  Just $ hourMinute True 12 (60 - n)
minutesBefore n TimeData {TTime.form = Just (TTime.TimeOfDay (Just h) is12H)} =
  Just $ hourMinute is12H (h - 1) (60 - n)
minutesBefore _ _ = Nothing

minutesAfter :: Int -> TimeData -> Maybe TimeData
minutesAfter n TimeData {TTime.form = Just (TTime.TimeOfDay (Just h) is12H)} =
  Just $ hourMinute is12H h n
minutesAfter _ _ = Nothing

-- | Convenience helper to return a time token from a rule
tt :: TimeData -> Maybe Token
tt = Just . Token Time

-- | Rule constructors
mkSingleRegexRule :: Text -> String -> Maybe Token -> Rule
mkSingleRegexRule name pattern token = Types.Rule
  { Types.name = name
  , Types.pattern = [regex pattern]
  , Types.prod = const token
  }

mkRuleInstants :: [(Text, TG.Grain, Int, String)] -> [Rule]
mkRuleInstants = map go
  where
    go (name, grain, n, pattern) = mkSingleRegexRule name pattern $ tt $
      cycleNth grain n

mkRuleDaysOfWeek :: [(Text, String)] -> [Rule]
mkRuleDaysOfWeek daysOfWeek = zipWith go daysOfWeek [1..7]
  where
    go (name, pattern) i =
      mkSingleRegexRule name pattern $ tt $ mkOkForThisNext $ dayOfWeek i

mkRuleDaysOfWeekLatent :: [(Text, String)] -> [Rule]
mkRuleDaysOfWeekLatent daysOfWeek = zipWith go daysOfWeek [1..7]
  where
    go (name, pattern) i =
      mkSingleRegexRule name pattern $ tt $ mkLatent $ mkOkForThisNext $ dayOfWeek i

mkRuleMonths :: [(Text, String)] -> [Rule]
mkRuleMonths = mkRuleMonthsWithLatent . map (uncurry (,, False))

mkRuleMonthsWithLatent :: [(Text, String, Bool)] -> [Rule]
mkRuleMonthsWithLatent months  = zipWith go months [1..12]
  where
    go (name, pattern, latent) i =
      mkSingleRegexRule name pattern $ tt $ (if latent then mkLatent else id)
      $ mkOkForThisNext $ month i

mkRuleSeasons :: [(Text, String, TimeData, TimeData)] -> [Rule]
mkRuleSeasons = map go
  where
    go (name, pattern, start, end) = mkSingleRegexRule name pattern $
      Token Time <$> mkOkForThisNext <$> interval TTime.Open start end

mkRuleHolidays :: [(Text, String, TimeData)] -> [Rule]
mkRuleHolidays = map go
  where
    go (name, pattern, td) = mkSingleRegexRule name pattern $ tt
      $ withHoliday name $ mkOkForThisNext td

mkRuleHolidays' :: [(Text, String, Maybe TimeData)] -> [Rule]
mkRuleHolidays' = map go
  where
    go (name, pattern, maybeTimeData) = mkSingleRegexRule name pattern $ do
      timeData <- maybeTimeData
      tt $ withHoliday name $ mkOkForThisNext timeData