module Data.Time.ZoneInfo (
ZoneInfo,
initZoneInfo,
utcZoneInfo,
newZoneInfo,
getZoneName,
getZoneMinutes,
posixToZonedTime,
utcToZonedTime',
localToPOSIXZoned,
localToUTCZoned,
convertTimeZone
) where
import Data.Fixed
import Data.Time.Calendar
import Data.Time.Clock
import Data.Time.Clock.POSIX
import Data.Time.LocalTime
import Foreign
import Foreign.C
data CZoneInfo
type ZoneInfo = ForeignPtr CZoneInfo
type CTm = ()
foreign import ccall unsafe "olson_init"
c_init :: CString -> IO CInt
foreign import ccall unsafe "olson_gmt"
c_gmt :: IO (Ptr CZoneInfo)
foreign import ccall unsafe "olson_create"
c_create :: CString -> IO (Ptr CZoneInfo)
foreign import ccall unsafe "&olson_destroy"
c_destroy :: FunPtr (Ptr CZoneInfo -> IO ())
foreign import ccall unsafe "olson_localtime"
c_localtime :: Ptr CZoneInfo -> Ptr CTime -> Ptr CTm -> IO (Ptr CTm)
foreign import ccall unsafe "olson_mktime"
c_mktime :: Ptr CZoneInfo -> Ptr CTm -> IO CTime
foreign import ccall unsafe "olson_gmtoff"
c_gmtoff :: Ptr CZoneInfo -> CInt -> IO CLong
foreign import ccall unsafe "olson_zone"
c_zone :: Ptr CZoneInfo -> CInt -> IO (Ptr CChar)
toPico :: Real a => a -> Pico -> Pico
toPico i =
(+) (realToFrac i)
posixToUTCTime :: POSIXTime -> UTCTime
posixToUTCTime =
posixSecondsToUTCTime
utcToPOSIXTime :: UTCTime -> POSIXTime
utcToPOSIXTime =
utcTimeToPOSIXSeconds
cToPOSIXTime :: CTime -> Pico -> POSIXTime
cToPOSIXTime ct =
realToFrac . toPico ct
dayToCTm :: Ptr CTm -> Day -> IO ()
dayToCTm tm day = do
((\hsc_ptr -> pokeByteOff hsc_ptr 20)) tm (fromIntegral year 1900 :: CInt)
((\hsc_ptr -> pokeByteOff hsc_ptr 16)) tm (fromIntegral mon 1 :: CInt)
((\hsc_ptr -> pokeByteOff hsc_ptr 12)) tm (fromIntegral mday :: CInt)
return ()
where
(year, mon, mday) = toGregorian day
ctmToDay :: Ptr CTm -> IO Day
ctmToDay tm = do
year <- ((\hsc_ptr -> peekByteOff hsc_ptr 20)) tm :: IO CInt
mon <- ((\hsc_ptr -> peekByteOff hsc_ptr 16)) tm :: IO CInt
mday <- ((\hsc_ptr -> peekByteOff hsc_ptr 12)) tm :: IO CInt
return $ fromGregorian (fromIntegral year + 1900)
(fromIntegral mon + 1) (fromIntegral mday)
timeOfDayToCTm :: Ptr CTm -> TimeOfDay -> IO ()
timeOfDayToCTm tm tod = do
((\hsc_ptr -> pokeByteOff hsc_ptr 8)) tm (fromIntegral hour :: CInt)
((\hsc_ptr -> pokeByteOff hsc_ptr 4)) tm (fromIntegral min' :: CInt)
((\hsc_ptr -> pokeByteOff hsc_ptr 0)) tm (truncate sec :: CInt)
return ()
where
hour = todHour tod
min' = todMin tod
sec = todSec tod
ctmToTimeOfDay :: Ptr CTm -> Pico -> IO TimeOfDay
ctmToTimeOfDay tm psec = do
hour <- ((\hsc_ptr -> peekByteOff hsc_ptr 8)) tm :: IO CInt
min' <- ((\hsc_ptr -> peekByteOff hsc_ptr 4)) tm :: IO CInt
sec <- ((\hsc_ptr -> peekByteOff hsc_ptr 0)) tm :: IO CInt
return $ TimeOfDay (fromIntegral hour) (fromIntegral min')
(toPico sec psec)
ctmToTimeZone :: Ptr CZoneInfo -> Ptr CTm -> IO TimeZone
ctmToTimeZone zi tm = do
isdst <- ((\hsc_ptr -> peekByteOff hsc_ptr 32)) tm :: IO CInt
gmtoff <- c_gmtoff zi isdst
zone <- c_zone zi isdst >>= peekCString
return $ TimeZone (fromIntegral $ div gmtoff 60) (isdst /= 0) zone
ctmToZonedTime :: Ptr CZoneInfo -> Ptr CTm -> Pico -> IO ZonedTime
ctmToZonedTime zi tm psec = do
day <- ctmToDay tm
tod <- ctmToTimeOfDay tm psec
zone <- ctmToTimeZone zi tm
return $ ZonedTime (LocalTime day tod) zone
ctimeToZonedTime :: Ptr CZoneInfo -> Ptr CTime -> Pico -> IO ZonedTime
ctimeToZonedTime zi ct psec = do
allocaBytes (44) $ \ tm -> do
c_localtime zi ct tm
ctmToZonedTime zi tm psec
newZoneInfoFinalizer :: Ptr CZoneInfo -> IO ZoneInfo
newZoneInfoFinalizer =
newForeignPtr c_destroy
initZoneInfo :: Maybe String -> IO ()
initZoneInfo Nothing = do
throwErrnoIf_ (< 0) "Data.Time.ZoneInfo.initZoneInfo" io
where
io = c_init nullPtr
initZoneInfo (Just s) = do
throwErrnoIf_ (< 0) "Data.Time.ZoneInfo.initZoneInfo" io
where
io = withCString s c_init
utcZoneInfo :: ZoneInfo
utcZoneInfo =
unsafePerformIO $ c_gmt >>= newZoneInfoFinalizer
newZoneInfo :: String -> IO ZoneInfo
newZoneInfo s = do
throwErrnoIfNull "Data.Time.ZoneInfo.newZoneInfo" io
io >>= newZoneInfoFinalizer
where
io = withCString s c_create
getZoneName :: ZoneInfo -> Bool -> IO (String)
getZoneName zi isdst = do
c_zone zi' isdst' >>= peekCString
where
zi' = unsafeForeignPtrToPtr zi
isdst' = if isdst then 1 else 0
getZoneMinutes :: ZoneInfo -> Bool -> IO Int
getZoneMinutes zi isdst = do
tz <- c_gmtoff zi' isdst'
return $ fromIntegral $ div tz 60
where
zi' = unsafeForeignPtrToPtr zi
isdst' = if isdst then 1 else 0
posixToZonedTime :: ZoneInfo -> POSIXTime -> IO ZonedTime
posixToZonedTime zi pt = do
with (fromInteger sec :: CTime) $ \ ct -> do
ctimeToZonedTime zi' ct (realToFrac psec)
where
zi' = unsafeForeignPtrToPtr zi
(sec, psec) = divMod' pt 1
utcToZonedTime' :: ZoneInfo -> UTCTime -> IO ZonedTime
utcToZonedTime' zi =
posixToZonedTime zi . utcToPOSIXTime
localToPOSIXZoned :: ZoneInfo -> LocalTime -> IO (POSIXTime, ZonedTime)
localToPOSIXZoned zi lt =
allocaBytes (44) $ \ tm -> do
dayToCTm tm day
timeOfDayToCTm tm tod
((\hsc_ptr -> pokeByteOff hsc_ptr 32)) tm (1 :: CInt)
ct <- c_mktime zi' tm
zt <- ctmToZonedTime zi' tm psec
return (cToPOSIXTime ct psec, zt)
where
zi' = unsafeForeignPtrToPtr zi
day = localDay lt
tod = localTimeOfDay lt
sec = todSec tod
psec = mod' sec 1
localToUTCZoned :: ZoneInfo -> LocalTime -> IO (UTCTime, ZonedTime)
localToUTCZoned zi lt = do
(pt, zi') <- localToPOSIXZoned zi lt
return (posixToUTCTime pt, zi')
convertTimeZone :: ZoneInfo -> LocalTime -> ZoneInfo -> IO ZonedTime
convertTimeZone src lt dst = do
(pt, _) <- localToPOSIXZoned src lt
posixToZonedTime dst pt