[project @ 1999-02-17 15:57:20 by simonm]
[ghc-hetmet.git] / ghc / lib / std / Time.lhs
1 %
2 % (c) The GRASP/AQUA Project, Glasgow University, 1995-99
3 %
4 \section[Time]{Haskell 1.4 Time of Day Library}
5
6 The {\em Time} library provides standard functionality for
7 clock times, including timezone information (i.e, the functionality of
8 "time.h",  adapted to the Haskell environment), It follows RFC 1129 in
9 its use of Coordinated Universal Time (UTC).
10
11 \begin{code}
12 {-# OPTIONS -#include "cbits/timezone.h" -#include "cbits/stgio.h"  #-}
13 module Time 
14      (
15         Month(..)
16      ,  Day(..)
17
18      ,  ClockTime(..) -- non-standard, lib. report gives this as abstract
19      ,  getClockTime
20
21      ,  TimeDiff(..)
22      ,  diffClockTimes
23      ,  addToClockTime
24
25      ,  timeDiffToString  -- non-standard
26      ,  formatTimeDiff    -- non-standard
27
28      ,  CalendarTime(..)
29      ,  toCalendarTime
30      ,  toUTCTime
31      ,  toClockTime
32      ,  calendarTimeToString
33      ,  formatCalendarTime
34
35      ) where
36
37 #ifdef __HUGS__
38 import PreludeBuiltin
39 #else
40 import PrelBase
41 import PrelIOBase
42 import PrelHandle
43 import PrelArr
44 import PrelST
45 import PrelAddr
46 import PrelPack         ( unpackCString )
47 #endif
48
49 import Ix
50 import Char             ( intToDigit )
51 import Locale
52
53 \end{code}
54
55 One way to partition and give name to chunks of a year and a week:
56
57 \begin{code}
58 data Month
59  = January   | February | March    | April
60  | May       | June     | July     | August
61  | September | October  | November | December
62  deriving (Eq, Ord, Enum, Bounded, Ix, Read, Show)
63
64 data Day 
65  = Sunday   | Monday | Tuesday | Wednesday
66  | Thursday | Friday | Saturday
67  deriving (Eq, Ord, Enum, Bounded, Ix, Read, Show)
68
69 \end{code}
70
71 @ClockTime@ is an abstract type, used for the internal clock time.
72 Clock times may be compared, converted to strings, or converted to an
73 external calendar time @CalendarTime@.
74
75 \begin{code}
76 #ifdef __HUGS__
77 -- I believe Int64 is more than big enough.
78 -- In fact, I think one of Int32 or Word32 would do. - ADR
79 data ClockTime = TOD Int64 Int64 deriving (Eq, Ord)
80 #else
81 data ClockTime = TOD Integer Integer deriving (Eq, Ord)
82 #endif
83 \end{code}
84
85 When a @ClockTime@ is shown, it is converted to a string of the form
86 @"Mon Nov 28 21:45:41 GMT 1994"@.
87
88 For now, we are restricted to roughly:
89 Fri Dec 13 20:45:52 1901 through Tue Jan 19 03:14:07 2038, because
90 we use the C library routines based on 32 bit integers.
91
92 \begin{code}
93 #ifdef __HUGS__
94 #warning Show ClockTime is bogus
95 instance Show ClockTime
96 #else
97 instance Show ClockTime where
98     showsPrec p (TOD (S# i) _nsec) = 
99       case int2Integer# i of (# s, d #) -> showsPrec p (TOD (J# s d) _nsec)
100     showsPrec _ (TOD (J# s# d#) _nsec) = 
101       showString $ unsafePerformIO $ do
102             buf <- allocChars 38 -- exactly enough for error message
103             str <- _ccall_ showTime (I# s#) d# buf
104             return (unpackCString str)
105
106     showList = showList__ (showsPrec 0)
107 #endif
108 \end{code}
109
110
111 @CalendarTime@ is a user-readable and manipulable
112 representation of the internal $ClockTime$ type.  The
113 numeric fields have the following ranges.
114
115 \begin{verbatim}
116 Value         Range             Comments
117 -----         -----             --------
118
119 year    -maxInt .. maxInt       [Pre-Gregorian dates are inaccurate]
120 mon           0 .. 11           [Jan = 0, Dec = 11]
121 day           1 .. 31
122 hour          0 .. 23
123 min           0 .. 59
124 sec           0 .. 61           [Allows for two leap seconds]
125 picosec       0 .. (10^12)-1    [This could be over-precise?]
126 wday          0 .. 6            [Sunday = 0, Saturday = 6]
127 yday          0 .. 365          [364 in non-Leap years]
128 tz       -43200 .. 43200        [Variation from UTC in seconds]
129 \end{verbatim}
130
131 The {\em tzname} field is the name of the time zone.  The {\em isdst}
132 field indicates whether Daylight Savings Time would be in effect.
133
134 \begin{code}
135 data CalendarTime 
136  = CalendarTime  {
137      ctYear    :: Int,
138      ctMonth   :: Int,
139      ctDay     :: Int,
140      ctHour    :: Int,
141      ctMin     :: Int,
142      ctSec     :: Int,
143 #ifdef __HUGS__
144      ctPicosec :: Int64,
145 #else
146      ctPicosec :: Integer,
147 #endif
148      ctWDay    :: Day,
149      ctYDay    :: Int,
150      ctTZName  :: String,
151      ctTZ      :: Int,
152      ctIsDST   :: Bool
153  }
154  deriving (Eq,Ord,Read,Show)
155
156 \end{code}
157
158 The @TimeDiff@ type records the difference between two clock times in
159 a user-readable way.
160
161 \begin{code}
162 data TimeDiff
163  = TimeDiff {
164      tdYear    :: Int,
165      tdMonth   :: Int,
166      tdDay     :: Int,
167      tdHour    :: Int,
168      tdMin     :: Int,
169      tdSec     :: Int,
170 #ifdef __HUGS__
171      tdPicosec :: Int64   -- not standard
172 #else
173      tdPicosec :: Integer -- not standard
174 #endif
175    }
176    deriving (Eq,Ord,Read,Show)
177 \end{code}
178
179 @getClockTime@ returns the current time in its internal representation.
180
181 \begin{code}
182 #ifdef __HUGS__
183 getClockTime :: IO ClockTime
184 getClockTime = do
185     i1 <- malloc1
186     i2 <- malloc1
187     rc <- prim_getClockTime i1 i2
188     if rc == 0 
189         then do
190             sec  <- cvtUnsigned i1
191             nsec <- cvtUnsigned i2
192             return (TOD sec (nsec * 1000))
193         else
194             constructErrorAndFail "getClockTime"
195   where
196     malloc1 = primNewByteArray sizeof_int64
197     cvtUnsigned arr = primReadInt64Array arr 0
198 #else
199 getClockTime :: IO ClockTime
200 getClockTime = do
201     i1 <- malloc1
202     i2 <- malloc1
203     rc <- _ccall_ getClockTime i1 i2
204     if rc == (0 ::Int)
205         then do
206             sec  <- cvtUnsigned i1
207             nsec <- cvtUnsigned i2
208             return (TOD sec (nsec * 1000))
209         else
210             constructErrorAndFail "getClockTime"
211   where
212     malloc1 = IO $ \ s# ->
213         case newIntArray# 1# s# of 
214           (# s2#, barr# #) -> 
215                 (# s2#, MutableByteArray bottom barr# #)
216
217     --  The C routine fills in an unsigned word.  We don't have 
218     --  `unsigned2Integer#,' so we freeze the data bits and use them 
219     --  for an MP_INT structure.  Note that zero is still handled specially,
220     --  although (J# 1# (ptr to 0#)) is probably acceptable to gmp.
221
222     cvtUnsigned (MutableByteArray _ arr#) = IO $ \ s# ->
223         case readIntArray# arr# 0# s# of 
224           (# s2#, r# #) ->
225             if r# ==# 0# 
226                 then (# s2#, 0 #)
227                 else case unsafeFreezeByteArray# arr# s2# of
228                         (# s3#, frozen# #) -> 
229                                 (# s3#, J# 1# frozen# #)
230 #endif
231 \end{code}
232
233 @addToClockTime@ {\em d} {\em t} adds a time difference {\em d} and a
234 clock time {\em t} to yield a new clock time.  The difference {\em d}
235 may be either positive or negative.  @[diffClockTimes@ {\em t1} {\em
236 t2} returns the difference between two clock times {\em t1} and {\em
237 t2} as a @TimeDiff@.
238
239
240 \begin{code}
241 #ifdef __HUGS__
242 addToClockTime  :: TimeDiff  -> ClockTime -> ClockTime
243 addToClockTime (TimeDiff year mon day hour min sec psec) 
244                (TOD c_sec c_psec) = unsafePerformIO $ do
245     res <- allocWords sizeof_int64
246     rc <- prim_toClockSec year mon day hour min sec 0 res 
247     if rc /= (0::Int)
248      then do
249             diff_sec <- primReadInt64Array res 0
250             let diff_psec = psec
251             return (TOD (c_sec + diff_sec) (c_psec + diff_psec))
252      else
253           error "Time.addToClockTime: can't perform conversion of TimeDiff"
254 #else
255 addToClockTime  :: TimeDiff  -> ClockTime -> ClockTime
256 addToClockTime (TimeDiff year mon day hour min sec psec) 
257                (TOD c_sec c_psec) = unsafePerformIO $ do
258     res <- allocWords (``sizeof(time_t)'')
259     ptr <- _ccall_ toClockSec year mon day hour min sec (0::Int) res 
260     let (A# ptr#) = ptr
261     if ptr /= nullAddr
262      then let
263             diff_sec  = (int2Integer (indexIntOffAddr# ptr# 0#))
264             diff_psec = psec
265           in
266           return (TOD (c_sec + diff_sec) (c_psec + diff_psec))
267      else
268           error "Time.addToClockTime: can't perform conversion of TimeDiff"
269 #endif
270
271 diffClockTimes  :: ClockTime -> ClockTime -> TimeDiff
272 diffClockTimes tod_a tod_b =
273   let
274    CalendarTime year_a mon_a day_a hour_a min_a sec_a psec_a _ _ _ _ _ = toUTCTime tod_a
275    CalendarTime year_b mon_b day_b hour_b min_b sec_b psec_b _ _ _ _ _ = toUTCTime tod_b
276   in
277   TimeDiff (year_a - year_b) 
278            (mon_a  - mon_b) 
279            (day_a  - day_b)
280            (hour_a - hour_b)
281            (min_a  - min_b)
282            (sec_a  - sec_b)
283            (psec_a - psec_b)
284 \end{code}
285
286 @toCalendarTime@ {\em t} converts {\em t} to a local time, modified by
287 the current timezone and daylight savings time settings.  @toUTCTime@
288 {\em t} converts {\em t} into UTC time.  @toClockTime@ {\em l}
289 converts {\em l} into the corresponding internal @ClockTime@.  The
290 {\em wday}, {\em yday}, {\em tzname}, and {\em isdst} fields are
291 ignored.
292
293 \begin{code}
294 #ifdef __HUGS__
295 toCalendarTime :: ClockTime -> IO CalendarTime
296 toCalendarTime (TOD sec psec) = do
297     res    <- allocWords sizeof_int64
298     zoneNm <- allocChars 32
299     prim_SETZONE res zoneNm
300     rc <- prim_toLocalTime sec res
301     if rc /= 0
302      then constructErrorAndFail "Time.toCalendarTime: out of range"
303      else do
304        sec   <-  get_tm_sec   res
305        min   <-  get_tm_min   res
306        hour  <-  get_tm_hour  res
307        mday  <-  get_tm_mday  res
308        mon   <-  get_tm_mon   res
309        year  <-  get_tm_year  res
310        wday  <-  get_tm_wday  res
311        yday  <-  get_tm_yday  res
312        isdst <-  get_tm_isdst res
313        zone  <-  prim_ZONE    res
314        tz    <-  prim_GMTOFF  res
315        tzname <- primUnpackCString zone
316        return (CalendarTime (1900+year) mon mday hour min sec psec 
317                             (toEnum wday) yday tzname tz (isdst /= 0))
318
319 toUTCTime :: ClockTime -> CalendarTime
320 toUTCTime  (TOD sec psec) = unsafePerformIO $ do
321        res    <- allocWords sizeof_int64
322        zoneNm <- allocChars 32
323        prim_SETZONE res zoneNm
324        rc <- prim_toUTCTime sec res
325        if rc /= 0
326         then error "Time.toUTCTime: out of range"
327         else do
328             sec   <- get_tm_sec  res
329             min   <- get_tm_min  res
330             hour  <- get_tm_hour res
331             mday  <- get_tm_mday res
332             mon   <- get_tm_mon  res
333             year  <- get_tm_year res
334             wday  <- get_tm_wday res
335             yday  <- get_tm_yday res
336             return (CalendarTime (1900+year) mon mday hour min sec psec 
337                           (toEnum wday) yday "UTC" 0 False)
338
339 toClockTime :: CalendarTime -> ClockTime
340 toClockTime (CalendarTime year mon mday hour min sec psec wday yday tzname tz isdst) =
341     if psec < 0 || psec > 999999999999 then
342         error "Time.toClockTime: picoseconds out of range"
343     else if tz < -43200 || tz > 43200 then
344         error "Time.toClockTime: timezone offset out of range"
345     else
346         unsafePerformIO ( do
347             res <- allocWords sizeof_int64
348             rc <- prim_toClockSec year mon mday hour min sec isDst res
349             if rc /= (0::Int)
350              then do
351                tm <- primReadInt64Array res 0
352                return (TOD tm psec)
353              else error "Time.toClockTime: can't perform conversion"
354         )
355     where
356      isDst = if isdst then (1::Int) else 0
357 #else
358 toCalendarTime :: ClockTime -> IO CalendarTime
359 toCalendarTime (TOD (S# i) psec) 
360   = case int2Integer# i of (# s, d #) -> toCalendarTime (TOD (J# s d) psec)
361 toCalendarTime (TOD (J# s# d#) psec) = do
362     res    <- allocWords (``sizeof(struct tm)''::Int)
363     zoneNm <- allocChars 32
364     _casm_ ``SETZONE((struct tm *)%0,(char *)%1); '' res zoneNm
365     tm     <- _ccall_ toLocalTime (I# s#) d# res
366     if tm == nullAddr
367      then constructErrorAndFail "Time.toCalendarTime: out of range"
368      else do
369        sec   <-  _casm_ ``%r = ((struct tm *)%0)->tm_sec;'' tm
370        min   <-  _casm_ ``%r = ((struct tm *)%0)->tm_min;'' tm
371        hour  <-  _casm_ ``%r = ((struct tm *)%0)->tm_hour;'' tm
372        mday  <-  _casm_ ``%r = ((struct tm *)%0)->tm_mday;'' tm
373        mon   <-  _casm_ ``%r = ((struct tm *)%0)->tm_mon;'' tm
374        year  <-  _casm_ ``%r = ((struct tm *)%0)->tm_year;'' tm
375        wday  <-  _casm_ ``%r = ((struct tm *)%0)->tm_wday;'' tm
376        yday  <-  _casm_ ``%r = ((struct tm *)%0)->tm_yday;'' tm
377        isdst <-  _casm_ ``%r = ((struct tm *)%0)->tm_isdst;'' tm
378        zone  <-  _ccall_ ZONE tm
379        tz    <-  _ccall_ GMTOFF tm
380        let tzname = unpackCString zone
381        return (CalendarTime (1900+year) mon mday hour min sec psec 
382                             (toEnum wday) yday tzname tz (isdst /= (0::Int)))
383
384 toUTCTime :: ClockTime -> CalendarTime
385 toUTCTime (TOD (S# i) psec) 
386   = case int2Integer# i of (# s, d #) -> toUTCTime (TOD (J# s d) psec)
387 toUTCTime  (TOD (J# s# d#) psec) = unsafePerformIO $ do
388        res    <- allocWords (``sizeof(struct tm)''::Int)
389        zoneNm <- allocChars 32
390        _casm_ ``SETZONE((struct tm *)%0,(char *)%1); '' res zoneNm
391        tm     <-  _ccall_ toUTCTime (I# s#) d# res
392        if tm == nullAddr
393         then error "Time.toUTCTime: out of range"
394         else do
395             sec   <- _casm_ ``%r = ((struct tm *)%0)->tm_sec;'' tm
396             min   <- _casm_ ``%r = ((struct tm *)%0)->tm_min;'' tm
397             hour  <- _casm_ ``%r = ((struct tm *)%0)->tm_hour;'' tm
398             mday  <- _casm_ ``%r = ((struct tm *)%0)->tm_mday;'' tm
399             mon   <- _casm_ ``%r = ((struct tm *)%0)->tm_mon;'' tm
400             year  <- _casm_ ``%r = ((struct tm *)%0)->tm_year;'' tm
401             wday  <- _casm_ ``%r = ((struct tm *)%0)->tm_wday;'' tm
402             yday  <- _casm_ ``%r = ((struct tm *)%0)->tm_yday;'' tm
403             return (CalendarTime (1900+year) mon mday hour min sec psec 
404                           (toEnum wday) yday "UTC" 0 False)
405
406 toClockTime :: CalendarTime -> ClockTime
407 toClockTime (CalendarTime year mon mday hour min sec psec _wday _yday _tzname tz isdst) =
408     if psec < 0 || psec > 999999999999 then
409         error "Time.toClockTime: picoseconds out of range"
410     else if tz < -43200 || tz > 43200 then
411         error "Time.toClockTime: timezone offset out of range"
412     else
413         unsafePerformIO ( do
414             res <- allocWords (``sizeof(time_t)'')
415             ptr <- _ccall_ toClockSec year mon mday hour min sec isDst res
416             let (A# ptr#) = ptr
417             if ptr /= nullAddr
418              then return (TOD (int2Integer (indexIntOffAddr# ptr# 0#)) psec)
419              else error "Time.toClockTime: can't perform conversion"
420         )
421     where
422      isDst = if isdst then (1::Int) else 0
423 #endif
424
425 bottom :: (Int,Int)
426 bottom = error "Time.bottom"
427
428
429 -- (copied from PosixUtil, for now)
430 -- Allocate a mutable array of characters with no indices.
431
432 #ifdef __HUGS__
433 allocChars :: Int -> IO (PrimMutableByteArray RealWorld)
434 allocChars size = primNewByteArray size
435
436 -- Allocate a mutable array of words with no indices
437
438 allocWords :: Int -> IO (PrimMutableByteArray RealWorld)
439 allocWords size = primNewByteArray size
440 #else
441 allocChars :: Int -> IO (MutableByteArray RealWorld ())
442 allocChars (I# size#) = IO $ \ s# ->
443     case newCharArray# size# s# of 
444       (# s2#, barr# #) -> 
445         (# s2#, MutableByteArray bot barr# #)
446   where
447     bot = error "Time.allocChars"
448
449 -- Allocate a mutable array of words with no indices
450
451 allocWords :: Int -> IO (MutableByteArray RealWorld ())
452 allocWords (I# size#) = IO $ \ s# ->
453     case newIntArray# size# s# of 
454       (# s2#, barr# #) -> 
455         (# s2#, MutableByteArray bot barr# #)
456   where
457     bot = error "Time.allocWords"
458 #endif
459 \end{code}
460
461 \begin{code}
462 calendarTimeToString  :: CalendarTime -> String
463 calendarTimeToString  =  formatCalendarTime defaultTimeLocale "%c"
464
465 formatCalendarTime :: TimeLocale -> String -> CalendarTime -> String
466 formatCalendarTime l fmt (CalendarTime year mon day hour min sec _
467                                        wday yday tzname _ _) =
468         doFmt fmt
469   where doFmt ('%':c:cs) = decode c ++ doFmt cs
470         doFmt (c:cs) = c : doFmt cs
471         doFmt "" = ""
472
473         decode 'A' = fst (wDays l  !! fromEnum wday) -- day of the week, full name
474         decode 'a' = snd (wDays l  !! fromEnum wday) -- day of the week, abbrev.
475         decode 'B' = fst (months l !! fromEnum mon)  -- month, full name
476         decode 'b' = snd (months l !! fromEnum mon)  -- month, abbrev
477         decode 'h' = snd (months l !! fromEnum mon)  -- ditto
478         decode 'C' = show2 (year `quot` 100)         -- century
479         decode 'c' = doFmt (dateTimeFmt l)           -- locale's data and time format.
480         decode 'D' = doFmt "%m/%d/%y"
481         decode 'd' = show2 day                       -- day of the month
482         decode 'e' = show2' day                      -- ditto, padded
483         decode 'H' = show2 hour                      -- hours, 24-hour clock, padded
484         decode 'I' = show2 (to12 hour)               -- hours, 12-hour clock
485         decode 'j' = show3 yday                      -- day of the year
486         decode 'k' = show2' hour                     -- hours, 24-hour clock, no padding
487         decode 'l' = show2' (to12 hour)              -- hours, 12-hour clock, no padding
488         decode 'M' = show2 min                       -- minutes
489         decode 'm' = show2 (fromEnum mon+1)          -- numeric month
490         decode 'n' = "\n"
491         decode 'p' = (if hour < 12 then fst else snd) (amPm l) -- am or pm
492         decode 'R' = doFmt "%H:%M"
493         decode 'r' = doFmt (time12Fmt l)
494         decode 'T' = doFmt "%H:%M:%S"
495         decode 't' = "\t"
496         decode 'S' = show2 sec                       -- seconds
497         decode 's' = show2 sec                       -- number of secs since Epoch. (ToDo.)
498         decode 'U' = show2 ((yday + 7 - fromEnum wday) `div` 7) -- week number, starting on Sunday.
499         decode 'u' = show (let n = fromEnum wday in  -- numeric day of the week (1=Monday, 7=Sunday)
500                            if n == 0 then 7 else n)
501         decode 'V' =                                 -- week number (as per ISO-8601.)
502             let (week, days) =                       -- [yep, I've always wanted to be able to display that too.]
503                    (yday + 7 - if fromEnum wday > 0 then 
504                                fromEnum wday - 1 else 6) `divMod` 7
505             in  show2 (if days >= 4 then
506                           week+1 
507                        else if week == 0 then 53 else week)
508
509         decode 'W' =                                 -- week number, weeks starting on monday
510             show2 ((yday + 7 - if fromEnum wday > 0 then 
511                                fromEnum wday - 1 else 6) `div` 7)
512         decode 'w' = show (fromEnum wday)            -- numeric day of the week, weeks starting on Sunday.
513         decode 'X' = doFmt (timeFmt l)               -- locale's preferred way of printing time.
514         decode 'x' = doFmt (dateFmt l)               -- locale's preferred way of printing dates.
515         decode 'Y' = show year                       -- year, including century.
516         decode 'y' = show2 (year `rem` 100)          -- year, within century.
517         decode 'Z' = tzname                          -- timezone name
518         decode '%' = "%"
519         decode c   = [c]
520
521
522 show2, show2', show3 :: Int -> String
523 show2 x = [intToDigit (x `quot` 10), intToDigit (x `rem` 10)]
524
525 show2' x = if x < 10 then [ ' ', intToDigit x] else show2 x
526
527 show3 x = intToDigit (x `quot` 100) : show2 (x `rem` 100)
528
529 to12 :: Int -> Int
530 to12 h = let h' = h `mod` 12 in if h' == 0 then 12 else h'
531 \end{code}
532
533 Useful extensions for formatting TimeDiffs.
534
535 \begin{code}
536 timeDiffToString :: TimeDiff -> String
537 timeDiffToString = formatTimeDiff defaultTimeLocale "%c"
538
539 formatTimeDiff :: TimeLocale -> String -> TimeDiff -> String
540 formatTimeDiff l fmt (TimeDiff year month day hour min sec _)
541  = doFmt fmt
542   where 
543    doFmt ""         = ""
544    doFmt ('%':c:cs) = decode c ++ doFmt cs
545    doFmt (c:cs)     = c : doFmt cs
546
547    decode spec =
548     case spec of
549       'B' -> fst (months l !! fromEnum month)
550       'b' -> snd (months l !! fromEnum month)
551       'h' -> snd (months l !! fromEnum month)
552       'C' -> show2 (year `quot` 100)
553       'D' -> doFmt "%m/%d/%y"
554       'd' -> show2 day
555       'e' -> show2' day
556       'H' -> show2 hour
557       'I' -> show2 (to12 hour)
558       'k' -> show2' hour
559       'l' -> show2' (to12 hour)
560       'M' -> show2 min
561       'm' -> show2 (fromEnum month + 1)
562       'n' -> "\n"
563       'p' -> (if hour < 12 then fst else snd) (amPm l)
564       'R' -> doFmt "%H:%M"
565       'r' -> doFmt (time12Fmt l)
566       'T' -> doFmt "%H:%M:%S"
567       't' -> "\t"
568       'S' -> show2 sec
569       's' -> show2 sec -- Implementation-dependent, sez the lib doc..
570       'X' -> doFmt (timeFmt l)
571       'x' -> doFmt (dateFmt l)
572       'Y' -> show year
573       'y' -> show2 (year `rem` 100)
574       '%' -> "%"
575       c   -> [c]
576
577 \end{code}
578
579 \begin{code}
580 #ifdef __HUGS__
581 foreign import stdcall "libHS_cbits.so" "get_tm_sec"   get_tm_sec   :: Bytes -> IO Int
582 foreign import stdcall "libHS_cbits.so" "get_tm_min"   get_tm_min   :: Bytes -> IO Int
583 foreign import stdcall "libHS_cbits.so" "get_tm_hour"  get_tm_hour  :: Bytes -> IO Int
584 foreign import stdcall "libHS_cbits.so" "get_tm_mday"  get_tm_mday  :: Bytes -> IO Int
585 foreign import stdcall "libHS_cbits.so" "get_tm_mon"   get_tm_mon   :: Bytes -> IO Int
586 foreign import stdcall "libHS_cbits.so" "get_tm_year"  get_tm_year  :: Bytes -> IO Int
587 foreign import stdcall "libHS_cbits.so" "get_tm_wday"  get_tm_wday  :: Bytes -> IO Int
588 foreign import stdcall "libHS_cbits.so" "get_tm_yday"  get_tm_yday  :: Bytes -> IO Int
589 foreign import stdcall "libHS_cbits.so" "get_tm_isdst" get_tm_isdst :: Bytes -> IO Int
590
591 foreign import stdcall "libHS_cbits.so" "prim_ZONE"    prim_ZONE    :: Bytes -> IO Addr
592 foreign import stdcall "libHS_cbits.so" "prim_GMTOFF"  prim_GMTOFF  :: Bytes -> IO Int
593
594 foreign import stdcall "libHS_cbits.so" "prim_SETZONE" prim_SETZONE :: Bytes -> Bytes -> IO Int
595
596 foreign import stdcall "libHS_cbits.so" "sizeof_word"      sizeof_word      :: Int
597 foreign import stdcall "libHS_cbits.so" "sizeof_struct_tm" sizeof_struct_tm :: Int
598 foreign import stdcall "libHS_cbits.so" "sizeof_time_t"    sizeof_time_t    :: Int
599
600 -- believed to be at least 1 bit (the sign bit!) bigger than sizeof_time_t
601 sizeof_int64 :: Int
602 sizeof_int64 = 8
603
604 foreign import stdcall "libHS_cbits.so" "prim_getClockTime" prim_getClockTime :: Bytes -> Bytes -> IO Int
605 foreign import stdcall "libHS_cbits.so" "prim_toClockSec"   prim_toClockSec   :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> Bytes -> IO Int
606 foreign import stdcall "libHS_cbits.so" "prim_toLocalTime"  prim_toLocalTime  :: Int64 -> Bytes -> IO Int
607 foreign import stdcall "libHS_cbits.so" "prim_toUTCTime"    prim_toUTCTime    :: Int64 -> Bytes -> IO Int
608 #endif
609 \end{code}