module Bookhound.Parsers.DateTime (date, time, timeZoneOffset, localDateTime, offsetDateTime, dateTime, year, day, month, hour, minute, second) where

import Bookhound.Parser            (Parser, satisfy, withErrorN)
import Bookhound.ParserCombinators (IsMatch (..), surroundedBy, (<#>), (|+),
                                    (|?))
import Bookhound.Parsers.Char      (colon, dash, digit, dot, plus)
import Control.Applicative

import Data.Maybe (fromMaybe)
import Data.Time  (Day, LocalTime (..), TimeOfDay (..), TimeZone,
                   ZonedTime (..), fromGregorian, minutesToTimeZone)


date :: Parser Day
date :: Parser Day
date = forall a. Int -> Text -> Parser a -> Parser a
withErrorN (-Int
1) Text
"Date" forall a b. (a -> b) -> a -> b
$
  Integer -> Int -> Int -> Day
fromGregorian forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Integer
year forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall a b. Parser a -> Parser b -> Parser b
surroundedBy Parser Char
dash Parser Int
month forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Parser Int
day


time :: Parser TimeOfDay
time :: Parser TimeOfDay
time = forall a. Int -> Text -> Parser a -> Parser a
withErrorN (-Int
1) Text
"Time" forall a b. (a -> b) -> a -> b
$
  do Int
h <- Parser Int
hour
     Int
m <- Parser Char
colon forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Parser Int
minute
     Int
s <- Parser Char
colon forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Parser Int
second
     Maybe String
decimals <- ((Parser Char
dot forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Parser String
secondDecimals) |?)
     pure $ Int -> Int -> Pico -> TimeOfDay
TimeOfDay Int
h Int
m forall a b. (a -> b) -> a -> b
$ forall a. Read a => String -> a
read (forall a. Show a => a -> String
show Int
s forall a. Semigroup a => a -> a -> a
<> forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap (String
"." ++) Maybe String
decimals)


timeZoneOffset :: Parser TimeZone
timeZoneOffset :: Parser TimeZone
timeZoneOffset = forall a. Int -> Text -> Parser a -> Parser a
withErrorN (-Int
1) Text
"Timezone Offset" forall a b. (a -> b) -> a -> b
$
  do Bool
pos <- (Bool
True forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ Parser Char
plus) forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (Bool
False forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ Parser Char
dash)
     Int
h <- Parser Int
hour
     Int
m <- forall a. a -> Maybe a -> a
fromMaybe Int
0 forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Parser Int
minute |?)
     pure $ Int -> TimeZone
minutesToTimeZone forall a b. (a -> b) -> a -> b
$ (if Bool
pos then Int
1 else (-Int
1)) forall a. Num a => a -> a -> a
* (Int
h forall a. Num a => a -> a -> a
* Int
60 forall a. Num a => a -> a -> a
+ Int
m)


localDateTime :: Parser LocalTime
localDateTime :: Parser LocalTime
localDateTime = forall a. Int -> Text -> Parser a -> Parser a
withErrorN (-Int
1) Text
"Local DateTime" forall a b. (a -> b) -> a -> b
$
  Day -> TimeOfDay -> LocalTime
LocalTime forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Parser Day
date forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* forall a. IsMatch a => [a] -> Parser a
oneOf [Char
'T', Char
't', Char
' ']) forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Parser TimeOfDay
time


offsetDateTime :: Parser ZonedTime
offsetDateTime :: Parser ZonedTime
offsetDateTime = forall a. Int -> Text -> Parser a -> Parser a
withErrorN (-Int
1) Text
"Offset DateTime" forall a b. (a -> b) -> a -> b
$
  LocalTime -> TimeZone -> ZonedTime
ZonedTime forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser LocalTime
localDateTime forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> (forall a. IsMatch a => a -> Parser a
is Char
' ' forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Parser TimeZone
timeZoneOffset)


dateTime :: Parser ZonedTime
dateTime :: Parser ZonedTime
dateTime = forall a. Int -> Text -> Parser a -> Parser a
withErrorN (-Int
1) Text
"DateTime" forall a b. (a -> b) -> a -> b
$
  ((LocalTime -> TimeZone -> ZonedTime
`ZonedTime` Int -> TimeZone
minutesToTimeZone Int
0) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser LocalTime
localDateTime forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* forall a. IsMatch a => a -> Parser a
is Char
'Z')
  forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser ZonedTime
offsetDateTime



year :: Parser Integer
year :: Parser Integer
year = forall a. Read a => String -> a
read forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Char
digit forall a. Parser a -> Int -> Parser [a]
<#> Int
4

day :: Parser Int
day :: Parser Int
day = forall a. Int -> Text -> Parser a -> Parser a
withErrorN (-Int
1) Text
"Day" forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> Parser a -> Parser a
satisfy (forall a. Ord a => a -> a -> a -> Bool
range Int
1 Int
31) forall a b. (a -> b) -> a -> b
$ forall a. Read a => String -> a
read forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Char
digit forall a. Parser a -> Int -> Parser [a]
<#> Int
2

month :: Parser Int
month :: Parser Int
month = forall a. Int -> Text -> Parser a -> Parser a
withErrorN (-Int
1) Text
"Month" forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> Parser a -> Parser a
satisfy (forall a. Ord a => a -> a -> a -> Bool
range Int
1 Int
12) forall a b. (a -> b) -> a -> b
$ forall a. Read a => String -> a
read forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Char
digit forall a. Parser a -> Int -> Parser [a]
<#> Int
2

hour :: Parser Int
hour :: Parser Int
hour = forall a. Int -> Text -> Parser a -> Parser a
withErrorN (-Int
1) Text
"Hour" forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> Parser a -> Parser a
satisfy (forall a. Ord a => a -> a -> a -> Bool
range Int
0 Int
23) forall a b. (a -> b) -> a -> b
$ forall a. Read a => String -> a
read forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Char
digit forall a. Parser a -> Int -> Parser [a]
<#> Int
2

minute :: Parser Int
minute :: Parser Int
minute = forall a. Int -> Text -> Parser a -> Parser a
withErrorN (-Int
1) Text
"Minute" forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> Parser a -> Parser a
satisfy (forall a. Ord a => a -> a -> a -> Bool
range Int
0 Int
59) forall a b. (a -> b) -> a -> b
$ forall a. Read a => String -> a
read forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Char
digit forall a. Parser a -> Int -> Parser [a]
<#> Int
2

second :: Parser Int
second :: Parser Int
second = forall a. Int -> Text -> Parser a -> Parser a
withErrorN (-Int
1) Text
"Second" forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> Parser a -> Parser a
satisfy (forall a. Ord a => a -> a -> a -> Bool
range Int
0 Int
59) forall a b. (a -> b) -> a -> b
$ forall a. Read a => String -> a
read forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Char
digit forall a. Parser a -> Int -> Parser [a]
<#> Int
2

secondDecimals :: Parser String
secondDecimals :: Parser String
secondDecimals = forall a. Int -> Text -> Parser a -> Parser a
withErrorN (-Int
1) Text
"Pico Seconds" forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> Parser a -> Parser a
satisfy ((forall a. Ord a => a -> a -> Bool
<= Int
12) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) a. Foldable t => t a -> Int
length) (Parser Char
digit |+)




range :: Ord a => a -> a -> a -> Bool
range :: forall a. Ord a => a -> a -> a -> Bool
range a
mn a
mx a
x = a
x forall a. Ord a => a -> a -> Bool
>= a
mn Bool -> Bool -> Bool
&& a
x forall a. Ord a => a -> a -> Bool
<= a
mx