[project @ 2004-03-05 18:00:35 by malcolm]
[ghc-base.git] / System / Time.hsc
index 6cb2590..9eeed25 100644 (file)
@@ -1,8 +1,5 @@
-{-# OPTIONS -optc-DNON_POSIX_SOURCE #-}
--- JRS 010117: we had to say NON_POSIX_SOURCE to get the resulting .hc
--- to compile on sparc-solaris.  Blargh.
 -----------------------------------------------------------------------------
--- 
+-- |
 -- Module      :  System.Time
 -- Copyright   :  (c) The University of Glasgow 2001
 -- License     :  BSD-style (see the file libraries/core/LICENSE)
@@ -11,8 +8,6 @@
 -- Stability   :  provisional
 -- Portability :  portable
 --
--- $Id: Time.hsc,v 1.2 2001/07/31 13:05:02 simonmar Exp $
---
 -- The standard Time library.
 --
 -----------------------------------------------------------------------------
@@ -101,16 +96,28 @@ module System.Time
 
      ) where
 
-#include "HsCore.h"
+#ifdef __GLASGOW_HASKELL__
+#include "HsBase.h"
+#endif
+
+#ifdef __NHC__
+#include <time.h>
+#define HAVE_TM_ZONE 1
+import Ix
+#endif
 
 import Prelude
 
 import Data.Ix
 import System.Locale
 import System.IO.Unsafe
-       
+
+#ifdef __HUGS__
+import Hugs.Time ( getClockTimePrim, toCalTimePrim, toClockTimePrim )
+#else
 import Foreign
 import Foreign.C
+#endif
 
 -- One way to partition and give name to chunks of a year and a week:
 
@@ -204,13 +211,19 @@ noTimeDiff = TimeDiff 0 0 0 0 0 0 0
 -- -----------------------------------------------------------------------------
 -- getClockTime returns the current time in its internal representation.
 
