diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,14 @@
 # Revision history for influxdb
 
+## v1.6.0 - 2018-04-14
+
+This release includes a few significant breaking changes.
+
+* Deprecate the confusing parseQueryField and re-export parseJSON instead
+* Rewrite the QueryResults instances for tuples
+* Add Timestamp instance for TimeSpec (#59)
+* Extend haddock comments
+
 ## v1.5.2 - 2018-04-11
 
 * Export parseResultsWithDecoder, Decoder, lenientDecoder and strictDecoder from Database.InfluxDB
diff --git a/influxdb.cabal b/influxdb.cabal
--- a/influxdb.cabal
+++ b/influxdb.cabal
@@ -1,5 +1,5 @@
 name: influxdb
-version: 1.5.2
+version: 1.6.0
 synopsis: Haskell client library for InfluxDB
 description:
   @influxdb@ is a Haskell client library for InfluxDB.
@@ -13,7 +13,7 @@
 copyright: Copyright (C) 2014-2018 Mitsutoshi Aoe
 category: Database
 build-type: Custom
-cabal-version: >= 1.24
+cabal-version: 1.24
 tested-with:
   GHC == 7.10.3
   GHC == 8.0.2
@@ -85,6 +85,7 @@
     , network >= 2.6 && < 2.7
     , optional-args >= 1.0 && < 1.1
     , scientific >= 0.3.3 && < 0.4
+    , tagged >= 0.1 && < 0.9
     , text < 1.3
     , time >= 1.5 && < 1.10
     , unordered-containers < 0.3
diff --git a/src/Database/InfluxDB.hs b/src/Database/InfluxDB.hs
--- a/src/Database/InfluxDB.hs
+++ b/src/Database/InfluxDB.hs
@@ -56,10 +56,15 @@
   , strictDecoder
   , getField
   , getTag
+  , parseJSON
   , parseUTCTime
   , parsePOSIXTime
   , parseQueryField
 
+  -- *** Re-exports from tagged
+  , Tagged(..)
+  , untag
+
   -- * Database management
   , manage
 
@@ -160,10 +165,35 @@
 
 == Querying data
 
-First we define a placeholder data type called 'CPUUsage' and a 'QueryResults'
-instance. 'getField', 'parseUTCTime' and 'parseQueryField' etc are avilable to
-make JSON decoding easier.
+=== Using an one-off tuple
 
+If all the field types are an instance of 'FromJSON', we can use a tuple to store
+the results.
+
+>>> :set -XDataKinds -XOverloadedStrings
+>>> type CPUUsage = (Tagged "time" UTCTime, Tagged "idle" Double, Tagged "system" Double, Tagged "user" Double)
+>>> v <- query p $ formatQuery ("SELECT * FROM "%F.measurement) cpuUsage :: IO (V.Vector CPUUsage)
+>>> v
+[(Tagged 2017-06-17 15:41:40 UTC,Tagged 10.1,Tagged 53.3,Tagged 46.6)]
+
+Note that the type signature on query here is also necessary to type check.
+We can remove the tags using 'untag':
+
+>>> V.map (\(a, b, c, d) -> (untag a, untag b, untag c, untag d)) v :: V.Vector (UTCTime, Double, Double, Double)
+[(2017-06-17 15:41:40 UTC,10.1,53.3,46.6)]
+
+Or even using 'Data.Coerce.coerce':
+
+>>> import Data.Coerce
+>>> coerce v :: V.Vector (UTCTime, Double, Double, Double)
+[(2017-06-17 15:41:40 UTC,10.1,53.3,46.6)]
+
+=== Using a custom data type
+
+We can define our custom data type and write a 'QueryResults' instance
+instead. 'getField', 'parseUTCTime' and 'parseQueryField' etc are avilable to
+make it easier to write a JSON decoder.
+
 >>> :{
 data CPUUsage = CPUUsage
   { time :: UTCTime
@@ -172,16 +202,14 @@
 instance QueryResults CPUUsage where
   parseResults prec = parseResultsWithDecoder strictDecoder $ \_ _ columns fields -> do
     time <- getField "time" columns fields >>= parseUTCTime prec
-    FieldFloat cpuIdle <- getField "idle" columns fields >>= parseQueryField
-    FieldFloat cpuSystem <- getField "system" columns fields >>= parseQueryField
-    FieldFloat cpuUser <- getField "user" columns fields >>= parseQueryField
+    cpuIdle <- getField "idle" columns fields >>= parseJSON
+    cpuSystem <- getField "system" columns fields >>= parseJSON
+    cpuUser <- getField "user" columns fields >>= parseJSON
     return CPUUsage {..}
 :}
 
 >>> query p $ formatQuery ("SELECT * FROM "%F.measurement) cpuUsage :: IO (V.Vector CPUUsage)
 [CPUUsage {time = 2017-06-17 15:41:40 UTC, cpuIdle = 10.1, cpuSystem = 53.3, cpuUser = 46.6}]
-
-Note that the type signature on query here is also necessary to type check.
 -}
 
 {- $write
diff --git a/src/Database/InfluxDB/JSON.hs b/src/Database/InfluxDB/JSON.hs
--- a/src/Database/InfluxDB/JSON.hs
+++ b/src/Database/InfluxDB/JSON.hs
@@ -20,6 +20,7 @@
   , getTag
 
   -- * Common JSON object parsers
+  , A.parseJSON
   , parseUTCTime
   , parsePOSIXTime
   , parseRFC3339
@@ -219,3 +220,6 @@
     return FieldNull
   _ -> fail $ "parseQueryField: expected a flat data structure, but got "
     ++ show val
+{-# DEPRECATED parseQueryField
+  "This function parses numbers in a misleading way. Use 'parseJSON' instead."
+  #-}
diff --git a/src/Database/InfluxDB/Manage.hs b/src/Database/InfluxDB/Manage.hs
--- a/src/Database/InfluxDB/Manage.hs
+++ b/src/Database/InfluxDB/Manage.hs
@@ -51,7 +51,7 @@
 import qualified Network.HTTP.Client as HC
 import qualified Network.HTTP.Types as HT
 
-import Database.InfluxDB.JSON (getField, parseQueryField)
+import Database.InfluxDB.JSON (getField)
 import Database.InfluxDB.Types as Types
 import Database.InfluxDB.Query hiding (query)
 import qualified Database.InfluxDB.Format as F
@@ -148,7 +148,7 @@
 
 instance QueryResults ShowSeries where
   parseResults _ = parseResultsWith $ \_ _ columns fields -> do
-    FieldString name <- getField "key" columns fields >>= parseQueryField
+    name <- getField "key" columns fields >>= parseJSON
     return $ ShowSeries $ F.formatKey F.text name
 
 makeLensesWith
diff --git a/src/Database/InfluxDB/Query.hs b/src/Database/InfluxDB/Query.hs
--- a/src/Database/InfluxDB/Query.hs
+++ b/src/Database/InfluxDB/Query.hs
@@ -7,6 +7,7 @@
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE ViewPatterns #-}
 module Database.InfluxDB.Query
@@ -30,15 +31,22 @@
 
   -- * Low-level functions
   , withQueryResponse
+
+  -- * Re-exports from tagged
+  , Tagged(..)
+  , untag
   ) where
 import Control.Exception
 import Control.Monad
 import Data.Char
 import Data.List
+import Data.Proxy
+import GHC.TypeLits
 
 import Control.Lens
 import Data.Aeson
 import Data.Optional (Optional(..), optional)
+import Data.Tagged
 import Data.Vector (Vector)
 import Data.Void
 import qualified Control.Foldl as L
@@ -79,10 +87,9 @@
 -- instance QueryResults H2OFeet where
 --   parseResults prec = parseResultsWith $ \_ _ columns fields -> do
 --     time <- getField "time" columns fields >>= parseUTCTime prec
---     String levelDesc <- getField "level_description" columns fields
---     String location <- getField "location" columns fields
---     FieldFloat waterLevel <-
---       getField "water_level" columns fields >>= parseQueryField
+--     levelDesc <- getField "level_description" columns fields >>= parseJSON
+--     location <- getField "location" columns fields >>= parseJSON
+--     waterLevel <- getField "water_level" columns fields >>= parseJSON
 --     return H2OFeet {..}
 -- :}
 class QueryResults a where
@@ -96,85 +103,177 @@
   parseResults _ = A.withObject "error" $ \obj -> obj .:? "error"
     >>= maybe (pure V.empty) (withText "error" $ fail . T.unpack)
 
-instance (a ~ Value, b ~ Value) => QueryResults (a, b) where
-  parseResults _ = parseResultsWith $ \_ _ _ fields ->
-    maybe (fail $ "invalid fields: " ++ show fields) return $ do
-      a <- fields V.!? 0
-      b <- fields V.!? 1
-      return (a, b)
+fieldName :: KnownSymbol k => proxy k -> T.Text
+fieldName = T.pack . symbolVal
 
-instance (a ~ Value, b ~ Value, c ~ Value)
-  => QueryResults (a, b, c) where
-    parseResults _ = parseResultsWith $ \_ _ _ fields ->
-      maybe (fail $ "invalid fields: " ++ show fields) return $ do
-        a <- fields V.!? 0
-        b <- fields V.!? 1
-        c <- fields V.!? 2
-        return (a, b, c)
+-- | One-off type for non-timestamped measurements
+--
+-- >>> let p = queryParams "_internal"
+-- >>> dbs <- query p "SHOW DATABASES" :: IO (V.Vector (Tagged "name" T.Text))
+-- >>> find ((== "_internal") . untag) dbs
+-- Just (Tagged "_internal")
+instance (KnownSymbol k, FromJSON v) => QueryResults (Tagged k v) where
+  parseResults _ = parseResultsWith $ \_ _ columns fields ->
+    getField (fieldName (Proxy :: Proxy k)) columns fields >>= parseJSON
 
-instance (a ~ Value, b ~ Value, c ~ Value, d ~ Value)
-  => QueryResults (a, b, c, d) where
-    parseResults _ = parseResultsWith $ \_ _ _ fields ->
-      maybe (fail $ "invalid fields: " ++ show fields) return $ do
-        a <- fields V.!? 0
-        b <- fields V.!? 1
-        c <- fields V.!? 2
-        d <- fields V.!? 3
-        return (a, b, c, d)
+-- | One-off tuple for sigle-field measurements
+instance
+  ( KnownSymbol k1, FromJSON v1
+  , KnownSymbol k2, FromJSON v2 )
+  => QueryResults (Tagged k1 v1, Tagged k2 v2) where
+    parseResults _ = parseResultsWith $ \_ _ columns fields -> do
+      v1 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k1)) columns fields
+      v2 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k2)) columns fields
+      return (v1, v2)
 
