packages feed

o-clock 0.1.1 → 1.0.0

raw patch · 12 files changed

+173/−85 lines, 12 filesdep +aesondep +hashabledep +serialisesetup-changedPVP ok

version bump matches the API change (PVP)

Dependencies added: aeson, hashable, serialise, text

API changes (from Hackage documentation)

- Time.Units: instance GHC.Num.Num (Time.Units.Time unit)
- Time.Units: instance GHC.Real.Fractional (Time.Units.Time unit)
- Time.Units: instance GHC.Real.Real (Time.Units.Time rat)
- Time.Units: instance GHC.Real.RealFrac (Time.Units.Time rat)
+ Time.Units: floorRat :: forall (unit :: Rat) b. (Integral b) => Time unit -> b

Files

CHANGELOG.md view
@@ -6,7 +6,11 @@ 1.0.0 ===== -* TODO+* [#106](https://github.com/serokell/o-clock/issues/106):+  Remove `Num`, `Fractional`, `Real`, `RealFrac` instancies of `Time`.+* [#100](https://github.com/serokell/o-clock/issues/100):+  Add `Hashable`, `NFData`, `Serialise`, `ToJSON`, `FromJSON`+  instances for `Time`.  0.1.1 =====
README.lhs view
@@ -2,6 +2,7 @@  [![Hackage](https://img.shields.io/hackage/v/o-clock.svg)](https://hackage.haskell.org/package/o-clock) [![Build status](https://travis-ci.org/serokell/o-clock.svg?branch=master)](https://travis-ci.org/serokell/o-clock)+[![Stackage](http://stackage.org/package/o-clock/badge/lts)](http://stackage.org/lts/package/o-clock) [![Stackage Nightly](http://stackage.org/package/o-clock/badge/nightly)](http://stackage.org/nightly/package/o-clock) [![MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/serokell/o-clock/blob/master/LICENSE) @@ -20,7 +21,7 @@  ```haskell ignore threadDelay $ sec 5-threadDelay (5 :: Time Second)+threadDelay (Time @Second 5) threadDelay @Second 5 ``` @@ -53,8 +54,12 @@    * It means that if you want to roll out your own time units and use it in your project,      this can be done in easy and convenient way (see tutorial below). -_**Note:**_ features support for `GHC-8.2.2` is quite limited.+6. `O'Clock` contains useful instances like `Hashable`, `NFData`, `Serialise`, `ToJSON`, `FromJSON`+   but it's not included to the package by default. To do that you need to provide corresponding+   flag from this list: `hashable`, `deepseq`, `serialise` and `aeson`. +_**Note:**_ features support for `GHC-8.2.2` and `GHC-8.0.2` is quite limited.+ ## Example: How to make your own time unit  This README section contains tutorial on how you can introduce your own time units.@@ -80,7 +85,7 @@ #if ( __GLASGOW_HASKELL__ >= 804 ) import Time (type (*)) #endif-import Time ((:%), Time, Hour, UnitName,floorUnit, hour, seriesF, toUnit)+import Time ((:%), (-:-), Time, Hour, UnitName,floorUnit, hour, seriesF, toUnit)  ``` @@ -116,11 +121,10 @@ convert them to work weeks and work days and then show in human readable format.  ```haskell-calculateWork :: Time Hour  -- type synonym for 'Time HourUnit'-              -> (Time WorkWeek, Time WorkDay)+calculateWork :: Time Hour -> (Time WorkWeek, Time WorkDay) calculateWork workHours =     let completeWeeks = floorUnit $ toUnit @WorkWeek workHours-        completeDays  = floorUnit $ toUnit @WorkDay  workHours - toUnit completeWeeks+        completeDays  = floorUnit $ toUnit @WorkDay  workHours -:- toUnit completeWeeks     in (completeWeeks, completeDays)  formatHours :: Time Hour -> String@@ -135,7 +139,7 @@ ```haskell main :: IO () main = do-    putStrLn $ "The result:   " ++ formatHours 140+    putStrLn $ "The result:   " ++ formatHours (hour 140)     putStrLn $ "With seriesF: " ++ (seriesF @'[WorkWeek, WorkDay] $ hour 140) ``` 
README.md view
@@ -2,6 +2,7 @@  [![Hackage](https://img.shields.io/hackage/v/o-clock.svg)](https://hackage.haskell.org/package/o-clock) [![Build status](https://travis-ci.org/serokell/o-clock.svg?branch=master)](https://travis-ci.org/serokell/o-clock)+[![Stackage](http://stackage.org/package/o-clock/badge/lts)](http://stackage.org/lts/package/o-clock) [![Stackage Nightly](http://stackage.org/package/o-clock/badge/nightly)](http://stackage.org/nightly/package/o-clock) [![MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/serokell/o-clock/blob/master/LICENSE) @@ -20,7 +21,7 @@  ```haskell ignore threadDelay $ sec 5-threadDelay (5 :: Time Second)+threadDelay (Time @Second 5) threadDelay @Second 5 ``` @@ -53,8 +54,12 @@    * It means that if you want to roll out your own time units and use it in your project,      this can be done in easy and convenient way (see tutorial below). -_**Note:**_ features support for `GHC-8.2.2` is quite limited.+6. `O'Clock` contains useful instances like `Hashable`, `NFData`, `Serialise`, `ToJSON`, `FromJSON`+   but it's not included to the package by default. To do that you need to provide corresponding+   flag from this list: `hashable`, `deepseq`, `serialise` and `aeson`. +_**Note:**_ features support for `GHC-8.2.2` and `GHC-8.0.2` is quite limited.+ ## Example: How to make your own time unit  This README section contains tutorial on how you can introduce your own time units.@@ -80,7 +85,7 @@ #if ( __GLASGOW_HASKELL__ >= 804 ) import Time (type (*)) #endif-import Time ((:%), Time, Hour, UnitName,floorUnit, hour, seriesF, toUnit)+import Time ((:%), (-:-), Time, Hour, UnitName,floorUnit, hour, seriesF, toUnit)  ``` @@ -116,11 +121,10 @@ convert them to work weeks and work days and then show in human readable format.  ```haskell-calculateWork :: Time Hour  -- type synonym for 'Time HourUnit'-              -> (Time WorkWeek, Time WorkDay)+calculateWork :: Time Hour -> (Time WorkWeek, Time WorkDay) calculateWork workHours =     let completeWeeks = floorUnit $ toUnit @WorkWeek workHours-        completeDays  = floorUnit $ toUnit @WorkDay  workHours - toUnit completeWeeks+        completeDays  = floorUnit $ toUnit @WorkDay  workHours -:- toUnit completeWeeks     in (completeWeeks, completeDays)  formatHours :: Time Hour -> String@@ -135,7 +139,7 @@ ```haskell main :: IO () main = do-    putStrLn $ "The result:   " ++ formatHours 140+    putStrLn $ "The result:   " ++ formatHours (hour 140)     putStrLn $ "With seriesF: " ++ (seriesF @'[WorkWeek, WorkDay] $ hour 140) ``` 
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
benchmark/Main.hs view
@@ -1,5 +1,6 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} +{-# LANGUAGE CPP                        #-} {-# LANGUAGE DataKinds                  #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE KindSignatures             #-}@@ -8,18 +9,24 @@  module Main where +#ifdef NO_deepseq import Control.DeepSeq (NFData)+#endif import Gauge (bench, bgroup, defaultMain, nf, whnf) -import Time (Day, Hour, Microsecond, Nanosecond, Rat, Second, Time (..), hour, mcs, ns, sec, toUnit,-             week)+#ifdef NO_deepseq+import Time (Rat, Time)+#endif+import Time (Day, Hour, Microsecond, Nanosecond, Second, hour, mcs, ns, sec, toUnit, week)  import qualified Data.Time.Units as TU (Day, Hour, Microsecond, Nanosecond, Second, Week,                                         convertUnit) import qualified Tiempo (hours, microSeconds, toHours, toMicroSeconds)  -deriving instance NFData (Time (unit :: Rat))+#ifdef NO_deepseq+instance NFData (Time (unit :: Rat))+#endif  main :: IO () main = defaultMain
examples/Playground.hs view
@@ -2,11 +2,11 @@  module Main where -import Time (Second, Time, threadDelay)+import Time (sec, threadDelay)  main :: IO () main = do-    let twoSecs = 2 :: Time Second+    let twoSecs = sec 2     putStrLn "Hello!"     threadDelay twoSecs     putStrLn "Hello after 2 seconds"
o-clock.cabal view
@@ -1,5 +1,5 @@ name:                o-clock-version:             0.1.1+version:             1.0.0 synopsis:            Type-safe time library. description:         See README.md for details. homepage:            https://github.com/serokell/o-clock@@ -11,13 +11,14 @@ copyright:           2018 Serokell category:            Time build-type:          Simple+stability:           stable extra-doc-files:     CHANGELOG.md                    , README.md                    , README.lhs-cabal-version:       >=2.0+cabal-version:       2.0 tested-with:         GHC == 8.0.2                    , GHC == 8.2.2-                   , GHC == 8.4.1+                   , GHC == 8.4.3  source-repository head   type:     git@@ -39,6 +40,19 @@                        ScopedTypeVariables                        TypeApplications                        TypeFamilies+  if flag(aeson)+    build-depends:     aeson    >= 1.2.4+                     , text+    cpp-options:       -DHAS_aeson+  if flag(hashable)+    build-depends:     hashable >= 1.2.6+    cpp-options:       -DHAS_hashable+  if flag(deepseq)+    build-depends:     deepseq  >= 1.4+    cpp-options:       -DHAS_deepseq+  if flag(serialise)+    build-depends:     serialise >= 0.2+    cpp-options:       -DHAS_serialise  executable play-o-clock   main-is:             Playground.hs@@ -103,9 +117,30 @@   main-is:             Main.hs   build-depends:       base       >= 4.8   && < 5                      , o-clock-                     , deepseq    >= 1.4                      , gauge      >= 0.2.1 && < 1                      , tiempo     >= 0.0.1.1                      , time-units == 1.0.0+  if !flag(deepseq)+    build-depends:     deepseq    >= 1.4+    cpp-options:       -DNO_deepseq+   default-extensions:  OverloadedStrings                        RecordWildCards++-- Flags++flag aeson+  description:         Provide instances for @aeson@+  default:             False++flag hashable+  description:         Provide instances for @hashable@+  default:             False++flag deepseq+  description:         Provide instances for @deepseq@+  default:             False++flag serialise+  description:         Provide instances for @serialise@+  default:             False
src/Time/Series.hs view
@@ -25,6 +25,7 @@        ) where  import Data.Char (isDigit, isLetter)+import Data.Semigroup ((<>)) import Text.Read (readMaybe) #if ( __GLASGOW_HASKELL__ >= 804 ) import Data.Kind (Constraint)@@ -37,6 +38,7 @@ import Time.Rational (Rat) import Time.Units (Day, Fortnight, Hour, KnownRatName, Microsecond, Millisecond, Minute, Nanosecond,                    Picosecond, Second, Time (..), Week, floorUnit, toUnit)+import Time.Timestamp ((-:-))  -- $setup -- >>> import Time.Units (Time (..), fortnight, hour, minute, ms, sec)@@ -162,10 +164,15 @@ #endif                     flooredNewUnit = floorUnit newUnit                     timeStr = case flooredNewUnit of-                                   0 -> ""+                                   Time 0 -> ""                                    _ -> show flooredNewUnit-                    nextUnit = newUnit - flooredNewUnit-                in if nextUnit == 0++#if ( __GLASGOW_HASKELL__ >= 804 )+                    nextUnit = withRuntimeDivRat @unit @unit $ newUnit -:- flooredNewUnit+#else+                    nextUnit = newUnit -:- flooredNewUnit+#endif+                in if nextUnit == Time 0                    then show newUnit                    else timeStr ++ seriesF @(nextUnit ': units) @unit nextUnit @@ -245,7 +252,7 @@                       maybeT = readMaybeTime @unit $ num ++ u                   in case maybeT of                          Nothing -> seriesP @(nextUnit ': units) str-                         Just t  -> (t +) <$> (seriesP @(nextUnit ': units) nextStr)+                         Just t  -> ((t <>)) <$> (seriesP @(nextUnit ': units) nextStr)  {- | Similar to 'seriesP', but parses using all time units of the library. 
src/Time/Timestamp.hs view
@@ -23,6 +23,8 @@         ) where +import GHC.Prim (coerce)+ import Time.Rational (KnownDivRat, KnownRat, Rat, RatioNat) import Time.Units (Second, Time (..), sec, toUnit) @@ -91,7 +93,7 @@ >>> 2 *:* 3 *:* sec 5 30s ->>> 3 *:* 5 *:* 7 :: Time Second+>>> 3 *:* 5 *:* sec 7 105s  >>> ms 2000 +:+ 2 *:* sec 3@@ -131,7 +133,7 @@       => Time unitLeft       -> Time unitResult       -> Time unitResult-t1 +:+ t2 = toUnit t1 + t2+t1 +:+ t2 = coerce ((+) :: RatioNat -> RatioNat -> RatioNat) (toUnit @unitResult t1) t2 {-# INLINE (+:+) #-}  -- | Substracts time amounts of different units. When the minuend is smaller@@ -145,7 +147,7 @@       => Time unitLeft       -> Time unitResult       -> Time unitResult-t1 -:- t2 = toUnit t1 - t2+t1 -:- t2 = coerce ((-) :: RatioNat -> RatioNat -> RatioNat) (toUnit @unitResult t1) t2 {-# INLINE (-:-) #-}  {- | Compute the difference between two amounts of time. The result is returned
src/Time/Units.hs view
@@ -37,6 +37,7 @@         -- ** Creation helpers        , time        , floorUnit+       , floorRat        , toNum         , sec@@ -74,6 +75,24 @@ import Text.ParserCombinators.ReadP (ReadP, char, munch1, option, pfail, (+++)) import Text.ParserCombinators.ReadPrec (ReadPrec, lift) +#ifdef HAS_hashable+import Data.Hashable (Hashable)+#endif++#ifdef HAS_deepseq+import Control.DeepSeq (NFData)+#endif++#ifdef HAS_serialise+import Codec.Serialise (Serialise (..))+#endif++#ifdef HAS_aeson+import Data.Aeson (ToJSON (..), FromJSON (..), withText)+import Text.Read (readMaybe)+import qualified Data.Text as Text+#endif+ #if ( __GLASGOW_HASKELL__ >= 804 ) import Time.Rational (type (*), type (/)) #endif@@ -120,11 +139,11 @@  -- | Time unit is represented as type level rational multiplier with kind 'Rat'. newtype Time (rat :: Rat) = Time { unTime :: RatioNat }-    deriving (Eq, Ord, Enum, Real, RealFrac, Generic)+    deriving (Eq, Ord, Enum, Generic)  -- | Addition is associative binary operation for 'Semigroup' of 'Time'. instance Semigroup (Time (rat :: Rat)) where-    (<>) = (+)+    (<>) = coerce ((+) :: RatioNat -> RatioNat -> RatioNat)     {-# INLINE (<>) #-}     sconcat = foldl' (<>) mempty     {-# INLINE sconcat #-}@@ -139,6 +158,29 @@     mconcat = foldl' (<>) mempty     {-# INLINE mconcat #-} +#ifdef HAS_hashable+instance Hashable (Time (rat :: Rat))+#endif++#ifdef HAS_deepseq+instance NFData (Time (rat :: Rat))+#endif++#ifdef HAS_serialise+instance Serialise (Time (rat :: Rat))+#endif++#ifdef HAS_aeson+instance (KnownUnitName unit) => ToJSON (Time (unit :: Rat)) where+    toJSON = toJSON . show++instance (KnownUnitName unit) => FromJSON (Time (unit :: Rat)) where+    parseJSON = withText "time" $ maybe parseFail pure . maybeTime+      where+        parseFail = fail $ "Can not parse Time. Expected unit: " ++ unitNameVal @unit+        maybeTime = readMaybe @(Time unit) . Text.unpack+#endif+ -- | Type family for prettier 'show' of time units. type family UnitName (unit :: Rat) :: Symbol @@ -206,28 +248,6 @@             unless (timeUnitStr == unitNameVal @unit) pfail             pure $ Time (n % d) --- | Has the same behavior as derived instance, but '*' operator--- throws the runtime error with 'error'.-instance Num (Time unit) where-    (+) = coerce ((+) :: RatioNat -> RatioNat -> RatioNat)-    {-# INLINE (+) #-}-    (-) = coerce ((-) :: RatioNat -> RatioNat -> RatioNat)-    {-# INLINE (-) #-}-    (*) = error "It's not possible to multiply time"-    abs = id-    {-# INLINE abs #-}-    signum = coerce (signum :: RatioNat -> RatioNat)-    {-# INLINE signum #-}-    fromInteger = coerce (fromInteger :: Integer -> RatioNat)-    {-# INLINE fromInteger #-}---- | Has the same behavior as derived instance, but '/' operator--- throws the runtime error with 'error'.-instance Fractional (Time unit) where-    fromRational = coerce (fromRational :: Rational -> RatioNat)-    {-# INLINE fromRational #-}-    (/) = error "It's not possible to divide time"- ---------------------------------------------------------------------------- -- Creation helpers ----------------------------------------------------------------------------@@ -317,6 +337,10 @@ fortnight = time {-# INLINE fortnight #-} +-- | Returns the greatest integer not greater than given 'Time'.+floorRat :: forall (unit :: Rat) b . (Integral b) => Time unit -> b+floorRat = floor . unTime+ {- | Similar to 'floor', but works with 'Time' units.  >>> floorUnit @Day (Time $ 5 % 2)@@ -330,7 +354,7 @@  -} floorUnit :: forall (unit :: Rat) . Time unit -> Time unit-floorUnit = time . fromIntegral @Natural . floor+floorUnit = time . fromIntegral @Natural . floorRat  {- | Convert time to the 'Num' in given units. @@ -361,7 +385,7 @@ -} toNum :: forall (unitTo :: Rat) n (unit :: Rat) . (KnownDivRat unit unitTo, Num n)       => Time unit -> n-toNum = fromIntegral @Natural . floor . toUnit @unitTo+toNum = fromIntegral @Natural . floorRat . toUnit @unitTo  ---------------------------------------------------------------------------- -- Functional@@ -369,7 +393,7 @@  {- | Converts from one time unit to another time unit. ->>> toUnit @Hour (120 :: Time Minute)+>>> toUnit @Hour (minute 120) 2h  >>> toUnit @Second (ms 7)@@ -378,7 +402,7 @@ >>> toUnit @Week (Time @Day 45) 6+3/7w ->>> toUnit @Second @Minute 3+>>> toUnit @Second @Minute (Time 3) 180s  >>> toUnit (day 42000000) :: Time Second@@ -409,7 +433,7 @@ threadDelay :: forall (unit :: Rat) m . (KnownDivRat unit Microsecond, MonadIO m)             => Time unit             -> m ()-threadDelay = liftIO . Concurrent.threadDelay . floor . toUnit @Microsecond+threadDelay = liftIO . Concurrent.threadDelay . floorRat . toUnit @Microsecond {-# INLINE threadDelay #-}  -- | Similar to 'CPUTime.getCPUTime' but returns the CPU time used by the current@@ -450,5 +474,5 @@         => Time unit   -- ^ time         -> IO a        -- ^ 'IO' action         -> m (Maybe a) -- ^ returns 'Nothing' if no result is available within the given time-timeout t = liftIO . Timeout.timeout (floor $ toUnit @Microsecond t)+timeout t = liftIO . Timeout.timeout (floorRat $ toUnit @Microsecond t) {-# INLINE timeout #-}
test/Test/Time/Timestamp.hs view
@@ -11,7 +11,7 @@ import Test.Tasty (TestTree) import Test.Tasty.Hspec (Spec, anyException, describe, it, shouldBe, shouldThrow, testSpec) -import Time (Hour, Microsecond, Minute, Picosecond, Second, Timestamp (..), Week, timeAdd, timeDiff,+import Time (Minute, Second, Time (..), Timestamp (..), Week, hour, ms, ps, sec, timeAdd, timeDiff,              timeDiv, timeMul)  timeStampTestTree :: IO TestTree@@ -22,18 +22,18 @@ spec_Timestamp = do     describe "TimeDiff" $ do         it "1 is less than 5, diff is 4 seconds" $-            timeDiff @Second (Timestamp 1) (Timestamp 5) `shouldBe` (LT, 4)+            timeDiff @Second (Timestamp 1) (Timestamp 5) `shouldBe` (LT, Time 4)         it "100 is greater that 40, diff is 60 sec == 1 min" $-            timeDiff @Minute (Timestamp 100) (Timestamp 40) `shouldBe` (GT, 1)+            timeDiff @Minute (Timestamp 100) (Timestamp 40) `shouldBe` (GT, Time 1)         it "42 is equal to 42, diff is 0 Weeks" $-            timeDiff @Week (Timestamp 42) (Timestamp 42) `shouldBe` (EQ, 0)+            timeDiff @Week (Timestamp 42) (Timestamp 42) `shouldBe` (EQ, Time 0)         it "3 hours offset 7 is 10"  $-            timeAdd @Hour 3 (Timestamp 7) `shouldBe` (Timestamp 10807)+            timeAdd (hour 3) (Timestamp 7) `shouldBe` (Timestamp 10807)         it "twice 21 mcs is 42 mcs"  $-            timeMul @Microsecond 2 21 `shouldBe` 42+            timeMul 2 (ms 21) `shouldBe` ms 42         it "zero x 42 s is zero"  $-            timeMul @Second 0 42 `shouldBe` 0+            timeMul 0 (sec 42) `shouldBe` sec 0         it "84 picoseconds divide by 2 is 42"  $-            timeDiv @Picosecond 84 2 `shouldBe` 42+            timeDiv (ps 84) (ps 2) `shouldBe` 42         it "fails when trying to divide by zero"  $-            evaluate (timeDiv @Second 42 0) `shouldThrow` anyException+            evaluate (timeDiv (sec 42) $ sec 0) `shouldThrow` anyException
test/Test/Time/Units.hs view
@@ -12,9 +12,8 @@ import Test.Tasty (TestTree) import Test.Tasty.Hspec (Spec, anyException, describe, it, shouldBe, shouldThrow, testSpec) -import Time (Day, Hour, Microsecond, Millisecond, Minute, Picosecond, Second, Time (..), Week, day,-             floorUnit, fortnight, hour, mcs, minute, ms, ns, ps, sec, seriesF, toUnit, unitsF,-             week, (+:+))+import Time (Day, Hour, Millisecond, Minute, Second, Time (..), Week, day, floorUnit, fortnight,+             hour, mcs, minute, ms, ns, ps, sec, seriesF, toUnit, unitsF, week, (+:+))  unitsTestTree :: IO TestTree unitsTestTree = testSpec "Units" spec_Units@@ -23,22 +22,22 @@ spec_Units = do     describe "Unit Conversion Test" $ do         it "11 seconds is 11000 milliseconds" $-            toUnit @Millisecond (sec 11) `shouldBe` 11000+            toUnit (sec 11) `shouldBe` ms 11000         it "5000 milliseconds is 5 seconds" $-            toUnit @Second (ms 5000) `shouldBe` 5+            toUnit (ms 5000) `shouldBe` sec 5         it "3 seconds is 3000000 microseconds" $-            toUnit @Microsecond (sec 3) `shouldBe` 3000000+            toUnit (sec 3) `shouldBe` mcs 3000000         it "3 microseconds is 3/1000000 seconds" $             toUnit @Second (mcs 3) `shouldBe` Time (3 :% 1000000)         it "7 days is 1 week" $-            toUnit @Week (day 7) `shouldBe` 1+            toUnit (day 7) `shouldBe` week 1         it "2 fornights is 28 days" $-            toUnit @Day (fortnight 2) `shouldBe` 28+            toUnit (fortnight 2) `shouldBe` day 28         it "1 nanosecond is 1000 picoseconds" $-            toUnit @Picosecond (ns 1) `shouldBe` 1000+            toUnit (ns 1) `shouldBe` ps 1000     describe "Read Time Test" $ do         it "parses '42s' as 42 seconds" $-            read @(Time Second) "42s" `shouldBe` 42+            read "42s" `shouldBe` sec 42         it "fails when '42mm' is expected as seconds" $             evaluate (read @(Time Second) "42mm") `shouldThrow` anyException         it "parses '7/2s' as 7/2 seconds" $@@ -54,20 +53,20 @@         it "fails when '/3s' is expected as seconds" $             evaluate (read @(Time Second) "/3s") `shouldThrow` anyException         it "parses '14/2h' as 7 hours" $-            read @(Time Hour) "14/2h" `shouldBe` 7+            read "14/2h" `shouldBe` hour 7         it "fails when '14/2h' expected as 7 seconds" $             evaluate (read @(Time Second) "14/2h") `shouldThrow` anyException         it "parses big number to big number" $-            read @(Time Microsecond) ('1' : replicate 20 '0' ++ "mcs") `shouldBe` 100000000000000000000+            read ('1' : replicate 20 '0' ++ "mcs") `shouldBe` mcs (10 ^ (20 :: Int))         it "fails when '4ms' expected as 4 seconds" $             evaluate (read @(Time Second) "4ms") `shouldThrow` anyException     describe "Floor tests" $ do         it "returns 0s when floor < 1 second" $-            floorUnit (Time @Second $ 2 :% 3) `shouldBe` 0+            floorUnit (Time @Second $ 2 :% 3) `shouldBe` sec 0         it "returns 2d when floor 2.5 days" $-            floorUnit @Day (Time $ 5 :% 2) `shouldBe` 2+            floorUnit (Time $ 5 :% 2) `shouldBe` day 2         it "returns 42ps when floor integer" $-            floorUnit (ps 42) `shouldBe` 42+            floorUnit (ps 42) `shouldBe` ps 42     describe "Formatting tests" $ do         it "4000 minutes should be formatted without ending-zeros" $             seriesF @'[Day, Hour, Minute, Second] (minute 4000) `shouldBe` "2d18h40m"