-#if HAVE_GETTIMEOFDAY
+getClockTime :: IO ClockTime
+#ifdef __HUGS__
+getClockTime = do
+  (sec,usec) <- getClockTimePrim
+  return (TOD (fromIntegral sec) ((fromIntegral usec) * 1000000))
+
+#elif HAVE_GETTIMEOFDAY
 getClockTime = do
   allocaBytes (#const sizeof(struct timeval)) $ \ p_timeval -> do
     throwErrnoIfMinus1_ "getClockTime" $ gettimeofday p_timeval nullPtr
-    sec  <- (#peek struct timeval,tv_sec)  p_timeval :: IO Int32
-    usec <- (#peek struct timeval,tv_usec) p_timeval :: IO Int32
-    return (TOD (fromIntegral sec) ((fromIntegral usec) * 1000))
+    sec  <- (#peek struct timeval,tv_sec)  p_timeval :: IO CTime
+    usec <- (#peek struct timeval,tv_usec) p_timeval :: IO CTime
+    return (TOD (fromIntegral sec) ((fromIntegral usec) * 1000000))
  
 #elif HAVE_FTIME
 getClockTime = do
@@ -218,7 +231,7 @@ getClockTime = do
   ftime p_timeb
   sec  <- (#peek struct timeb,time) p_timeb :: IO CTime
   msec <- (#peek struct timeb,millitm) p_timeb :: IO CUShort
-  return (TOD (fromIntegral sec) (fromIntegral msec * 1000{-ToDo: correct???-}))
+  return (TOD (fromIntegral sec) (fromIntegral msec * 1000000000))
 
 #else /* use POSIX time() */
 getClockTime = do
@@ -244,7 +257,9 @@ addToClockTime (TimeDiff year mon day hour min sec psec)
          cal      = toUTCTime (TOD (c_sec + sec_diff) (c_psec + psec))
                                                        -- FIXME! ^^^^
           new_mon  = fromEnum (ctMonth cal) + r_mon 
-         (month', yr_diff)
+         month' = fst tmp
+         yr_diff = snd tmp
+          tmp
            | new_mon < 0  = (toEnum (12 + new_mon), (-1))
            | new_mon > 11 = (toEnum (new_mon `mod` 12), 1)
            | otherwise    = (toEnum new_mon, 0)
@@ -301,6 +316,7 @@ normalizeTimeDiff td =
         , tdSec   = diffSecs
         }
 
+#ifndef __HUGS__
 -- -----------------------------------------------------------------------------
 -- How do we deal with timezones on this architecture?
 
@@ -320,12 +336,10 @@ gmtoff x    = (#peek struct tm,tm_gmtoff) x
 #   define tzname _tzname
 #  endif
 #  ifndef mingw32_TARGET_OS
-foreign label tzname :: Ptr (Ptr CChar)
+foreign import ccall unsafe "&tzname" tzname :: Ptr (Ptr CChar)
 #  else
-foreign import "ghcTimezone" unsafe timezone :: Ptr CLong
-foreign import "ghcTzname" unsafe tzname :: Ptr (Ptr CChar)
-#   def inline long  *ghcTimezone(void) { return &_timezone; }
-#   def inline char **ghcTzname(void) { return _tzname; }
+foreign import ccall unsafe "__hscore_timezone" timezone :: Ptr CLong
+foreign import ccall unsafe "__hscore_tzname"   tzname :: Ptr (Ptr CChar)
 #  endif
 zone x = do 
   dst <- (#peek struct tm,tm_isdst) x
@@ -336,26 +350,33 @@ zone x = do
 # endif /* ! HAVE_TZNAME */
 
 -- Get the offset in secs from UTC, if (struct tm) doesn't supply it. */
-#if defined(mingw32_TARGET_OS) || defined(cygwin32_TARGET_OS)
-#define timezone _timezone
-#endif
-
-# if HAVE_ALTZONE
-foreign label altzone  :: Ptr CTime
-foreign label timezone :: Ptr CTime
+# if HAVE_DECL_ALTZONE
+foreign import ccall "&altzone"  altzone  :: Ptr CTime
+foreign import ccall "&timezone" timezone :: Ptr CTime
 gmtoff x = do 
   dst <- (#peek struct tm,tm_isdst) x
   tz <- if dst then peek altzone else peek timezone
-  return (fromIntegral tz)
-#  define GMTOFF(x)     (((struct tm *)x)->tm_isdst ? altzone : timezone )
-# else /* ! HAVE_ALTZONE */
+  return (-fromIntegral tz)
+# else /* ! HAVE_DECL_ALTZONE */
+
+#if !defined(mingw32_TARGET_OS)
+foreign import ccall unsafe "timezone" timezone :: Ptr CLong
+#endif
+
 -- Assume that DST offset is 1 hour ...
 gmtoff x = do 
   dst <- (#peek struct tm,tm_isdst) x
   tz  <- peek timezone
-  if dst then return (fromIntegral tz - 3600) else return tz
-# endif /* ! HAVE_ALTZONE */
+   -- According to the documentation for tzset(), 
+   --   http://www.opengroup.org/onlinepubs/007908799/xsh/tzset.html
+   -- timezone offsets are > 0 west of the Prime Meridian.
+   --
+   -- This module assumes the interpretation of tm_gmtoff, i.e., offsets
+   -- are > 0 East of the Prime Meridian, so flip the sign.
+  return (- (if dst then (fromIntegral tz - 3600) else tz))
+# endif /* ! HAVE_DECL_ALTZONE */
 #endif  /* ! HAVE_TM_ZONE */
+#endif /* ! __HUGS__ */
 
 -- -----------------------------------------------------------------------------
 -- toCalendarTime t converts t to a local time, modified by
@@ -366,18 +387,68 @@ gmtoff x = do
 
 
 toCalendarTime :: ClockTime -> IO CalendarTime
-toCalendarTime =  clockToCalendarTime localtime False
+#ifdef __HUGS__
+toCalendarTime =  toCalTime False
+#elif HAVE_LOCALTIME_R
+toCalendarTime =  clockToCalendarTime_reentrant (throwAwayReturnPointer localtime_r) False
+#else
+toCalendarTime =  clockToCalendarTime_static localtime False
+#endif
 
-toUTCTime      :: ClockTime -> CalendarTime
-toUTCTime      =  unsafePerformIO . clockToCalendarTime gmtime True
+toUTCTime :: ClockTime -> CalendarTime
+#ifdef __HUGS__
+toUTCTime      =  unsafePerformIO . toCalTime True
+#elif HAVE_GMTIME_R
+toUTCTime      =  unsafePerformIO . clockToCalendarTime_reentrant (throwAwayReturnPointer gmtime_r) True
+#else
+toUTCTime      =  unsafePerformIO . clockToCalendarTime_static gmtime True
+#endif
 
--- ToDo: should be made thread safe, because localtime uses static storage,
--- or use the localtime_r version.
-clockToCalendarTime :: (Ptr CTime -> IO (Ptr CTm)) -> Bool -> ClockTime
+#ifdef __HUGS__
+toCalTime :: Bool -> ClockTime -> IO CalendarTime
+toCalTime toUTC (TOD s psecs)
+  | (s > fromIntegral (maxBound :: Int)) || 
+    (s < fromIntegral (minBound :: Int))
+  = error ((if toUTC then "toUTCTime: " else "toCalendarTime: ") ++
+           "clock secs out of range")
+  | otherwise = do
+    (sec,min,hour,mday,mon,year,wday,yday,isdst,zone,off) <- 
+               toCalTimePrim (if toUTC then 1 else 0) (fromIntegral s)
+    return (CalendarTime{ ctYear=1900+year
+                       , ctMonth=toEnum mon
+                       , ctDay=mday
+                       , ctHour=hour
+                       , ctMin=min
+                       , ctSec=sec
+                       , ctPicosec=psecs
+                       , ctWDay=toEnum wday
+                       , ctYDay=yday
+                       , ctTZName=(if toUTC then "UTC" else zone)
+                       , ctTZ=(if toUTC then 0 else off)
+                       , ctIsDST=not toUTC && (isdst/=0)
+                       })
+#else /* ! __HUGS__ */
+throwAwayReturnPointer :: (Ptr CTime -> Ptr CTm -> IO (Ptr CTm))
+                       -> (Ptr CTime -> Ptr CTm -> IO (       ))
+throwAwayReturnPointer fun x y = fun x y >> return ()
+
+clockToCalendarTime_static :: (Ptr CTime -> IO (Ptr CTm)) -> Bool -> ClockTime
         -> IO CalendarTime
-clockToCalendarTime fun is_utc (TOD secs psec) = do
-  withObject (fromIntegral secs :: CTime)  $ \ p_timer -> do
+clockToCalendarTime_static fun is_utc (TOD secs psec) = do
+  with (fromIntegral secs :: CTime)  $ \ p_timer -> do
     p_tm <- fun p_timer        -- can't fail, according to POSIX
+    clockToCalendarTime_aux is_utc p_tm psec
+
+clockToCalendarTime_reentrant :: (Ptr CTime -> Ptr CTm -> IO ()) -> Bool -> ClockTime
+        -> IO CalendarTime
+clockToCalendarTime_reentrant fun is_utc (TOD secs psec) = do
+  with (fromIntegral secs :: CTime)  $ \ p_timer -> do
+    allocaBytes (#const sizeof(struct tm)) $ \ p_tm -> do
+      fun p_timer p_tm
+      clockToCalendarTime_aux is_utc p_tm psec
+
+clockToCalendarTime_aux :: Bool -> Ptr CTm -> Integer -> IO CalendarTime
+clockToCalendarTime_aux is_utc p_tm psec = do
     sec   <-  (#peek struct tm,tm_sec  ) p_tm :: IO CInt
     min   <-  (#peek struct tm,tm_min  ) p_tm :: IO CInt
     hour  <-  (#peek struct tm,tm_hour ) p_tm :: IO CInt
@@ -408,9 +479,16 @@ clockToCalendarTime fun is_utc (TOD secs psec) = do
                (if is_utc then "UTC" else tzname)
                (if is_utc then 0     else fromIntegral tz)
                (if is_utc then False else isdst /= 0))
-
+#endif /* ! __HUGS__ */
 
 toClockTime :: CalendarTime -> ClockTime
+#ifdef __HUGS__
+toClockTime (CalendarTime yr mon mday hour min sec psec
+                         _wday _yday _tzname tz _isdst) =
+  unsafePerformIO $ do
+    s <- toClockTimePrim (yr-1900) (fromEnum mon) mday hour min sec tz
+    return (TOD (fromIntegral s) psec)
+#else /* ! __HUGS__ */
 toClockTime (CalendarTime year mon mday hour min sec psec 
                          _wday _yday _tzname tz isdst) =
 
@@ -451,7 +529,8 @@ toClockTime (CalendarTime year mon mday hour min sec psec
         -- 
         gmtoff <- gmtoff p_tm
        let res = fromIntegral t - tz + fromIntegral gmtoff
-       return (TOD (fromIntegral res) 0)
+       return (TOD (fromIntegral res) psec)
+#endif /* ! __HUGS__ */
 
 -- -----------------------------------------------------------------------------
 -- Converting time values to strings.
@@ -593,27 +672,36 @@ formatTimeDiff l fmt td@(TimeDiff year month day hour min sec _)
 
    addS v s = if abs v == 1 then fst s else snd s
 
-
+#ifndef __HUGS__
 -- -----------------------------------------------------------------------------
 -- Foreign time interface (POSIX)
 
 type CTm = () -- struct tm
 
-foreign import unsafe localtime :: Ptr CTime -> IO (Ptr CTm)
-foreign import unsafe gmtime    :: Ptr CTime -> IO (Ptr CTm)
-foreign import unsafe mktime    :: Ptr CTm   -> IO CTime
-foreign import unsafe time      :: Ptr CTime -> IO CTime
+#if HAVE_LOCALTIME_R
+foreign import ccall unsafe localtime_r :: Ptr CTime -> Ptr CTm -> IO (Ptr CTm)
+#else
+foreign import ccall unsafe localtime   :: Ptr CTime -> IO (Ptr CTm)
+#endif
+#if HAVE_GMTIME_R
+foreign import ccall unsafe gmtime_r    :: Ptr CTime -> Ptr CTm -> IO (Ptr CTm)
+#else
+foreign import ccall unsafe gmtime      :: Ptr CTime -> IO (Ptr CTm)
+#endif
+foreign import ccall unsafe mktime      :: Ptr CTm   -> IO CTime
+foreign import ccall unsafe time        :: Ptr CTime -> IO CTime
 
 #if HAVE_GETTIMEOFDAY
 type CTimeVal = ()
-foreign import unsafe gettimeofday :: Ptr CTimeVal -> Ptr () -> IO CInt
+foreign import ccall unsafe gettimeofday :: Ptr CTimeVal -> Ptr () -> IO CInt
 #endif
 
 #if HAVE_FTIME
 type CTimeB = ()
 #ifndef mingw32_TARGET_OS
-foreign import unsafe ftime :: Ptr CTimeB -> IO CInt
+foreign import ccall unsafe ftime :: Ptr CTimeB -> IO CInt
 #else
-foreign import unsafe ftime :: Ptr CTimeB -> IO ()
+foreign import ccall unsafe ftime :: Ptr CTimeB -> IO ()
 #endif
 #endif
+#endif /* ! __HUGS__ */