Documentation about Read/Show instances of TimeZone (#28)
[packages/time.git] / lib / Data / Time / LocalTime / Internal / TimeZone.hs
1 {-# OPTIONS -fno-warn-unused-imports #-}
2 {-# LANGUAGE ForeignFunctionInterface #-}
3 module Data.Time.LocalTime.Internal.TimeZone
4 (
5 -- * Time zones
6 TimeZone(..),timeZoneOffsetString,timeZoneOffsetString',timeZoneOffsetString'',minutesToTimeZone,hoursToTimeZone,utc,
7
8 -- getting the locale time zone
9 getTimeZone,getCurrentTimeZone
10 ) where
11
12 --import System.Time.Calendar.Format
13 import Data.Time.Calendar.Private
14 import Data.Time.Clock.System
15 import Data.Time.Clock.POSIX
16 import Data.Time.Clock.Internal.UTCTime
17
18 #if __GLASGOW_HASKELL__ >= 709
19 import Foreign
20 #else
21 import Foreign.Safe
22 #endif
23 import Foreign.C
24 import Control.DeepSeq
25 import Data.Typeable
26 import Data.Data
27
28 -- | A TimeZone is a whole number of minutes offset from UTC, together with a name and a \"just for summer\" flag.
29 data TimeZone = TimeZone {
30 -- | The number of minutes offset from UTC. Positive means local time will be later in the day than UTC.
31 timeZoneMinutes :: Int,
32 -- | Is this time zone just persisting for the summer?
33 timeZoneSummerOnly :: Bool,
34 -- | The name of the zone, typically a three- or four-letter acronym.
35 timeZoneName :: String
36 } deriving (Eq,Ord,Data, Typeable)
37
38 instance NFData TimeZone where
39 rnf (TimeZone m so n) = rnf m `seq` rnf so `seq` rnf n `seq` ()
40
41 -- | Create a nameless non-summer timezone for this number of minutes.
42 minutesToTimeZone :: Int -> TimeZone
43 minutesToTimeZone m = TimeZone m False ""
44
45 -- | Create a nameless non-summer timezone for this number of hours.
46 hoursToTimeZone :: Int -> TimeZone
47 hoursToTimeZone i = minutesToTimeZone (60 * i)
48
49 showT :: Bool -> PadOption -> Int -> String
50 showT False opt t = showPaddedNum opt ((div t 60) * 100 + (mod t 60))
51 showT True opt t = let
52 opt' = case opt of
53 NoPad -> NoPad
54 Pad i c -> Pad (max 0 $ i - 3) c
55 in showPaddedNum opt' (div t 60) ++ ":" ++ show2 (mod t 60)
56
57 timeZoneOffsetString'' :: Bool -> PadOption -> TimeZone -> String
58 timeZoneOffsetString'' colon opt (TimeZone t _ _) | t < 0 = '-':(showT colon opt (negate t))
59 timeZoneOffsetString'' colon opt (TimeZone t _ _) = '+':(showT colon opt t)
60
61 -- | Text representing the offset of this timezone, such as \"-0800\" or \"+0400\" (like @%z@ in formatTime), with arbitrary padding.
62 timeZoneOffsetString' :: Maybe Char -> TimeZone -> String
63 timeZoneOffsetString' Nothing = timeZoneOffsetString'' False NoPad
64 timeZoneOffsetString' (Just c) = timeZoneOffsetString'' False $ Pad 4 c
65
66 -- | Text representing the offset of this timezone, such as \"-0800\" or \"+0400\" (like @%z@ in formatTime).
67 timeZoneOffsetString :: TimeZone -> String
68 timeZoneOffsetString = timeZoneOffsetString'' False (Pad 4 '0')
69
70 -- | This only shows the time zone name, or offset if the name is empty.
71 instance Show TimeZone where
72 show zone@(TimeZone _ _ "") = timeZoneOffsetString zone
73 show (TimeZone _ _ name) = name
74
75 -- | The UTC time zone.
76 utc :: TimeZone
77 utc = TimeZone 0 False "UTC"
78
79 {-# CFILES cbits/HsTime.c #-}
80 foreign import ccall unsafe "HsTime.h get_current_timezone_seconds" get_current_timezone_seconds :: CTime -> Ptr CInt -> Ptr CString -> IO CLong
81
82 getTimeZoneCTime :: CTime -> IO TimeZone
83 getTimeZoneCTime ctime = with 0 (\pdst -> with nullPtr (\pcname -> do
84 secs <- get_current_timezone_seconds ctime pdst pcname
85 case secs of
86 0x80000000 -> fail "localtime_r failed"
87 _ -> do
88 dst <- peek pdst
89 cname <- peek pcname
90 name <- peekCString cname
91 return (TimeZone (div (fromIntegral secs) 60) (dst == 1) name)
92 ))
93
94 toCTime :: Int64 -> IO CTime
95 toCTime t = let
96 tt = fromIntegral t
97 t' = fromIntegral tt
98 -- there's no instance Bounded CTime, so this is the easiest way to check for overflow
99 in if t' == t then return $ CTime tt else fail "Data.Time.LocalTime.Internal.TimeZone.toCTime: Overflow"
100
101 -- | Get the local time-zone for a given time (varying as per summertime adjustments).
102 getTimeZoneSystem :: SystemTime -> IO TimeZone
103 getTimeZoneSystem t = do
104 ctime <- toCTime $ systemSeconds t
105 getTimeZoneCTime ctime
106
107 -- | Get the local time-zone for a given time (varying as per summertime adjustments).
108 getTimeZone :: UTCTime -> IO TimeZone
109 getTimeZone t = do
110 ctime <- toCTime $ floor $ utcTimeToPOSIXSeconds t
111 getTimeZoneCTime ctime
112
113 -- | Get the current time-zone.
114 getCurrentTimeZone :: IO TimeZone
115 getCurrentTimeZone = getSystemTime >>= getTimeZoneSystem