-instance (a ~ Value, b ~ Value, c ~ Value, d ~ Value, e ~ Value)
-  => QueryResults (a, b, c, d, e) where
-    parseResults _ = parseResultsWith $ \_ _ _ fields ->
-      maybe (fail $ "invalid fields: " ++ show fields) return $ do
-        a <- fields V.!? 0
-        b <- fields V.!? 1
-        c <- fields V.!? 2
-        d <- fields V.!? 3
-        e <- fields V.!? 4
-        return (a, b, c, d, e)
+-- | One-off tuple for two-field measurements
+instance
+  ( KnownSymbol k1, FromJSON v1
+  , KnownSymbol k2, FromJSON v2
+  , KnownSymbol k3, FromJSON v3 )
+  => QueryResults (Tagged k1 v1, Tagged k2 v2, Tagged k3 v3) where
+    parseResults _ = parseResultsWith $ \_ _ columns fields -> do
+      v1 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k1)) columns fields
+      v2 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k2)) columns fields
+      v3 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k3)) columns fields
+      return (v1, v2, v3)
 
-instance (a ~ Value, b ~ Value, c ~ Value, d ~ Value, e ~ Value, f ~ Value)
-  => QueryResults (a, b, c, d, e, f) where
-    parseResults _ = parseResultsWith $ \_ _ _ fields ->
-      maybe (fail $ "invalid fields: " ++ show fields) return $ do
-        a <- fields V.!? 0
-        b <- fields V.!? 1
-        c <- fields V.!? 2
-        d <- fields V.!? 3
-        e <- fields V.!? 4
-        f <- fields V.!? 5
-        return (a, b, c, d, e, f)
+-- | One-off tuple for three-field measurements
+instance
+  ( KnownSymbol k1, FromJSON v1
+  , KnownSymbol k2, FromJSON v2
+  , KnownSymbol k3, FromJSON v3
+  , KnownSymbol k4, FromJSON v4 )
+  => QueryResults (Tagged k1 v1, Tagged k2 v2, Tagged k3 v3, Tagged k4 v4) where
+    parseResults _ = parseResultsWith $ \_ _ columns fields -> do
+      v1 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k1)) columns fields
+      v2 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k2)) columns fields
+      v3 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k3)) columns fields
+      v4 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k4)) columns fields
+      return (v1, v2, v3, v4)
 
