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

import Bookhound.Parser            (Parser, check, withError)
import Bookhound.ParserCombinators (IsMatch (..), within, (<#>), (<|>), (|+),
                                    (|?))
import Bookhound.Parsers.Char      (colon, dash, digit, plus)

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


date :: Parser Day
date :: Parser Day
date = forall a. String -> Parser a -> Parser a
withError String
"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
within 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. String -> Parser a -> Parser a
withError String
"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
       Integer
decimals <- forall a. a -> Maybe a -> a
fromMaybe Integer
0 forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ((Parser Char
colon forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Parser Integer
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
<> String
"." forall a. Semigroup a => a -> a -> a
<> forall a. Show a => a -> String
show Integer
decimals)


timeZoneOffset :: Parser TimeZone
timeZoneOffset :: Parser TimeZone
timeZoneOffset = forall a. String -> Parser a -> Parser a
withError String
"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 a. Parser a -> Parser a -> Parser 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 Char
colon forall (f :: * -> *) a b. Applicative f => f a -> f b -> 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. String -> Parser a -> Parser a
withError String
"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']) 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. String -> Parser a -> Parser a
withError String
"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
<*> Parser TimeZone
timeZoneOffset

dateTime :: Parser ZonedTime
dateTime :: Parser ZonedTime
dateTime = forall a. String -> Parser a -> Parser a
withError String
"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 a. Parser a -> Parser a -> Parser 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 -> Integer -> Parser [a]
<#> Integer
4

day :: Parser Int
day :: Parser Int
day = forall a. String -> (a -> Bool) -> Parser a -> Parser a
check String
"Day" (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 -> Integer -> Parser [a]
<#> Integer
2

month :: Parser Int
month :: Parser Int
month = forall a. String -> (a -> Bool) -> Parser a -> Parser a
check String
"Month" (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 -> Integer -> Parser [a]
<#> Integer
2

hour :: Parser Int
hour :: Parser Int
hour = forall a. String -> (a -> Bool) -> Parser a -> Parser a
check String
"Hour" (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 -> Integer -> Parser [a]
<#> Integer
2

minute :: Parser Int
minute :: Parser Int
minute = forall a. String -> (a -> Bool) -> Parser a -> Parser a
check String
"Minute" (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 -> Integer -> Parser [a]
<#> Integer
2

second :: Parser Int
second :: Parser Int
second = forall a. String -> (a -> Bool) -> Parser a -> Parser a
check String
"Second" (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 -> Integer -> Parser [a]
<#> Integer
2

secondDecimals :: Parser Integer
secondDecimals :: Parser Integer
secondDecimals = forall a. Read a => String -> a
read forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. String -> (a -> Bool) -> Parser a -> Parser a
check String
"Pico Seconds" ((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