2 Module : Gargantext.Text.Corpus.Parsers.Date
3 Description : Some utils to parse dates
4 Copyright : (c) CNRS 2017-present
5 License : AGPL + CECILL v3
6 Maintainer : team@gargantext.org
7 Stability : experimental
13 module Gargantext.Text.Corpus.Parsers.Date.Parsec
16 import Control.Monad ((=<<))
17 import Data.Either (Either)
18 import Data.Fixed (Fixed (MkFixed))
19 import Data.Foldable (length)
20 import Data.String (String)
21 import Data.Text (Text, unpack)
22 import Data.Time (ZonedTime(..), LocalTime(..), TimeZone(..), TimeOfDay(..))
23 import Data.Time.Calendar (Day, fromGregorian)
24 import Gargantext.Prelude
25 import Prelude (toInteger, div, otherwise, (++))
26 import Text.Parsec.Error (ParseError)
27 import Text.Parsec.Prim (Stream, ParsecT)
28 import Text.Parsec.String (Parser)
29 import Text.ParserCombinators.Parsec (many1, noneOf, anyChar, char, oneOf)
30 import Text.XML.HXT.DOM.Util (decimalStringToInt)
31 import qualified Text.ParserCombinators.Parsec (parse)
33 -- | Permit to transform a String to an Int in a monadic context
34 wrapDST :: Monad m => String -> m Int
35 wrapDST = return . decimalStringToInt
37 -- | Generic parser which take at least one element not given in argument
38 many1NoneOf :: Stream s m Char => [Char] -> ParsecT s u m [Char]
39 many1NoneOf = (many1 . noneOf)
41 getMultiplicator :: Int -> Int
44 | otherwise = 10 * (getMultiplicator $ div a 10)
46 -- | Parser for date format y-m-d
47 parseGregorian :: Parser Day
49 y <- wrapDST =<< many1NoneOf ['-']
51 m <- wrapDST =<< many1NoneOf ['-']
53 d <- wrapDST =<< many1NoneOf ['T']
55 return $ fromGregorian (toInteger y) m d
57 ---- | Parser for time format h:m:s
58 parseTimeOfDay :: Parser TimeOfDay
60 h <- wrapDST =<< many1NoneOf [':']
62 m <- wrapDST =<< many1NoneOf [':']
64 r <- many1NoneOf ['.']
66 dec <- many1NoneOf ['+', '-']
67 let (nb, l) = (decimalStringToInt $ r ++ dec, length dec)
68 seconds = nb * 10^(12-l)
69 return $ TimeOfDay h m (MkFixed . toInteger $ seconds)
72 -- | Parser for timezone format +hh:mm
73 parseTimeZone :: Parser TimeZone
75 sign <- oneOf ['+', '-']
76 h <- wrapDST =<< many1NoneOf [':']
78 m <- wrapDST =<< (many1 $ anyChar)
79 let timeInMinute = if sign == '+' then h * 60 + m else -h * 60 - m
80 in return $ TimeZone timeInMinute False "CET"
82 ---- | Parser which use parseGregorian, parseTimeOfDay and parseTimeZone to create a ZonedTime
83 parseZonedTime :: Parser ZonedTime
88 return $ ZonedTime (LocalTime d (tod)) tz
90 ---- | Opposite of toRFC3339
91 fromRFC3339 :: Text -> Either ParseError ZonedTime
92 fromRFC3339 t = Text.ParserCombinators.Parsec.parse parseZonedTime "ERROR: Couldn't parse zoned time." input
93 where input = unpack t