+-- | One-off tuple for four-field measurements
 instance
-  ( a ~ Value, b ~ Value, c ~ Value, d ~ Value, e ~ Value, f ~ Value
-  , g ~ Value )
-  => QueryResults (a, b, c, d, e, f, g) where
-    parseResults _ = parseResultsWith $ \_ _ _ fields ->
-      maybe (fail $ "invalid fields: " ++ show fields) return $ do
-        a <- fields V.!? 0
-        b <- fields V.!? 1
-        c <- fields V.!? 2
-        d <- fields V.!? 3
-        e <- fields V.!? 4
-        f <- fields V.!? 5
-        g <- fields V.!? 6
-        return (a, b, c, d, e, f, g)
+  ( KnownSymbol k1, FromJSON v1
+  , KnownSymbol k2, FromJSON v2
+  , KnownSymbol k3, FromJSON v3
+  , KnownSymbol k4, FromJSON v4
+  , KnownSymbol k5, FromJSON v5 )
+  => QueryResults
+    ( Tagged k1 v1, Tagged k2 v2, Tagged k3 v3, Tagged k4 v4
+    , Tagged k5 v5
+    ) where
+    parseResults _ = parseResultsWith $ \_ _ columns fields -> do
+      v1 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k1)) columns fields
+      v2 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k2)) columns fields
+      v3 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k3)) columns fields
+      v4 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k4)) columns fields
+      v5 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k5)) columns fields
+      return (v1, v2, v3, v4, v5)
 
