    {-# 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 (..))

    -------------------------------------------------------------------------------
    -- 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 :: DiffTime -> TimeOfDay
    pastMidnight = DiffTime -> TimeOfDay
    timeToTimeOfDay

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

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

    #if MIN_VERSION_time(1,9,0) && !MIN_VERSION_base(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 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