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
12 {-# LANGUAGE FlexibleContexts #-}
13 {-# LANGUAGE NoImplicitPrelude #-}
14 {-# LANGUAGE OverloadedStrings #-}
16 module Gargantext.Text.Corpus.Parsers.Date.Parsec
19 import Control.Monad ((=<<))
20 import Data.Either (Either)
21 import Data.Fixed (Fixed (MkFixed))
22 import Data.Foldable (length)
23 import Data.String (String)
24 import Data.Text (Text, unpack)
25 import Data.Time (ZonedTime(..), LocalTime(..), TimeZone(..), TimeOfDay(..))
26 import Data.Time.Calendar (Day, fromGregorian)
27 import Gargantext.Prelude
28 import Prelude (toInteger, div, otherwise, (++))
29 import Text.Parsec.Error (ParseError)
30 import Text.Parsec.Prim (Stream, ParsecT)
31 import Text.Parsec.String (Parser)
32 import Text.ParserCombinators.Parsec (many1, noneOf, anyChar, char, oneOf)
33 import Text.XML.HXT.DOM.Util (decimalStringToInt)
34 import qualified Text.ParserCombinators.Parsec (parse)
36 -- | Permit to transform a String to an Int in a monadic context
37 wrapDST :: Monad m => String -> m Int
38 wrapDST = return . decimalStringToInt
40 -- | Generic parser which take at least one element not given in argument
41 many1NoneOf :: Stream s m Char => [Char] -> ParsecT s u m [Char]
42 many1NoneOf = (many1 . noneOf)
44 getMultiplicator :: Int -> Int
47 | otherwise = 10 * (getMultiplicator $ div a 10)
49 -- | Parser for date format y-m-d
50 parseGregorian :: Parser Day
52 y <- wrapDST =<< many1NoneOf ['-']
54 m <- wrapDST =<< many1NoneOf ['-']
56 d <- wrapDST =<< many1NoneOf ['T']
58 return $ fromGregorian (toInteger y) m d
60 ---- | Parser for time format h:m:s
61 parseTimeOfDay :: Parser TimeOfDay
63 h <- wrapDST =<< many1NoneOf [':']
65 m <- wrapDST =<< many1NoneOf [':']
67 r <- many1NoneOf ['.']
69 dec <- many1NoneOf ['+', '-']
70 let (nb, l) = (decimalStringToInt $ r ++ dec, length dec)
71 seconds = nb * 10^(12-l)
72 return $ TimeOfDay h m (MkFixed . toInteger $ seconds)
75 -- | Parser for timezone format +hh:mm
76 parseTimeZone :: Parser TimeZone
78 sign <- oneOf ['+', '-']
79 h <- wrapDST =<< many1NoneOf [':']
81 m <- wrapDST =<< (many1 $ anyChar)
82 let timeInMinute = if sign == '+' then h * 60 + m else -h * 60 - m
83 in return $ TimeZone timeInMinute False "CET"
85 ---- | Parser which use parseGregorian, parseTimeOfDay and parseTimeZone to create a ZonedTime
86 parseZonedTime :: Parser ZonedTime
91 return $ ZonedTime (LocalTime d (tod)) tz
93 ---- | Opposite of toRFC3339
94 fromRFC3339 :: Text -> Either ParseError ZonedTime
95 fromRFC3339 t = Text.ParserCombinators.Parsec.parse parseZonedTime "ERROR: Couldn't parse zoned time." input
96 where input = unpack t