+-- | One-off tuple for five-field measurements
 instance
-  ( a ~ Value, b ~ Value, c ~ Value, d ~ Value, e ~ Value, f ~ Value
-  , g ~ Value, h ~ Value )
-  => QueryResults (a, b, c, d, e, f, g, h) where
-    parseResults _ = parseResultsWith $ \_ _ _ fields ->
-      maybe (fail $ "invalid fields: " ++ show fields) return $ do
-        a <- fields V.!? 0
-        b <- fields V.!? 1
-        c <- fields V.!? 2
-        d <- fields V.!? 3
-        e <- fields V.!? 4
-        f <- fields V.!? 5
-        g <- fields V.!? 6
-        h <- fields V.!? 7
-        return (a, b, c, d, e, f, g, h)
+  ( KnownSymbol k1, FromJSON v1
+  , KnownSymbol k2, FromJSON v2
+  , KnownSymbol k3, FromJSON v3
+  , KnownSymbol k4, FromJSON v4
+  , KnownSymbol k5, FromJSON v5
+  , KnownSymbol k6, FromJSON v6 )
+  => QueryResults
+    ( Tagged k1 v1, Tagged k2 v2, Tagged k3 v3, Tagged k4 v4
+    , Tagged k5 v5, Tagged k6 v6
+    ) where
+    parseResults _ = parseResultsWith $ \_ _ columns fields -> do
+      v1 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k1)) columns fields
+      v2 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k2)) columns fields
+      v3 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k3)) columns fields
+      v4 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k4)) columns fields
+      v5 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k5)) columns fields
+      v6 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k6)) columns fields
+      return (v1, v2, v3, v4, v5, v6)
+
+-- | One-off tuple for six-field measurement
+instance
+  ( KnownSymbol k1, FromJSON v1
+  , KnownSymbol k2, FromJSON v2
+  , KnownSymbol k3, FromJSON v3
+  , KnownSymbol k4, FromJSON v4
+  , KnownSymbol k5, FromJSON v5
+  , KnownSymbol k6, FromJSON v6
+  , KnownSymbol k7, FromJSON v7 )
+  => QueryResults
+    ( Tagged k1 v1, Tagged k2 v2, Tagged k3 v3, Tagged k4 v4
+    , Tagged k5 v5, Tagged k6 v6, Tagged k7 v7
+    ) where
+    parseResults _ = parseResultsWith $ \_ _ columns fields -> do
+      v1 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k1)) columns fields
+      v2 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k2)) columns fields
+      v3 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k3)) columns fields
+      v4 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k4)) columns fields
+      v5 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k5)) columns fields
+      v6 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k6)) columns fields
+      v7 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k7)) columns fields
+      return (v1, v2, v3, v4, v5, v6, v7)
+
+-- | One-off tuple for seven-field measurements
+instance
+  ( KnownSymbol k1, FromJSON v1
+  , KnownSymbol k2, FromJSON v2
+  , KnownSymbol k3, FromJSON v3
+  , KnownSymbol k4, FromJSON v4
+  , KnownSymbol k5, FromJSON v5
+  , KnownSymbol k6, FromJSON v6
+  , KnownSymbol k7, FromJSON v7
+  , KnownSymbol k8, FromJSON v8 )
+  => QueryResults
+    ( Tagged k1 v1, Tagged k2 v2, Tagged k3 v3, Tagged k4 v4
+    , Tagged k5 v5, Tagged k6 v6, Tagged k7 v7, Tagged k8 v8
+    ) where
+    parseResults _ = parseResultsWith $ \_ _ columns fields -> do
+      v1 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k1)) columns fields
+      v2 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k2)) columns fields
+      v3 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k3)) columns fields
+      v4 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k4)) columns fields
+      v5 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k5)) columns fields
+      v6 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k6)) columns fields
+      v7 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k7)) columns fields
+      v8 <- parseJSON
+        =<< getField (fieldName (Proxy :: Proxy k8)) columns fields
+      return (v1, v2, v3, v4, v5, v6, v7, v8)
 
 -- | The full set of parameters for the query API
 data QueryParams = QueryParams
