91a096a8f313d91132f0068ee693e31f1e4e2a4f
[packages/time.git] / lib / Data / Time / Clock / POSIX.hs
1 -- | POSIX time, if you need to deal with timestamps and the like.
2 -- Most people won't need this module.
3 module Data.Time.Clock.POSIX
4 (
5 posixDayLength,POSIXTime,
6 makePOSIXTime,ptSeconds,ptNanoSeconds,
7 posixSecondsToUTCTime,utcTimeToPOSIXSeconds,getPOSIXTime,getCurrentTime
8 ) where
9
10 import Data.Time.Clock.UTC
11 import Data.Time.Clock.Scale (picosecondsToDiffTime)
12 import Data.Time.Calendar.Days
13 import Data.Int (Int64)
14 import Data.Word
15 import Data.Fixed (divMod')
16 import Control.DeepSeq
17
18 #include "HsTimeConfig.h"
19
20 #ifdef mingw32_HOST_OS
21 import System.Win32.Time
22 #elif HAVE_CLOCK_GETTIME
23 import Data.Time.Clock.CTimespec
24 import Foreign.C.Types (CTime(..), CLong(..))
25 #else
26 import Data.Time.Clock.CTimeval
27 import Foreign.C.Types (CLong(..))
28 #endif
29
30 --------------------------------------------------------------------------------
31
32 -- | POSIX time is the nominal time since 1970-01-01 00:00 UTC
33 --
34 data POSIXTime = POSIXTime
35 { ptSeconds :: {-# UNPACK #-} !Int64
36 , ptNanoSeconds :: {-# UNPACK #-} !Word32
37 } deriving (Eq,Ord)
38
39 makePOSIXTime :: Int64 -> Word32 -> POSIXTime
40 makePOSIXTime xs xn
41 | xn < 0 || xn >= 1000000000 = POSIXTime (xs + fromIntegral q) r
42 | otherwise = POSIXTime xs xn
43 where (q, r) = xn `divMod` 1000000000
44
45 instance NFData POSIXTime where
46 rnf a = a `seq` ()
47
48 posixToUTCTime :: POSIXTime -> UTCTime
49 posixToUTCTime (POSIXTime s ns) =
50 let (d, s') = s `divMod` posixDayLength
51 ps = s' * 1000000000000 + fromIntegral (ns * 1000) -- 'Int64' can hold ps in one day
52 in UTCTime (addDays (fromIntegral d) unixEpochDay)
53 (picosecondsToDiffTime (fromIntegral ps))
54
55 posixDayLength :: Int64
56 posixDayLength = 86400
57
58 unixEpochDay :: Day
59 unixEpochDay = ModifiedJulianDay 40587
60
61 getPOSIXTime :: IO POSIXTime
62 #ifdef mingw32_HOST_OS
63 -- On Windows, the equlvalent of POSIX time is "file time", defined as
64 -- the number of 100-nanosecond intervals that have elapsed since
65 -- 12:00 A.M. January 1, 1601 (UTC). We can convert this into a POSIX
66 -- time by adjusting the offset to be relative to the POSIX epoch.
67
68 getPOSIXTime = do
69 FILETIME ft <- System.Win32.Time.getSystemTimeAsFileTime
70 let (s, us) = (ft - win32_epoch_adjust) `divMod` 10000000
71 return (POSIXTime (fromIntegral s) (fromIntegral us * 1000))
72 where
73 win32_epoch_adjust :: Word64
74 win32_epoch_adjust = 116444736000000000
75
76 #elif HAVE_CLOCK_GETTIME
77 -- Use hi-res clock_gettime
78
79 getPOSIXTime = do
80 MkCTimespec (CTime s) (CLong ns) <- getCTimespec
81 return (POSIXTime (fromIntegral s) (fromIntegral ns))
82
83 #else
84 -- Use gettimeofday
85 getPOSIXTime = do
86 MkCTimeval (CLong s) (CLong us) <- getCTimeval
87 return (POSIXTime (fromIntegral s) (fromIntegral us * 1000))
88
89 #endif
90
91 --------------------------------------------------------------------------------
92
93 posixDayLength_ :: NominalDiffTime
94 posixDayLength_ = 86400
95
96 posixSecondsToUTCTime :: NominalDiffTime -> UTCTime
97 posixSecondsToUTCTime i = let
98 (d,t) = divMod' i posixDayLength_
99 in UTCTime (addDays d unixEpochDay) (realToFrac t)
100
101 utcTimeToPOSIXSeconds :: UTCTime -> NominalDiffTime
102 utcTimeToPOSIXSeconds (UTCTime d t) =
103 (fromInteger (diffDays d unixEpochDay) * posixDayLength_) + min posixDayLength_ (realToFrac t)
104
105 -- | Get the current 'UTCTime' from the system clock.
106 getCurrentTime :: IO UTCTime
107 getCurrentTime = posixToUTCTime `fmap` getPOSIXTime