diff options
| author | Gregor Kleen <aethoago@141.li> | 2017-02-15 18:17:26 +0100 |
|---|---|---|
| committer | Gregor Kleen <aethoago@141.li> | 2017-02-15 18:17:26 +0100 |
| commit | 5b09b096e38ed231b62df57736e87c989b481b5d (patch) | |
| tree | 3319c041140f1cb6e052cc44bd79f3868180e3e9 | |
| parent | ee09f262f9b8c7c6a4042071cdfff3e22adbef86 (diff) | |
| download | postdelay-5b09b096e38ed231b62df57736e87c989b481b5d.tar postdelay-5b09b096e38ed231b62df57736e87c989b481b5d.tar.gz postdelay-5b09b096e38ed231b62df57736e87c989b481b5d.tar.bz2 postdelay-5b09b096e38ed231b62df57736e87c989b481b5d.tar.xz postdelay-5b09b096e38ed231b62df57736e87c989b481b5d.zip | |
Purely relative time specifications
| -rw-r--r-- | lib/Postdelay/TimeSpec.hs | 28 | ||||
| -rw-r--r-- | lib/Postdelay/TimeSpec/Units.hs | 42 | ||||
| -rw-r--r-- | lib/Postdelay/TimeSpec/Utils.hs | 8 | ||||
| -rw-r--r-- | postdelay.cabal | 2 | ||||
| -rw-r--r-- | postdelay.nix | 10 |
5 files changed, 64 insertions, 26 deletions
diff --git a/lib/Postdelay/TimeSpec.hs b/lib/Postdelay/TimeSpec.hs index 5c41180..03baf85 100644 --- a/lib/Postdelay/TimeSpec.hs +++ b/lib/Postdelay/TimeSpec.hs | |||
| @@ -14,16 +14,38 @@ import Text.Megaparsec | |||
| 14 | 14 | ||
| 15 | import Control.Monad.IO.Class | 15 | import Control.Monad.IO.Class |
| 16 | import Control.Applicative | 16 | import Control.Applicative |
| 17 | import Control.Lens hiding ((#)) | ||
| 17 | 18 | ||
| 18 | import Data.Semigroup | 19 | import Data.Bool |
| 20 | import Data.Semigroup hiding (option) | ||
| 19 | import Data.Monoid (Endo(..)) | 21 | import Data.Monoid (Endo(..)) |
| 22 | import Data.Foldable | ||
| 23 | import Data.VectorSpace | ||
| 20 | 24 | ||
| 21 | import Data.Time | 25 | import Data.Time |
| 26 | import Data.Time.Lens | ||
| 22 | import Data.Time.Zones | 27 | import Data.Time.Zones |
| 23 | 28 | ||
| 24 | 29 | ||
| 30 | seconds' :: Timeable t => Lens' t Time | ||
| 31 | seconds' = seconds . iso (% Second) (# Second) | ||
| 32 | |||
| 33 | |||
| 25 | pTimeSpec :: StringParser s m => m (Endo LocalTime) | 34 | pTimeSpec :: StringParser s m => m (Endo LocalTime) |
| 26 | pTimeSpec = empty | 35 | pTimeSpec = label "Relative time specification" $ |
| 36 | choice [ pOffsets False | ||
| 37 | ] | ||
| 38 | |||
| 39 | pOffsets :: forall s m. StringParser s m | ||
| 40 | => Bool -- ^ Require sign on first offset? | ||
| 41 | -> m (Endo LocalTime) | ||
| 42 | pOffsets (bool optSigned signed -> reqSgn) = fmap fold $ (:) <$> offset reqSgn <*> many (offset optSigned) | ||
| 43 | where | ||
| 44 | asOffset :: Time -> Endo LocalTime | ||
| 45 | asOffset by = Endo $ flexDT.seconds' %~ (^+^ by) | ||
| 46 | offset :: (m Time -> m Time) -> m (Endo LocalTime) | ||
| 47 | offset sgn = asOffset <$> lexeme (sgn timeLength) <?> "Time offset" | ||
| 27 | 48 | ||
| 28 | pTimeZone :: (StringParser s m, MonadIO m) => m (Either TimeZone TZ) | 49 | pTimeZone :: (StringParser s m, MonadIO m) => m (Either TimeZone TZ) |
| 29 | pTimeZone = empty | 50 | pTimeZone = label "Timezone" $ |
| 51 | empty | ||
diff --git a/lib/Postdelay/TimeSpec/Units.hs b/lib/Postdelay/TimeSpec/Units.hs index 330997a..0062460 100644 --- a/lib/Postdelay/TimeSpec/Units.hs +++ b/lib/Postdelay/TimeSpec/Units.hs | |||
| @@ -1,19 +1,21 @@ | |||
| 1 | {-# LANGUAGE TemplateHaskell, TypeFamilies, GADTs, RankNTypes, DataKinds, ViewPatterns, ScopedTypeVariables #-} | 1 | {-# LANGUAGE TemplateHaskell, GADTs, RankNTypes, DataKinds, ViewPatterns, ScopedTypeVariables, FlexibleContexts, FlexibleInstances, UndecidableInstances, TypeFamilies #-} |
| 2 | 2 | ||
| 3 | module Postdelay.TimeSpec.Units | 3 | module Postdelay.TimeSpec.Units |
| 4 | ( Time | 4 | ( Time |
| 5 | 5 | ||
| 6 | , Second, Minute, Hour | 6 | , Second(..), Minute(..), Hour(..) |
| 7 | , Day, Week, Month, Year | 7 | , Day(..), Week(..), Month(..), Year(..) |
| 8 | 8 | ||
| 9 | , timeLength | 9 | , timeLength |
| 10 | 10 | ||
| 11 | , module Data.Metrology | ||
| 11 | , module Data.Units.SI.Prefixes | 12 | , module Data.Units.SI.Prefixes |
| 12 | ) where | 13 | ) where |
| 13 | 14 | ||
| 14 | import Postdelay.TimeSpec.Utils | 15 | import Postdelay.TimeSpec.Utils |
| 15 | 16 | ||
| 16 | import Control.Applicative | 17 | import Control.Applicative |
| 18 | import Control.Monad | ||
| 17 | 19 | ||
| 18 | import Data.Metrology | 20 | import Data.Metrology |
| 19 | import Data.Metrology.TH | 21 | import Data.Metrology.TH |
| @@ -42,9 +44,6 @@ declareDerivedUnit "Year" [t| Day |] 365.25 Nothing | |||
| 42 | 44 | ||
| 43 | type Time = MkQu_DLN D.Time DefaultLCSU Fixed.Pico | 45 | type Time = MkQu_DLN D.Time DefaultLCSU Fixed.Pico |
| 44 | 46 | ||
| 45 | data Prefix where | ||
| 46 | Prefix :: forall p. (UnitPrefix p, Show p) => p -> Prefix | ||
| 47 | |||
| 48 | instance HasResolution p => AdditiveGroup (Fixed p) where | 47 | instance HasResolution p => AdditiveGroup (Fixed p) where |
| 49 | zeroV = 0 | 48 | zeroV = 0 |
| 50 | (^+^) = (+) | 49 | (^+^) = (+) |
| @@ -57,16 +56,16 @@ instance HasResolution p => VectorSpace (Fixed p) where | |||
| 57 | 56 | ||
| 58 | 57 | ||
| 59 | timeLength :: StringParser s m => m Time | 58 | timeLength :: StringParser s m => m Time |
| 60 | timeLength = (*^) <$> lexeme rational <*> timeUnit | 59 | timeLength = (*^) <$> lexeme rational <*> timeUnit <?> "Length of time" |
| 61 | 60 | ||
| 62 | rational :: forall s m n. (StringParser s m, Fractional n, Num n) => m n | 61 | rational :: forall s m n. (StringParser s m, Fractional n, Num n) => m n |
| 63 | rational = combine <$> some digitChar <*> option [] (char '.' *> many digitChar) | 62 | rational = combine <$> some digitChar <*> option [] (char '.' *> many digitChar) <?> "Decimal number" |
| 64 | where | 63 | where |
| 65 | combine :: [Char] -> [Char] -> n | 64 | combine :: [Char] -> [Char] -> n |
| 66 | combine (map asN -> whole) (map asN -> fractional) | 65 | combine (map asN -> whole) (map asN -> fractional) |
| 67 | = foldl' (\i l -> i * 10 + l) 0 whole + (foldr' (\h t -> h + t / 10) 0 fractional) / 10 | 66 | = foldl' (\i l -> i * 10 + l) 0 whole + (foldr' (\h t -> h + t / 10) 0 fractional) / 10 |
| 68 | asN :: Char -> n | 67 | asN :: Char -> n |
| 69 | asN c = fromIntegral $ ((-) `on` fromEnum) c '0' | 68 | asN c = fromIntegral $ fromEnum c - fromEnum '0' |
| 70 | 69 | ||
| 71 | timeUnit :: StringParser s m => m Time | 70 | timeUnit :: StringParser s m => m Time |
| 72 | timeUnit = label "Unit of time" . choice $ | 71 | timeUnit = label "Unit of time" . choice $ |
| @@ -95,15 +94,28 @@ timeUnit = label "Unit of time" . choice $ | |||
| 95 | , 1 % Year <$ choice [ string' "years" | 94 | , 1 % Year <$ choice [ string' "years" |
| 96 | , string' "year" | 95 | , string' "year" |
| 97 | ] | 96 | ] |
| 98 | ] ++ | 97 | , option 1 siPrefix <**> choice [ (% Second) <$ string "s" |
| 99 | [ (% Second) <$> option 1 siPrefix <* string "s" | 98 | , (% Hour ) <$ string "h" |
| 100 | , (% Hour) <$> option 1 siPrefix <* string "h" | 99 | , (% Day ) <$ string "d" |
| 101 | , (% Day) <$> option 1 siPrefix <* string "d" | 100 | , (% Year ) <$ choice [ string "a", string' "yr", string' "yrs" ] |
| 102 | , (% Year) <$> option 1 siPrefix <* choice [ string "a", string "yr", string "yrs" ] | 101 | ] |
| 103 | ] | 102 | ] |
| 104 | 103 | ||
| 104 | |||
| 105 | data Prefix where | ||
| 106 | Prefix :: forall p. ParseablePrefix p => p -> Prefix | ||
| 107 | |||
| 108 | class UnitPrefix a => ParseablePrefix a where | ||
| 109 | parser :: StringParser s m => a -> m () | ||
| 110 | |||
| 111 | instance {-# OVERLAPPABLE #-} (UnitPrefix a, Show a) => ParseablePrefix a where | ||
| 112 | parser = void . string . show | ||
| 113 | |||
| 114 | instance ParseablePrefix Micro where | ||
| 115 | parser _ = void $ choice [ string "ยต", string "u" ] | ||
| 116 | |||
| 105 | siPrefix :: (StringParser s m, Fractional n) => m n | 117 | siPrefix :: (StringParser s m, Fractional n) => m n |
| 106 | siPrefix = label "SI prefix" . choice $ map (\(Prefix p) -> multiplier p <$ string (show p)) | 118 | siPrefix = label "SI prefix" . choice $ map (\(Prefix p) -> multiplier p <$ parser p) |
| 107 | [ Prefix Deca, Prefix Hecto, Prefix Kilo, Prefix Mega, Prefix Giga | 119 | [ Prefix Deca, Prefix Hecto, Prefix Kilo, Prefix Mega, Prefix Giga |
| 108 | , Prefix Tera, Prefix Peta, Prefix Exa, Prefix Zetta, Prefix Yotta | 120 | , Prefix Tera, Prefix Peta, Prefix Exa, Prefix Zetta, Prefix Yotta |
| 109 | , Prefix Deci, Prefix Centi, Prefix Milli, Prefix Micro, Prefix Nano | 121 | , Prefix Deci, Prefix Centi, Prefix Milli, Prefix Micro, Prefix Nano |
diff --git a/lib/Postdelay/TimeSpec/Utils.hs b/lib/Postdelay/TimeSpec/Utils.hs index e4ba732..85ac299 100644 --- a/lib/Postdelay/TimeSpec/Utils.hs +++ b/lib/Postdelay/TimeSpec/Utils.hs | |||
| @@ -7,8 +7,10 @@ import Control.Monad | |||
| 7 | import Control.Lens | 7 | import Control.Lens |
| 8 | 8 | ||
| 9 | import Data.Time | 9 | import Data.Time |
| 10 | import Data.Time.Lens | ||
| 10 | import Data.Time.Zones | 11 | import Data.Time.Zones |
| 11 | 12 | ||
| 13 | import Data.Functor | ||
| 12 | import Data.AdditiveGroup | 14 | import Data.AdditiveGroup |
| 13 | 15 | ||
| 14 | import Text.Megaparsec | 16 | import Text.Megaparsec |
| @@ -30,6 +32,6 @@ signed = (<*>) (lexeme sign) | |||
| 30 | optSigned = (<*>) (option id $ lexeme sign) | 32 | optSigned = (<*>) (option id $ lexeme sign) |
| 31 | 33 | ||
| 32 | sign :: (StringParser s m, AdditiveGroup n) => m (n -> n) | 34 | sign :: (StringParser s m, AdditiveGroup n) => m (n -> n) |
| 33 | sign = choice [ id <$ char '+' | 35 | sign = label "sign" $ choice [ char '+' $> id |
| 34 | , negateV <$ char '-' | 36 | , char '-' $> negateV |
| 35 | ] | 37 | ] |
diff --git a/postdelay.cabal b/postdelay.cabal index 2e66ace..6d4323a 100644 --- a/postdelay.cabal +++ b/postdelay.cabal | |||
| @@ -36,6 +36,8 @@ library | |||
| 36 | , units >=2.4 && <3 | 36 | , units >=2.4 && <3 |
| 37 | , units-defs >=2.0 && <3 | 37 | , units-defs >=2.0 && <3 |
| 38 | , exceptions >=0.8 && <1 | 38 | , exceptions >=0.8 && <1 |
| 39 | , lens >=4.15 && <5 | ||
| 40 | , lens-datetime >=0.3 && <1 | ||
| 39 | hs-source-dirs: lib | 41 | hs-source-dirs: lib |
| 40 | default-language: Haskell2010 | 42 | default-language: Haskell2010 |
| 41 | 43 | ||
diff --git a/postdelay.nix b/postdelay.nix index 3bd6184..938557f 100644 --- a/postdelay.nix +++ b/postdelay.nix | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | { mkDerivation, base, case-insensitive, exceptions, hsemail, list-t | 1 | { mkDerivation, base, case-insensitive, exceptions, hsemail, lens |
| 2 | , megaparsec, mime, mtl, old-time, parsec, stdenv, time | 2 | , lens-datetime, list-t, megaparsec, mime, mtl, old-time, parsec |
| 3 | , transformers, tz, units, units-defs | 3 | , stdenv, time, transformers, tz, units, units-defs |
| 4 | }: | 4 | }: |
| 5 | mkDerivation { | 5 | mkDerivation { |
| 6 | pname = "postdelay"; | 6 | pname = "postdelay"; |
| @@ -9,8 +9,8 @@ mkDerivation { | |||
| 9 | isLibrary = true; | 9 | isLibrary = true; |
| 10 | isExecutable = true; | 10 | isExecutable = true; |
| 11 | libraryHaskellDepends = [ | 11 | libraryHaskellDepends = [ |
| 12 | base case-insensitive exceptions hsemail list-t megaparsec mime mtl | 12 | base case-insensitive exceptions hsemail lens lens-datetime list-t |
| 13 | old-time parsec time tz units units-defs | 13 | megaparsec mime mtl old-time parsec time tz units units-defs |
| 14 | ]; | 14 | ]; |
| 15 | executableHaskellDepends = [ base transformers ]; | 15 | executableHaskellDepends = [ base transformers ]; |
| 16 | homepage = "https://git.yggdrasil.li/gkleen/pub/postdelay"; | 16 | homepage = "https://git.yggdrasil.li/gkleen/pub/postdelay"; |