diff --git a/src/Database/InfluxDB/Types.hs b/src/Database/InfluxDB/Types.hs
--- a/src/Database/InfluxDB/Types.hs
+++ b/src/Database/InfluxDB/Types.hs
@@ -8,6 +8,7 @@
 {-# LANGUAGE KindSignatures #-}
 {-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE NamedFieldPuns #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE StandaloneDeriving #-}
 {-# LANGUAGE TemplateHaskell #-}
@@ -23,6 +24,7 @@
 import Data.Time.Clock
 import Data.Time.Clock.POSIX
 import Network.HTTP.Client (Manager, ManagerSettings, Request)
+import System.Clock (TimeSpec(..))
 import qualified Data.ByteString.Lazy as BL
 import qualified Data.Text as T
 import qualified Network.HTTP.Client as HC
@@ -226,7 +228,7 @@
   scaleTo :: Precision 'WriteRequest -> time -> Int64
 
 roundAt :: RealFrac a => a -> a -> a
-roundAt scale x = fromIntegral (round (x / scale) :: Int) * scale
+roundAt scale x = fromIntegral (round (x / scale) :: Int64) * scale
 
 -- | Scale of the type precision.
 --
@@ -244,14 +246,104 @@
   Minute -> 60
   Hour ->   60 * 60
 
+-- |
+-- >>> import Data.Time.Calendar
+-- >>> let t = UTCTime (fromGregorian 2018 04 14) 123.123456789
+-- >>> t
+-- 2018-04-14 00:02:03.123456789 UTC
+-- >>> roundTo Nanosecond t
+-- 1523664123123456789
+-- >>> roundTo Microsecond t
+-- 1523664123123457000
+-- >>> roundTo Millisecond t
+-- 1523664123123000000
+-- >>> roundTo Second t
+-- 1523664123000000000
+-- >>> roundTo Minute t
+-- 1523664120000000000
+-- >>> roundTo Hour t
+-- 1523664000000000000
+-- >>> scaleTo Nanosecond t
+-- 1523664123123456789
+-- >>> scaleTo Microsecond t
+-- 1523664123123457
+-- >>> scaleTo Millisecond t
+-- 1523664123123
+-- >>> scaleTo Second t
+-- 1523664123
+-- >>> scaleTo Minute t
+-- 25394402
+-- >>> scaleTo Hour t
+-- 423240
 instance Timestamp UTCTime where
   roundTo prec = roundTo prec . utcTimeToPOSIXSeconds
   scaleTo prec = scaleTo prec . utcTimeToPOSIXSeconds
 
+-- |
+-- >>> let dt = 123.123456789 :: NominalDiffTime
+-- >>> roundTo Nanosecond dt
+-- 123123456789
+-- >>> roundTo Microsecond dt
+-- 123123457000
+-- >>> roundTo Millisecond dt
+-- 123123000000
+-- >>> roundTo Second dt
+-- 123000000000
+-- >>> roundTo Minute dt
+-- 120000000000
+-- >>> roundTo Hour dt
+-- 0
+-- >>> scaleTo Nanosecond dt
+-- 123123456789
+-- >>> scaleTo Microsecond dt
+-- 123123457
+-- >>> scaleTo Millisecond dt
+-- 123123
+-- >>> scaleTo Second dt
+-- 123
+-- >>> scaleTo Minute dt
+-- 2
+-- >>> scaleTo Hour dt
+-- 0
 instance Timestamp NominalDiffTime where
   roundTo prec time =
     round $ 10^(9 :: Int) * roundAt (precisionScale prec) time
   scaleTo prec time = round $ time / precisionScale prec
+
+-- |
+-- >>> let timespec = TimeSpec 123 123456789
+-- >>> roundTo Nanosecond timespec
+-- 123123456789
+-- >>> roundTo Microsecond timespec
+-- 123123457000
+-- >>> roundTo Millisecond timespec
+-- 123123000000
+-- >>> roundTo Second timespec
+-- 123000000000
+-- >>> roundTo Minute timespec
+-- 120000000000
+-- >>> roundTo Hour timespec
+-- 0
+-- >>> scaleTo Nanosecond timespec
+-- 123123456789
+-- >>> scaleTo Microsecond timespec
+-- 123123457
+-- >>> scaleTo Millisecond timespec
+-- 123123
+-- >>> scaleTo Second timespec
+-- 123
+-- >>> scaleTo Minute timespec
+-- 2
+-- >>> scaleTo Hour timespec
+-- 0
+instance Timestamp TimeSpec where
+  roundTo prec t =
+    round $ 10^(9 :: Int) * roundAt (precisionScale prec) (timeSpecToSeconds t)
+  scaleTo prec t = round $ timeSpecToSeconds t / precisionScale prec
+
+timeSpecToSeconds :: TimeSpec -> Double
+timeSpecToSeconds TimeSpec { sec, nsec } =
+  fromIntegral sec + fromIntegral nsec * 10^^(-9 :: Int)
 
 -- | Exceptions used in this library.
 --
