    {-# LANGUAGE CPP, DeriveDataTypeable #-}
    module Data.Time.LocalTime.Compat (
        -- * Time zones
        TimeZone(..),timeZoneOffsetString,timeZoneOffsetString',minutesToTimeZone,hoursToTimeZone,utc,

        -- getting the locale time zone
        getTimeZone,getCurrentTimeZone,

        -- * Time of day
        TimeOfDay(..),midnight,midday,makeTimeOfDayValid,
        timeToDaysAndTimeOfDay,daysAndTimeOfDayToTime,
        utcToLocalTimeOfDay,localToUTCTimeOfDay,
        timeToTimeOfDay,timeOfDayToTime,
        dayFractionToTimeOfDay,timeOfDayToDayFraction,
        pastMidnight, sinceMidnight,

        -- * CalendarDiffTime
        CalendarDiffTime (..),
        calendarTimeDays, calendarTimeTime, scaleCalendarDiffTime,

        -- * Local Time
        LocalTime(..),

        addLocalTime,diffLocalTime,

        -- converting UTC and UT1 times to LocalTime
        utcToLocalTime,localTimeToUTC,ut1ToLocalTime,localTimeToUT1,

        -- * Zoned Time
        ZonedTime(..),utcToZonedTime,zonedTimeToUTC,getZonedTime,utcToLocalZonedTime,
        ) where

    import Data.Time.Orphans ()

    import Data.Time.LocalTime
    import Data.Time.Clock.Compat
    import Data.Time.Calendar.Compat

    import Data.Fixed (Pico (..), showFixed, divMod')
    import Data.Monoid (Monoid (..))
    import Data.Data (Data, Typeable)
    import Data.Semigroup (Semigroup (..))

    import Control.DeepSeq (NFData (..))

    -------------------------------------------------------------------------------
    -- TimeOfDay
    -------------------------------------------------------------------------------

    #if !MIN_VERSION_time(1,9,0)

    -- | Convert a period of time into a count of days and a time of day since midnight.
    -- The time of day will never have a leap second.
    timeToDaysAndTimeOfDay :: NominalDiffTime -> (Integer,TimeOfDay)
    timeToDaysAndTimeOfDay dt = let
        s = realToFrac dt
        (m,ms) = divMod' s 60
        (h,hm) = divMod' m 60
        (d,dh) = divMod' h 24
        in (d,TimeOfDay dh hm ms)

    -- | Convert a count of days and a time of day since midnight into a period of time.
    daysAndTimeOfDayToTime :: Integer -> TimeOfDay -> NominalDiffTime
    daysAndTimeOfDayToTime d (TimeOfDay dh hm ms) = (+) (realToFrac ms) $ (*) 60 $ (+) (realToFrac hm) $ (*) 60 $ (+) (realToFrac dh) $ (*) 24 $ realToFrac d

    #endif

    #if !MIN_VERSION_time(1,10,0)
    -- | Same as 'timeToTimeOfDay'.
    pastMidnight :: DiffTime -> TimeOfDay
    pastMidnight = timeToTimeOfDay

    -- | Same as 'timeOfDayToTime'.
    sinceMidnight :: TimeOfDay -> DiffTime
    sinceMidnight = timeOfDayToTime
    #endif

    -------------------------------------------------------------------------------
    -- CalendarDiffTime
    -------------------------------------------------------------------------------

    #if MIN_VERSION_time(1,9,0) && !MIN_VERSION_time(1,9,2)
    deriving instance Typeable CalendarDiffTime
    deriving instance Data CalendarDiffTime
    #endif

    #if !MIN_VERSION_time(1,9,2)

    data CalendarDiffTime = CalendarDiffTime
        { ctMonths :: Integer
        , ctTime :: NominalDiffTime
        } deriving (Eq,
        Data
        ,Typeable
        )

    -- | Additive
    instance Semigroup CalendarDiffTime where
        CalendarDiffTime m1 d1 <> CalendarDiffTime m2 d2 = CalendarDiffTime (m1 + m2) (d1 + d2)

    instance Monoid CalendarDiffTime where
        mempty = CalendarDiffTime 0 0
        mappend = (<>)

    instance NFData CalendarDiffTime where
        rnf (CalendarDiffTime x y) = rnf x `seq` rnf y

    instance Show CalendarDiffTime where
        show (CalendarDiffTime m t) = "P" ++ show m ++ "MT" ++ showFixed True (realToFrac t :: Pico) ++ "S"

    calendarTimeDays :: CalendarDiffDays -> CalendarDiffTime
    calendarTimeDays (CalendarDiffDays m d) = CalendarDiffTime m $ fromInteger d * nominalDay

    calendarTimeTime :: NominalDiffTime -> CalendarDiffTime
    calendarTimeTime dt = CalendarDiffTime 0 dt

    -- | Scale by a factor. Note that @scaleCalendarDiffTime (-1)@ will not perfectly invert a duration, due to variable month lengths.
    scaleCalendarDiffTime :: Integer -> CalendarDiffTime -> CalendarDiffTime
    scaleCalendarDiffTime k (CalendarDiffTime m d) = CalendarDiffTime (k * m) (fromInteger k * d)
    #endif

    -------------------------------------------------------------------------------
    -- LocalTime
    -------------------------------------------------------------------------------

    #if !MIN_VERSION_time(1,9,0)

    -- | addLocalTime a b = a + b
    addLocalTime :: NominalDiffTime -> LocalTime -> LocalTime
    addLocalTime x = utcToLocalTime utc . addUTCTime x . localTimeToUTC utc

    -- | diffLocalTime a b = a - b
    diffLocalTime :: LocalTime -> LocalTime -> NominalDiffTime
    diffLocalTime a b = diffUTCTime (localTimeToUTC utc a) (localTimeToUTC utc b)

    #endif