packages feed

cabal-install-bundle-1.18.0.2: Distribution/Client/Compat/Time.hs

{-# LANGUAGE CPP, ForeignFunctionInterface #-}
module Distribution.Client.Compat.Time
       (EpochTime, getModTime, getFileAge, getCurTime)
       where

import Data.Int (Int64)
import System.Directory (getModificationTime)

#if MIN_VERSION_directory(1,2,0)
import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds, posixDayLength)
import Data.Time (getCurrentTime, diffUTCTime)
#else
import System.Time (ClockTime(..), getClockTime
                   ,diffClockTimes, normalizeTimeDiff, tdDay)
#endif

#if defined mingw32_HOST_OS

import Data.Int         (Int32)
import Data.Word        (Word32)
import Foreign          (Ptr, allocaBytes, peekByteOff)
import Foreign.C.Types  (CChar)
import Foreign.C.String (withCString)
import System.IO.Error  (mkIOError, doesNotExistErrorType)

type WIN32_FILE_ATTRIBUTE_DATA = Ptr ()
type LPCSTR = Ptr CChar

foreign import stdcall "Windows.h GetFileAttributesExA"
  c_getFileAttributesEx :: LPCSTR -> Int32
                           -> WIN32_FILE_ATTRIBUTE_DATA -> IO Bool

size_WIN32_FILE_ATTRIBUTE_DATA :: Int
size_WIN32_FILE_ATTRIBUTE_DATA = 36

index_WIN32_FILE_ATTRIBUTE_DATA_ftLastWriteTime_dwLowDateTime :: Int
index_WIN32_FILE_ATTRIBUTE_DATA_ftLastWriteTime_dwLowDateTime = 20

#else

#if MIN_VERSION_base(4,5,0)
import Foreign.C.Types    (CTime(..))
#else
import Foreign.C.Types    (CTime)
#endif
import System.Posix.Files (getFileStatus, modificationTime)

#endif

-- | The number of seconds since the UNIX epoch.
type EpochTime = Int64

-- | Return modification time of given file. Works around the low clock
-- resolution problem that 'getModificationTime' has on GHC < 7.8.
--
-- This is a modified version of the code originally written for OpenShake by
-- Neil Mitchell. See module Development.Shake.FileTime.
getModTime :: FilePath -> IO EpochTime

#if defined mingw32_HOST_OS

-- Directly against the Win32 API.
getModTime path = withCString path $ \file ->
  allocaBytes size_WIN32_FILE_ATTRIBUTE_DATA $ \info -> do
    res <- c_getFileAttributesEx file 0 info
    if not res
      then do
        let err = mkIOError doesNotExistErrorType
                  "Distribution.Client.Compat.Time.getModTime"
                  Nothing (Just path)
        ioError err
      else do
        dword <- peekByteOff info
                 index_WIN32_FILE_ATTRIBUTE_DATA_ftLastWriteTime_dwLowDateTime
        -- TODO: Convert Windows seconds to POSIX seconds. ATM we don't care
        -- since we only use the value for comparisons.
        return $! fromIntegral (dword :: Word32)
#else

-- Directly against the unix library.
getModTime path = do
    -- CTime is Int32 in base 4.5, Int64 in base >= 4.6, and an abstract type in
    -- base < 4.5.
    t <- fmap modificationTime $ getFileStatus path
#if MIN_VERSION_base(4,5,0)
    let CTime i = t
    return (fromIntegral i)
#else
    return (read . show $ t)
#endif
#endif

-- | Return age of given file in days.
getFileAge :: FilePath -> IO Int
getFileAge file = do
  t0 <- getModificationTime file
#if MIN_VERSION_directory(1,2,0)
  t1 <- getCurrentTime
  let days = truncate $ (t1 `diffUTCTime` t0) / posixDayLength
#else
  t1 <- getClockTime
  let days = (tdDay . normalizeTimeDiff) (t1 `diffClockTimes` t0)
#endif
  return days

getCurTime :: IO EpochTime
getCurTime =  do
#if MIN_VERSION_directory(1,2,0)
  (truncate . utcTimeToPOSIXSeconds) `fmap` getCurrentTime
#else
  (TOD s _) <- getClockTime
  return $! fromIntegral s
#endif