    {-# LANGUAGE Safe #-}

    -- | Fast access to the system clock.
    module Data.Time.Clock.System (
        systemEpochDay,
        SystemTime (..),
        truncateSystemTimeLeapSecond,
        getSystemTime,
        systemToUTCTime,
        utcToSystemTime,
        systemToTAITime,
    ) where

    import Data.Int (Int64)
    import Data.Time.Calendar.Days
    import Data.Time.Clock.Internal.AbsoluteTime
    import Data.Time.Clock.Internal.DiffTime
    import Data.Time.Clock.Internal.SystemTime
    import Data.Time.Clock.Internal.UTCTime

    -- | Map leap-second values to the start of the following second.
    -- The resulting 'systemNanoseconds' will always be in the range 0 to 1E9-1.
    truncateSystemTimeLeapSecond :: SystemTime -> SystemTime
    truncateSystemTimeLeapSecond :: SystemTime -> SystemTime
    truncateSystemTimeLeapSecond (MkSystemTime Int64
    seconds Word32
    nanoseconds)
        | Word32
    nanoseconds Word32 -> Word32 -> Bool
    forall a. Ord a => a -> a -> Bool
    >= Word32
    1000000000 = Int64 -> Word32 -> SystemTime
    MkSystemTime (Int64 -> Int64
    forall a. Enum a => a -> a
    succ Int64
    seconds) Word32
    0
    truncateSystemTimeLeapSecond SystemTime
    t = SystemTime
    t

    -- | Convert 'SystemTime' to 'UTCTime', matching zero 'SystemTime' to midnight of 'systemEpochDay' UTC.
    systemToUTCTime :: SystemTime -> UTCTime
    systemToUTCTime :: SystemTime -> UTCTime
    systemToUTCTime (MkSystemTime Int64
    seconds Word32
    nanoseconds) = let
        days :: Int64
        timeSeconds :: Int64
        (Int64
    days, Int64
    timeSeconds) = Int64
    seconds Int64 -> Int64 -> (Int64, Int64)
    forall a. Integral a => a -> a -> (a, a)
    `divMod` Int64
    86400
        day :: Day
        day :: Day
    day = Integer -> Day -> Day
    addDays (Int64 -> Integer
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int64
    days) Day
    systemEpochDay
        timeNanoseconds :: Int64
        timeNanoseconds :: Int64
    timeNanoseconds = Int64
    timeSeconds Int64 -> Int64 -> Int64
    forall a. Num a => a -> a -> a
    * Int64
    1000000000 Int64 -> Int64 -> Int64
    forall a. Num a => a -> a -> a
    + (Word32 -> Int64
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Word32
    nanoseconds)
        timePicoseconds :: Int64
        timePicoseconds :: Int64
    timePicoseconds = Int64
    timeNanoseconds Int64 -> Int64 -> Int64
    forall a. Num a => a -> a -> a
    * Int64
    1000
        time :: DiffTime
        time :: DiffTime
    time = Integer -> DiffTime
    picosecondsToDiffTime (Integer -> DiffTime) -> Integer -> DiffTime
    forall a b. (a -> b) -> a -> b
    $ Int64 -> Integer
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int64
    timePicoseconds
        in Day -> DiffTime -> UTCTime
    UTCTime Day
    day DiffTime
    time

    -- | Convert 'UTCTime' to 'SystemTime', matching zero 'SystemTime' to midnight of 'systemEpochDay' UTC.
    utcToSystemTime :: UTCTime -> SystemTime
    utcToSystemTime :: UTCTime -> SystemTime
    utcToSystemTime (UTCTime Day
    day DiffTime
    time) = let
        days :: Int64
        days :: Int64
    days = Integer -> Int64
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Integer -> Int64) -> Integer -> Int64
    forall a b. (a -> b) -> a -> b
    $ Day -> Day -> Integer
    diffDays Day
    day Day
    systemEpochDay
        timePicoseconds :: Int64
        timePicoseconds :: Int64
    timePicoseconds = Integer -> Int64
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Integer -> Int64) -> Integer -> Int64
    forall a b. (a -> b) -> a -> b
    $ DiffTime -> Integer
    diffTimeToPicoseconds DiffTime
    time
        timeNanoseconds :: Int64
        timeNanoseconds :: Int64
    timeNanoseconds = Int64
    timePicoseconds Int64 -> Int64 -> Int64
    forall a. Integral a => a -> a -> a
    `div` Int64
    1000
        timeSeconds :: Int64
        nanoseconds :: Int64
        (Int64
    timeSeconds, Int64
    nanoseconds) =
            if Int64
    timeNanoseconds Int64 -> Int64 -> Bool
    forall a. Ord a => a -> a -> Bool
    >= Int64
    86400000000000
                then (Int64
    86399, Int64
    timeNanoseconds Int64 -> Int64 -> Int64
    forall a. Num a => a -> a -> a
    - Int64
    86399000000000)
                else Int64
    timeNanoseconds Int64 -> Int64 -> (Int64, Int64)
    forall a. Integral a => a -> a -> (a, a)
    `divMod` Int64
    1000000000
        seconds :: Int64
        seconds :: Int64
    seconds = Int64
    days Int64 -> Int64 -> Int64
    forall a. Num a => a -> a -> a
    * Int64
    86400 Int64 -> Int64 -> Int64
    forall a. Num a => a -> a -> a
    + Int64
    timeSeconds
        in Int64 -> Word32 -> SystemTime
    MkSystemTime Int64
    seconds (Word32 -> SystemTime) -> Word32 -> SystemTime
    forall a b. (a -> b) -> a -> b
    $ Int64 -> Word32
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int64
    nanoseconds

    systemEpochAbsolute :: AbsoluteTime
    systemEpochAbsolute :: AbsoluteTime
    systemEpochAbsolute = Day -> AbsoluteTime
    taiNominalDayStart Day
    systemEpochDay

    -- | Convert 'SystemTime' to 'AbsoluteTime', matching zero 'SystemTime' to midnight of 'systemEpochDay' TAI.
    systemToTAITime :: SystemTime -> AbsoluteTime
    systemToTAITime :: SystemTime -> AbsoluteTime
    systemToTAITime (MkSystemTime Int64
    s Word32
    ns) = let
        diff :: DiffTime
        diff :: DiffTime
    diff = (Int64 -> DiffTime
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int64
    s) DiffTime -> DiffTime -> DiffTime
    forall a. Num a => a -> a -> a
    + (Word32 -> DiffTime
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Word32
    ns) DiffTime -> DiffTime -> DiffTime
    forall a. Num a => a -> a -> a
    * DiffTime
    1E-9
        in DiffTime -> AbsoluteTime -> AbsoluteTime
    addAbsoluteTime DiffTime
    diff AbsoluteTime
    systemEpochAbsolute

    -- | The day of the epoch of 'SystemTime', 1970-01-01
    systemEpochDay :: Day
    systemEpochDay :: Day
    systemEpochDay = Integer -> Day
    ModifiedJulianDay Integer
    40587