{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
module Airship.Internal.Date
( parseRfc1123Date
, utcTimeToRfc1123) where
#if __GLASGOW_HASKELL__ < 710
import Control.Applicative ((<$>))
#endif
import Data.ByteString.Char8 ()
import Data.ByteString.Internal
import Data.Time.Calendar (fromGregorian, toGregorian)
import Data.Time.Calendar.WeekDate (toWeekDate)
import Data.Time.Clock (UTCTime (..), secondsToDiffTime)
import Data.Word
import Foreign.ForeignPtr
import Foreign.Ptr
import Foreign.Storable
import qualified Network.HTTP.Date as HD
httpDateToUtc :: HD.HTTPDate -> UTCTime
httpDateToUtc h = UTCTime days diffTime
where days = fromGregorian (fromIntegral $ HD.hdYear h) (HD.hdMonth h) (HD.hdDay h)
diffTime = secondsToDiffTime seconds
seconds = fromIntegral $ hourS + minS + HD.hdSecond h
hourS = HD.hdHour h * 60 * 60
minS = HD.hdMinute h * 60
parseRfc1123Date :: ByteString -> Maybe UTCTime
parseRfc1123Date b = httpDateToUtc <$> HD.parseHTTPDate b
utcTimeToRfc1123 :: UTCTime -> ByteString
utcTimeToRfc1123 (UTCTime day offset) =
unsafeCreate 29 $ \ptr -> do
cpy3 ptr weekDays (3 * w)
poke (ptr `plusPtr` 3) comma
poke (ptr `plusPtr` 4) spc
int2 (ptr `plusPtr` 5) d
poke (ptr `plusPtr` 7) spc
cpy3 (ptr `plusPtr` 8) months (3 * m)
poke (ptr `plusPtr` 11) spc
int4 (ptr `plusPtr` 12) y
poke (ptr `plusPtr` 16) spc
int2 (ptr `plusPtr` 17) h
poke (ptr `plusPtr` 19) colon
int2 (ptr `plusPtr` 20) n
poke (ptr `plusPtr` 22) colon
int2 (ptr `plusPtr` 23) s
poke (ptr `plusPtr` 25) spc
poke (ptr `plusPtr` 26) (71 :: Word8)
poke (ptr `plusPtr` 27) (77 :: Word8)
poke (ptr `plusPtr` 28) (84 :: Word8)
where
y = fromIntegral y'
offset' = round offset
h = offset' `mod` 3600
n = offset' `mod` 60
s = offset' - (h * 3600) - (n * 60)
(y', m, d) = toGregorian day
(_, _, w) = toWeekDate day
cpy3 :: Ptr Word8 -> ForeignPtr Word8 -> Int -> IO ()
cpy3 ptr p o = withForeignPtr p $ \fp ->
memcpy ptr (fp `plusPtr` o) 3
int2 :: Ptr Word8 -> Int -> IO ()
int2 ptr n
| n < 10 = do
poke ptr zero
poke (ptr `plusPtr` 1) (i2w8 n)
| otherwise = do
poke ptr (i2w8 (n `quot` 10))
poke (ptr `plusPtr` 1) (i2w8 (n `rem` 10))
int4 :: Ptr Word8 -> Int -> IO ()
int4 ptr n0 = do
let (n1,x1) = n0 `quotRem` 10
(n2,x2) = n1 `quotRem` 10
(x4,x3) = n2 `quotRem` 10
poke ptr (i2w8 x4)
poke (ptr `plusPtr` 1) (i2w8 x3)
poke (ptr `plusPtr` 2) (i2w8 x2)
poke (ptr `plusPtr` 3) (i2w8 x1)
i2w8 :: Int -> Word8
i2w8 n = fromIntegral n + zero
months :: ForeignPtr Word8
months = let (PS p _ _) = "___JanFebMarAprMayJunJulAugSepOctNovDec" in p
weekDays :: ForeignPtr Word8
weekDays = let (PS p _ _) = "___MonTueWedThuFriSatSun" in p
spc :: Word8
spc = 32
comma :: Word8
comma = 44
colon :: Word8
colon = 58
zero :: Word8
zero = 48