    -- |
    -- Module      : Data.Hourglass.Local
    -- License     : BSD-style
    -- Maintainer  : Vincent Hanquez <vincent@snarc.org>
    -- Stability   : experimental
    -- Portability : unknown
    --
    -- Local time = global time + timezone
    --
    {-# LANGUAGE FlexibleInstances #-}
    module Data.Hourglass.Local
        (
        -- * Local time
        -- ** Local time type
          LocalTime
        -- ** Local time creation and manipulation
        , localTime
        , localTimeUnwrap
        , localTimeToGlobal
        , localTimeFromGlobal
        , localTimeGetTimezone
        , localTimeSetTimezone
        , localTimeConvert
        ) where

    import Data.Hourglass.Types
    import Data.Hourglass.Time
    import Data.Hourglass.Diff

    -- | Local time representation
    --
    -- this is a time representation augmented by a timezone
    -- to get back to a global time, the timezoneOffset needed to be added to the local time.
    --
    data LocalTime t = LocalTime
        { forall t. LocalTime t -> t
    localTimeUnwrap      :: t              -- ^ unwrap the LocalTime value. the time value is local.
        , forall t. LocalTime t -> TimezoneOffset
    localTimeGetTimezone :: TimezoneOffset -- ^ get the timezone associated with LocalTime
        }

    -- FIXME add instance Read too.

    instance Show t => Show (LocalTime t) where
        show :: LocalTime t -> String
    show (LocalTime t
    t TimezoneOffset
    tz) = t -> String
    forall a. Show a => a -> String
    show t
    t String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ TimezoneOffset -> String
    forall a. Show a => a -> String
    show TimezoneOffset
    tz
    instance Eq t => Eq (LocalTime t) where
        LocalTime t
    t1 TimezoneOffset
    tz1 == :: LocalTime t -> LocalTime t -> Bool
    == LocalTime t
    t2 TimezoneOffset
    tz2 = TimezoneOffset
    tz1 TimezoneOffset -> TimezoneOffset -> Bool
    forall a. Eq a => a -> a -> Bool
    == TimezoneOffset
    tz2 Bool -> Bool -> Bool
    && t
    t1 t -> t -> Bool
    forall a. Eq a => a -> a -> Bool
    == t
    t2

    instance (Ord t, Time t) => Ord (LocalTime t) where
        compare :: LocalTime t -> LocalTime t -> Ordering
    compare l1 :: LocalTime t
    l1@(LocalTime t
    g1 TimezoneOffset
    tz1) l2 :: LocalTime t
    l2@(LocalTime t
    g2 TimezoneOffset
    tz2) =
            case TimezoneOffset -> TimezoneOffset -> Ordering
    forall a. Ord a => a -> a -> Ordering
    compare TimezoneOffset
    tz1 TimezoneOffset
    tz2 of
                Ordering
    EQ -> t -> t -> Ordering
    forall a. Ord a => a -> a -> Ordering
    compare t
    g1 t
    g2
                Ordering
    _  -> let t1 :: t
    t1 = LocalTime t -> t
    forall t. Time t => LocalTime t -> t
    localTimeToGlobal LocalTime t
    l1
                          t2 :: t
    t2 = LocalTime t -> t
    forall t. Time t => LocalTime t -> t
    localTimeToGlobal LocalTime t
    l2
                       in t -> t -> Ordering
    forall a. Ord a => a -> a -> Ordering
    compare t
    t1 t
    t2

    instance Functor LocalTime where
        fmap :: forall a b. (a -> b) -> LocalTime a -> LocalTime b
    fmap a -> b
    f (LocalTime a
    t TimezoneOffset
    tz) = b -> TimezoneOffset -> LocalTime b
    forall t. t -> TimezoneOffset -> LocalTime t
    LocalTime (a -> b
    f a
    t) TimezoneOffset
    tz

    -- | Create a local time type from a timezone and a time type.
    --
    -- The time value is assumed to be local to the timezone offset set,
    -- so no transformation is done.
    localTime :: Time t => TimezoneOffset -> t -> LocalTime t
    localTime :: forall t. Time t => TimezoneOffset -> t -> LocalTime t
    localTime TimezoneOffset
    tz t
    t = t -> TimezoneOffset -> LocalTime t
    forall t. t -> TimezoneOffset -> LocalTime t
    LocalTime t
    t TimezoneOffset
    tz

    -- | Get back a global time value
    localTimeToGlobal :: Time t => LocalTime t -> t
    localTimeToGlobal :: forall t. Time t => LocalTime t -> t
    localTimeToGlobal (LocalTime t
    local TimezoneOffset
    tz)
        | TimezoneOffset
    tz TimezoneOffset -> TimezoneOffset -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int -> TimezoneOffset
    TimezoneOffset Int
    0 = t
    local
        | Bool
    otherwise              = ElapsedP -> t
    forall t1 t2. (Timeable t1, Time t2) => t1 -> t2
    timeConvert (ElapsedP -> t) -> ElapsedP -> t
    forall a b. (a -> b) -> a -> b
    $ ElapsedP -> Seconds -> ElapsedP
    elapsedTimeAddSecondsP (t -> ElapsedP
    forall t. Timeable t => t -> ElapsedP
    timeGetElapsedP t
    local) Seconds
    tzSecs
      where tzSecs :: Seconds
    tzSecs = Seconds -> Seconds
    forall a. Num a => a -> a
    negate (Seconds -> Seconds) -> Seconds -> Seconds
    forall a b. (a -> b) -> a -> b
    $ TimezoneOffset -> Seconds
    timezoneOffsetToSeconds TimezoneOffset
    tz

    -- | create a local time value from a global one
    localTimeFromGlobal :: Time t => t -> LocalTime t
    localTimeFromGlobal :: forall t. Time t => t -> LocalTime t
    localTimeFromGlobal = TimezoneOffset -> t -> LocalTime t
    forall t. Time t => TimezoneOffset -> t -> LocalTime t
    localTime (Int -> TimezoneOffset
    TimezoneOffset Int
    0)

    -- | Change the timezone, and adjust the local value to represent the new local value.
    localTimeSetTimezone :: Time t => TimezoneOffset -> LocalTime t -> LocalTime t
    localTimeSetTimezone :: forall t. Time t => TimezoneOffset -> LocalTime t -> LocalTime t
    localTimeSetTimezone TimezoneOffset
    tz currentLocal :: LocalTime t
    currentLocal@(LocalTime t
    t TimezoneOffset
    currentTz)
        | Seconds
    diffTz Seconds -> Seconds -> Bool
    forall a. Eq a => a -> a -> Bool
    == Seconds
    0 = LocalTime t
    currentLocal
        | Bool
    otherwise   = t -> TimezoneOffset -> LocalTime t
    forall t. t -> TimezoneOffset -> LocalTime t
    LocalTime (ElapsedP -> t
    forall t1 t2. (Timeable t1, Time t2) => t1 -> t2
    timeConvert ElapsedP
    t') TimezoneOffset
    tz
      where t' :: ElapsedP
    t'        = ElapsedP -> Seconds -> ElapsedP
    elapsedTimeAddSecondsP (t -> ElapsedP
    forall t. Timeable t => t -> ElapsedP
    timeGetElapsedP t
    t) Seconds
    diffTz
            diffTz :: Seconds
    diffTz    = TimezoneOffset -> Seconds
    timezoneOffsetToSeconds TimezoneOffset
    tz Seconds -> Seconds -> Seconds
    forall a. Num a => a -> a -> a
    - TimezoneOffset -> Seconds
    timezoneOffsetToSeconds TimezoneOffset
    currentTz

    -- | convert the local time representation to another time representation determined by context.
    localTimeConvert :: (Time t1, Time t2) => LocalTime t1 -> LocalTime t2
    localTimeConvert :: forall t1 t2. (Time t1, Time t2) => LocalTime t1 -> LocalTime t2
    localTimeConvert = (t1 -> t2) -> LocalTime t1 -> LocalTime t2
    forall a b. (a -> b) -> LocalTime a -> LocalTime b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap t1 -> t2
    forall t1 t2. (Timeable t1, Time t2) => t1 -> t2
    timeConvert