module PostgreSQL.Binary.Interval where

import PostgreSQL.Binary.Prelude hiding (months)
import qualified PostgreSQL.Binary.Time as Time

data Interval = Interval
  { Interval -> Int64
micros :: Int64,
    Interval -> Int32
days :: Int32,
    Interval -> Int32
months :: Int32
  }
  deriving (Int -> Interval -> ShowS
[Interval] -> ShowS
Interval -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Interval] -> ShowS
$cshowList :: [Interval] -> ShowS
show :: Interval -> String
$cshow :: Interval -> String
showsPrec :: Int -> Interval -> ShowS
$cshowsPrec :: Int -> Interval -> ShowS
Show, Interval -> Interval -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Interval -> Interval -> Bool
$c/= :: Interval -> Interval -> Bool
== :: Interval -> Interval -> Bool
$c== :: Interval -> Interval -> Bool
Eq)

-- |
-- Oddly enough despite a claim of support of up to 178000000 years in
-- <http://www.postgresql.org/docs/9.3/static/datatype-datetime.html Postgres' docs>
-- in practice it starts behaving unpredictably after a smaller limit.
DiffTime
maxDiffTime :: DiffTime = DiffTime
1780000 forall a. Num a => a -> a -> a
* Int64 -> DiffTime
Time.microsToDiffTime Int64
Time.yearMicros

DiffTime
minDiffTime :: DiffTime = forall a. Num a => a -> a
negate DiffTime
maxDiffTime

fromDiffTime :: DiffTime -> Maybe Interval
fromDiffTime :: DiffTime -> Maybe Interval
fromDiffTime DiffTime
x =
  if DiffTime
x forall a. Ord a => a -> a -> Bool
> DiffTime
maxDiffTime Bool -> Bool -> Bool
|| DiffTime
x forall a. Ord a => a -> a -> Bool
< DiffTime
minDiffTime
    then forall a. Maybe a
Nothing
    else forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ Integer -> Interval
fromPicosUnsafe (forall a b. a -> b
unsafeCoerce DiffTime
x)

fromPicosUnsafe :: Integer -> Interval
fromPicosUnsafe :: Integer -> Interval
fromPicosUnsafe =
  forall s a. State s a -> s -> a
evalState forall a b. (a -> b) -> a -> b
$ do
    forall (m :: * -> *) s. Monad m => (s -> s) -> StateT s m ()
modify forall a b. (a -> b) -> a -> b
$ forall a b c. (a -> b -> c) -> b -> a -> c
flip forall a. Integral a => a -> a -> a
div (Integer
10 forall a b. (Num a, Integral b) => a -> b -> a
^ Integer
6)
    Integer
u <- forall (m :: * -> *) s a. Monad m => (s -> (a, s)) -> StateT s m a
state forall a b. (a -> b) -> a -> b
$ forall a b. (a, b) -> (b, a)
swap forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. forall a b c. (a -> b -> c) -> b -> a -> c
flip forall a. Integral a => a -> a -> (a, a)
divMod (Integer
10 forall a b. (Num a, Integral b) => a -> b -> a
^ Integer
6 forall a. Num a => a -> a -> a
* Integer
60 forall a. Num a => a -> a -> a
* Integer
60 forall a. Num a => a -> a -> a
* Integer
24)
    Integer
d <- forall (m :: * -> *) s a. Monad m => (s -> (a, s)) -> StateT s m a
state forall a b. (a -> b) -> a -> b
$ forall a b. (a, b) -> (b, a)
swap forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. forall a b c. (a -> b -> c) -> b -> a -> c
flip forall a. Integral a => a -> a -> (a, a)
divMod (Integer
31)
    Integer
m <- forall (m :: * -> *) s. Monad m => StateT s m s
get
    forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Int64 -> Int32 -> Int32 -> Interval
Interval (forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
u) (forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
d) (forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
m)

toDiffTime :: Interval -> DiffTime
toDiffTime :: Interval -> DiffTime
toDiffTime Interval
x =
  Integer -> DiffTime
picosecondsToDiffTime forall a b. (a -> b) -> a -> b
$
    (Integer
10 forall a b. (Num a, Integral b) => a -> b -> a
^ Integer
6)
      forall a. Num a => a -> a -> a
* ( forall a b. (Integral a, Num b) => a -> b
fromIntegral (Interval -> Int64
micros Interval
x)
            forall a. Num a => a -> a -> a
+ Integer
10 forall a b. (Num a, Integral b) => a -> b -> a
^ Integer
6 forall a. Num a => a -> a -> a
* Integer
60 forall a. Num a => a -> a -> a
* Integer
60 forall a. Num a => a -> a -> a
* Integer
24 forall a. Num a => a -> a -> a
* (forall a b. (Integral a, Num b) => a -> b
fromIntegral (Interval -> Int32
days Interval
x forall a. Num a => a -> a -> a
+ Int32
31 forall a. Num a => a -> a -> a
* Interval -> Int32
months Interval
x))
        )