{-# LANGUAGE CPP #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ViewPatterns #-}

#if HLINT
#include "cabal_macros.h"
#endif

-- | This module provides the 'Thyme' typeclass, and instances for
-- converting between "Data.Time" and "Data.Thyme" types. It also provides
-- compatibility wrappers for existing code using "Data.Time".
--
-- Note that we do not provide 'Num' hierarchy instances for 'DiffTime' nor
-- 'NominalDiffTime' here. If you want to use them anyway despite parts of
-- them being ill-defined (e.g. @('*')@ on 'DiffTime'), import
-- "Data.Thyme.Time" instead.

module Data.Thyme.Time.Core
    ( module Data.Thyme
    , module Data.Thyme.Time.Core
    ) where

import Prelude
import Control.Lens
import Data.AffineSpace
import Data.Fixed
import Data.Ratio
import Data.Thyme
import Data.Thyme.Clock.TAI
import qualified Data.Time.Calendar as T
import qualified Data.Time.Clock as T
import qualified Data.Time.Clock.TAI as T
import qualified Data.Time.LocalTime as T
import Data.Thyme.TrueName

------------------------------------------------------------------------
-- * Type conversion

-- | Typeclass for converting between "Data.Time" and "Data.Thyme" types.
class Thyme time thyme | thyme -> time where
    -- | Convert between "Data.Time" and "Data.Thyme" types.
    --
    -- @
    -- > :set -t
    -- > import qualified "Data.Time"
    --
    -- > 'thyme' 'Control.Lens.#' ('fromSeconds'' 10 :: 'DiffTime')
    -- 10s
    -- it :: 'Data.Time.DiffTime'
    --
    -- > 'Data.Time.secondsToDiffTime' 10 '^.' 'thyme' :: 'DiffTime'
    -- 10s
    -- it :: 'DiffTime'
    -- @
    thyme :: Iso' time thyme

instance Thyme T.Day Day where
    {-# INLINE thyme #-}
    thyme :: Iso' Day Day
thyme = forall s a b t. (s -> a) -> (b -> t) -> Iso s t a b
iso
        (Int -> Day
ModifiedJulianDay forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Num a => Integer -> a
fromInteger forall b c a. (b -> c) -> (a -> b) -> a -> c
. Day -> Integer
T.toModifiedJulianDay)
        (Integer -> Day
T.ModifiedJulianDay forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Integral a => a -> Integer
toInteger forall b c a. (b -> c) -> (a -> b) -> a -> c
. Day -> Int
toModifiedJulianDay)

instance Thyme T.UniversalTime UniversalTime where
    {-# INLINE thyme #-}
    thyme :: Iso' UniversalTime UniversalTime
thyme = forall s a b t. (s -> a) -> (b -> t) -> Iso s t a b
iso UniversalTime -> Rational
T.getModJulianDate Rational -> UniversalTime
T.ModJulianDate forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall s t a b. AnIso s t a b -> Iso b a t s
from Iso' UniversalTime Rational
modJulianDate

instance Thyme T.DiffTime DiffTime where
    {-# INLINE thyme #-}
    thyme :: Iso' DiffTime DiffTime
thyme = Overloaded p f DiffTime DiffTime Pico Pico
dt forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall {a} {a}. Overloaded p f (Fixed a) (Fixed a) Integer Integer
fixed forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall s t a b. AnIso s t a b -> Iso b a t s
from forall t. TimeDiff t => Iso' t Integer
picoseconds where
        dt :: Overloaded p f DiffTime DiffTime Pico Pico
dt = forall s a b t. (s -> a) -> (b -> t) -> Iso s t a b
iso (\ DiffTime
[truename| ''T.DiffTime MkDiffTime | ps |] -> Pico
ps )
            [truename| ''T.DiffTime MkDiffTime |]
#if MIN_VERSION_base(4,7,0)
        fixed :: Overloaded p f (Fixed a) (Fixed a) Integer Integer
fixed = forall s a b t. (s -> a) -> (b -> t) -> Iso s t a b
iso (\ (MkFixed Integer
n) -> Integer
n ) forall k (a :: k). Integer -> Fixed a
MkFixed
#else
        fixed = iso (\ [truename| ''Fixed MkFixed | n |] -> n )
            [truename| ''Fixed MkFixed |]
#endif

instance Thyme T.NominalDiffTime NominalDiffTime where
    {-# INLINE thyme #-}
    thyme :: Iso' NominalDiffTime NominalDiffTime
thyme = Overloaded p f NominalDiffTime NominalDiffTime Pico Pico
ndt forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall {a} {a}. Overloaded p f (Fixed a) (Fixed a) Integer Integer
fixed forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall s t a b. AnIso s t a b -> Iso b a t s
from forall t. TimeDiff t => Iso' t Integer
picoseconds where
        ndt :: Overloaded p f NominalDiffTime NominalDiffTime Pico Pico
ndt = forall s a b t. (s -> a) -> (b -> t) -> Iso s t a b
iso (\ NominalDiffTime
[truename| ''T.NominalDiffTime MkNominalDiffTime | ps |] -> Pico
ps )
            [truename| ''T.NominalDiffTime MkNominalDiffTime |]
#if MIN_VERSION_base(4,7,0)
        fixed :: Overloaded p f (Fixed a) (Fixed a) Integer Integer
fixed = forall s a b t. (s -> a) -> (b -> t) -> Iso s t a b
iso (\ (MkFixed Integer
n) -> Integer
n ) forall k (a :: k). Integer -> Fixed a
MkFixed
#else
        fixed = iso (\ [truename| ''Fixed MkFixed | n |] -> n )
            [truename| ''Fixed MkFixed |]
#endif

instance Thyme T.UTCTime UTCView where
    {-# INLINE thyme #-}
    thyme :: Iso' UTCTime UTCView
thyme = forall s a b t. (s -> a) -> (b -> t) -> Iso s t a b
iso
        (\ (T.UTCTime Day
d DiffTime
t) -> Day -> DiffTime -> UTCView
UTCView (Day
d forall s a. s -> Getting a s a -> a
^. forall time thyme. Thyme time thyme => Iso' time thyme
thyme) (DiffTime
t forall s a. s -> Getting a s a -> a
^. forall time thyme. Thyme time thyme => Iso' time thyme
thyme))
        (\ (UTCView Day
d DiffTime
t) -> Day -> DiffTime -> UTCTime
T.UTCTime (forall time thyme. Thyme time thyme => Iso' time thyme
thyme forall s t a b. AReview s t a b -> b -> t
# Day
d) (forall time thyme. Thyme time thyme => Iso' time thyme
thyme forall s t a b. AReview s t a b -> b -> t
# DiffTime
t))

instance Thyme T.UTCTime UTCTime where
    {-# INLINE thyme #-}
    thyme :: Iso' UTCTime UTCTime
thyme = forall time thyme. Thyme time thyme => Iso' time thyme
thyme forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall s t a b. AnIso s t a b -> Iso b a t s
from Iso' UTCTime UTCView
utcTime

instance Thyme T.AbsoluteTime AbsoluteTime where
    {-# INLINE thyme #-}
    thyme :: Iso' AbsoluteTime AbsoluteTime
thyme = forall s a b t. (s -> a) -> (b -> t) -> Iso s t a b
iso (AbsoluteTime -> AbsoluteTime -> DiffTime
`T.diffAbsoluteTime` AbsoluteTime
T.taiEpoch)
            (DiffTime -> AbsoluteTime -> AbsoluteTime
`T.addAbsoluteTime` AbsoluteTime
T.taiEpoch)
        forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall time thyme. Thyme time thyme => Iso' time thyme
thyme forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall s a b t. (s -> a) -> (b -> t) -> Iso s t a b
iso (AbsoluteTime
taiEpoch forall p. AffineSpace p => p -> Diff p -> p
.+^) (forall p. AffineSpace p => p -> p -> Diff p
.-. AbsoluteTime
taiEpoch)

instance Thyme T.TimeZone TimeZone where
    {-# INLINE thyme #-}
    thyme :: Iso' TimeZone TimeZone
thyme = forall s a b t. (s -> a) -> (b -> t) -> Iso s t a b
iso (\ T.TimeZone {Bool
Int
String
timeZoneMinutes :: TimeZone -> Int
timeZoneName :: TimeZone -> String
timeZoneSummerOnly :: TimeZone -> Bool
timeZoneName :: String
timeZoneSummerOnly :: Bool
timeZoneMinutes :: Int
..} -> TimeZone {Bool
Int
String
timeZoneName :: String
timeZoneSummerOnly :: Bool
timeZoneMinutes :: Int
timeZoneName :: String
timeZoneSummerOnly :: Bool
timeZoneMinutes :: Int
..})
        (\ TimeZone {Bool
Int
String
timeZoneName :: String
timeZoneSummerOnly :: Bool
timeZoneMinutes :: Int
timeZoneName :: TimeZone -> String
timeZoneSummerOnly :: TimeZone -> Bool
timeZoneMinutes :: TimeZone -> Int
..} -> T.TimeZone {Bool
Int
String
timeZoneName :: String
timeZoneSummerOnly :: Bool
timeZoneMinutes :: Int
timeZoneMinutes :: Int
timeZoneName :: String
timeZoneSummerOnly :: Bool
..})

instance Thyme T.TimeOfDay TimeOfDay where
    {-# INLINE thyme #-}
    thyme :: Iso' TimeOfDay TimeOfDay
thyme = forall s a b t. (s -> a) -> (b -> t) -> Iso s t a b
iso ( \ (T.TimeOfDay Int
h Int
m Pico
s) -> Int -> Int -> DiffTime -> TimeOfDay
TimeOfDay Int
h Int
m forall a b. (a -> b) -> a -> b
$
            forall t. TimeDiff t => Iso' t Int64
microseconds forall s t a b. AReview s t a b -> b -> t
# forall a b. (RealFrac a, Integral b) => a -> b
round (Pico
s forall a. Num a => a -> a -> a
* Pico
1000000) )
        ( \ (TimeOfDay Int
h Int
m DiffTime
s) -> Int -> Int -> Pico -> TimeOfDay
T.TimeOfDay Int
h Int
m forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Fractional a => Rational -> a
fromRational forall a b. (a -> b) -> a -> b
$
            forall a. Integral a => a -> Integer
toInteger (DiffTime
s forall s a. s -> Getting a s a -> a
^. forall t. TimeDiff t => Iso' t Int64
microseconds) forall a. Integral a => a -> a -> Ratio a
% Integer
1000000 )

instance Thyme T.LocalTime LocalTime where
    {-# INLINE thyme #-}
    thyme :: Iso' LocalTime LocalTime
thyme = forall s a b t. (s -> a) -> (b -> t) -> Iso s t a b
iso
        (\ (T.LocalTime Day
d TimeOfDay
t) -> Day -> TimeOfDay -> LocalTime
LocalTime (Day
d forall s a. s -> Getting a s a -> a
^. forall time thyme. Thyme time thyme => Iso' time thyme
thyme) (TimeOfDay
t forall s a. s -> Getting a s a -> a
^. forall time thyme. Thyme time thyme => Iso' time thyme
thyme))
        (\ (LocalTime Day
d TimeOfDay
t) -> Day -> TimeOfDay -> LocalTime
T.LocalTime (forall time thyme. Thyme time thyme => Iso' time thyme
thyme forall s t a b. AReview s t a b -> b -> t
# Day
d) (forall time thyme. Thyme time thyme => Iso' time thyme
thyme forall s t a b. AReview s t a b -> b -> t
# TimeOfDay
t))

instance Thyme T.ZonedTime ZonedTime where
    {-# INLINE thyme #-}
    thyme :: Iso' ZonedTime ZonedTime
thyme = forall s a b t. (s -> a) -> (b -> t) -> Iso s t a b
iso
        (\ (T.ZonedTime LocalTime
t TimeZone
z) -> LocalTime -> TimeZone -> ZonedTime
ZonedTime (LocalTime
t forall s a. s -> Getting a s a -> a
^. forall time thyme. Thyme time thyme => Iso' time thyme
thyme) (TimeZone
z forall s a. s -> Getting a s a -> a
^. forall time thyme. Thyme time thyme => Iso' time thyme
thyme))
        (\ (ZonedTime LocalTime
t TimeZone
z) -> LocalTime -> TimeZone -> ZonedTime
T.ZonedTime (forall time thyme. Thyme time thyme => Iso' time thyme
thyme forall s t a b. AReview s t a b -> b -> t
# LocalTime
t) (forall time thyme. Thyme time thyme => Iso' time thyme
thyme forall s t a b. AReview s t a b -> b -> t
# TimeZone
z))

-- | Convert a "Data.Time" type to a "Data.Thyme" type, if you would rather
-- not use "Control.Lens" directly.
--
-- @
-- 'toThyme' = 'view' 'thyme'
-- 'toThyme' t ≡ t '^.' 'thyme'
-- @
{-# INLINE toThyme #-}
toThyme :: (Thyme time thyme) => time -> thyme
toThyme :: forall time thyme. Thyme time thyme => time -> thyme
toThyme = forall a s. Getting a s a -> s -> a
view forall time thyme. Thyme time thyme => Iso' time thyme
thyme

-- | Convert a "Data.Thyme" type to a "Data.Time" type, if you would rather
-- not use "Control.Lens" directly.
--
-- @
-- 'fromThyme' = 'review' 'thyme'
-- 'fromThyme' t ≡ 'thyme' 'Control.Lens.#' t
-- @
{-# INLINE fromThyme #-}
fromThyme :: (Thyme time thyme) => thyme -> time
fromThyme :: forall time thyme. Thyme time thyme => thyme -> time
fromThyme = forall s t a b. AReview s t a b -> b -> t
review forall time thyme. Thyme time thyme => Iso' time thyme
thyme