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