File: Time.hsc

package info (click to toggle)
haskell-hsql 1.8.1-4
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 140 kB
  • sloc: haskell: 543; makefile: 3
file content (143 lines) | stat: -rw-r--r-- 4,689 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
{-# LANGUAGE CPP,ForeignFunctionInterface #-}
module DB.HSQL.Type.Time where

import Control.Monad(mplus)
import System.IO.Unsafe(unsafePerformIO)
import System.Time(ClockTime(..),CalendarTime(..)
                  ,getClockTime,toCalendarTime,toUTCTime)
import Text.ParserCombinators.ReadP(ReadP,char,skipSpaces,readP_to_S)
import Text.Read.Lex(readDecP)
import Foreign(Ptr,allocaBytes,pokeByteOff)
import Foreign.C(CTime(..),CInt)

import DB.HSQL.Type
    (SqlType(SqlTimeTZ,SqlTime,SqlDate,SqlDateTimeTZ,SqlDateTime
            ,SqlTimeStamp,SqlText))
import Database.HSQL.Types(SqlBind(..))

#include <time.h>

-- |
mkClockTime :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> ClockTime
mkClockTime year mon mday hour min sec tz =
    unsafePerformIO $ do
      allocaBytes (#const sizeof(struct tm)) $ \p_tm -> do
	(#poke struct tm,tm_sec  ) p_tm	(fromIntegral sec  :: CInt)
        (#poke struct tm,tm_min  ) p_tm	(fromIntegral min  :: CInt)
        (#poke struct tm,tm_hour ) p_tm	(fromIntegral hour :: CInt)
        (#poke struct tm,tm_mday ) p_tm	(fromIntegral mday :: CInt)
        (#poke struct tm,tm_mon  ) p_tm	(fromIntegral (mon-1) :: CInt)
        (#poke struct tm,tm_year ) p_tm	(fromIntegral (year-1900) :: CInt)
        (#poke struct tm,tm_isdst) p_tm	(-1 :: CInt)
	t <- mktime p_tm
        let t'=
#if __GLASGOW_HASKELL__ >= 603
                fromEnum t
#else
                t
#endif
        return (TOD (fromIntegral t' + fromIntegral (tz-currTZ)) 0)

foreign import ccall unsafe 
  mktime :: Ptr () -> IO CTime

-- |
{-# NOINLINE currTZ #-}
currTZ :: Int
currTZ = 
    ctTZ (unsafePerformIO (getClockTime >>= toCalendarTime)) -- Hack

-- |
parseTZ :: ReadP Int
parseTZ =  
    (char '+' >> readDecP) `mplus` (char '-' >> fmap negate readDecP)

-- |
f_read :: ReadP a -> String -> Maybe a
f_read f s = case readP_to_S f s of 
               [(x,_)] -> Just x

-- |
readHMS :: ReadP (Int, Int, Int)
readHMS = do
  hour  <- readDecP
  char ':'
  minutes <- readDecP
  char ':'
  seconds <- readDecP
  return (hour, minutes, seconds)

-- |
readYMD :: ReadP (Int, Int, Int)
readYMD = do
  year  <- readDecP
  char '-'
  month <- readDecP
  char '-'
  day   <- readDecP
  return (year, month, day)

-- |
readDateTime :: ReadP (Int, Int, Int, Int, Int, Int)
readDateTime = do
  (year, month, day) <- readYMD
  skipSpaces
  (hour, minutes, seconds) <- readHMS
  return (year, month, day, hour, minutes, seconds)


-- |
instance SqlBind ClockTime where
    fromSqlValue SqlTimeTZ s = f_read getTimeTZ s
	where getTimeTZ :: ReadP ClockTime
	      getTimeTZ = do
		(hour, minutes, seconds) <- readHMS
		(char '.' >> readDecP) `mplus` (return 0)
		tz <- parseTZ
		return (mkClockTime 1970 1 1 hour minutes seconds (tz*3600))
    fromSqlValue SqlTime s = f_read getTime s
	where getTime :: ReadP ClockTime
	      getTime = do
		(hour, minutes, seconds) <- readHMS
		return (mkClockTime 1970 1 1 hour minutes seconds currTZ)
    fromSqlValue SqlDate s = f_read getDate s
	where getDate :: ReadP ClockTime
	      getDate = do
		(year, month, day) <- readYMD
		return (mkClockTime year month day 0 0 0 currTZ)
    fromSqlValue SqlDateTimeTZ s = f_read getDateTimeTZ s
	where getDateTimeTZ :: ReadP ClockTime
	      getDateTimeTZ = do
	        (year, month, day, hour, minutes, seconds) <- readDateTime
		char '.' >> readDecP -- ) `mplus` (return 0)
                tz <- parseTZ
		return (mkClockTime year month day 
                                    hour minutes seconds 
                                    (tz*3600))
    -- The only driver which seems to report the type as SqlTimeStamp seems
    -- to be the MySQL driver. MySQL (at least 4.1) uses the same format for
    -- datetime and timestamp columns.
    -- Allow SqlText to support SQLite, which reports everything as SqlText
    fromSqlValue t s 
        | t == SqlDateTime || t == SqlTimeStamp || t == SqlText = 
            f_read getDateTime s
                where getDateTime :: ReadP ClockTime
		      getDateTime = do
			(year, month, day, hour, minutes, seconds) <- 
                            readDateTime
			return (mkClockTime year month day 
                                            hour minutes seconds 
                                            currTZ)
    fromSqlValue _ _ = Nothing

    toSqlValue ct = 
        '\'' : (shows (ctYear t) . score .
                shows (ctMonth t) . score .
                shows (ctDay t) . space .
                shows (ctHour t) . colon .
                shows (ctMin t) . colon .
                shows (ctSec t)) "'"
        where t = toUTCTime ct
              score = showChar '-'
              space = showChar ' '
              colon = showChar ':'