summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregor Kleen <aethoago@141.li>2017-02-15 18:17:26 +0100
committerGregor Kleen <aethoago@141.li>2017-02-15 18:17:26 +0100
commit5b09b096e38ed231b62df57736e87c989b481b5d (patch)
tree3319c041140f1cb6e052cc44bd79f3868180e3e9
parentee09f262f9b8c7c6a4042071cdfff3e22adbef86 (diff)
downloadpostdelay-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.hs28
-rw-r--r--lib/Postdelay/TimeSpec/Units.hs42
-rw-r--r--lib/Postdelay/TimeSpec/Utils.hs8
-rw-r--r--postdelay.cabal2
-rw-r--r--postdelay.nix10
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
15import Control.Monad.IO.Class 15import Control.Monad.IO.Class
16import Control.Applicative 16import Control.Applicative
17import Control.Lens hiding ((#))
17 18
18import Data.Semigroup 19import Data.Bool
20import Data.Semigroup hiding (option)
19import Data.Monoid (Endo(..)) 21import Data.Monoid (Endo(..))
22import Data.Foldable
23import Data.VectorSpace
20 24
21import Data.Time 25import Data.Time
26import Data.Time.Lens
22import Data.Time.Zones 27import Data.Time.Zones
23 28
24 29
30seconds' :: Timeable t => Lens' t Time
31seconds' = seconds . iso (% Second) (# Second)
32
33
25pTimeSpec :: StringParser s m => m (Endo LocalTime) 34pTimeSpec :: StringParser s m => m (Endo LocalTime)
26pTimeSpec = empty 35pTimeSpec = label "Relative time specification" $
36 choice [ pOffsets False
37 ]
38
39pOffsets :: forall s m. StringParser s m
40 => Bool -- ^ Require sign on first offset?
41 -> m (Endo LocalTime)
42pOffsets (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
28pTimeZone :: (StringParser s m, MonadIO m) => m (Either TimeZone TZ) 49pTimeZone :: (StringParser s m, MonadIO m) => m (Either TimeZone TZ)
29pTimeZone = empty 50pTimeZone = 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
3module Postdelay.TimeSpec.Units 3module 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
14import Postdelay.TimeSpec.Utils 15import Postdelay.TimeSpec.Utils
15 16
16import Control.Applicative 17import Control.Applicative
18import Control.Monad
17 19
18import Data.Metrology 20import Data.Metrology
19import Data.Metrology.TH 21import Data.Metrology.TH
@@ -42,9 +44,6 @@ declareDerivedUnit "Year" [t| Day |] 365.25 Nothing
42 44
43type Time = MkQu_DLN D.Time DefaultLCSU Fixed.Pico 45type Time = MkQu_DLN D.Time DefaultLCSU Fixed.Pico
44 46
45data Prefix where
46 Prefix :: forall p. (UnitPrefix p, Show p) => p -> Prefix
47
48instance HasResolution p => AdditiveGroup (Fixed p) where 47instance 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
59timeLength :: StringParser s m => m Time 58timeLength :: StringParser s m => m Time
60timeLength = (*^) <$> lexeme rational <*> timeUnit 59timeLength = (*^) <$> lexeme rational <*> timeUnit <?> "Length of time"
61 60
62rational :: forall s m n. (StringParser s m, Fractional n, Num n) => m n 61rational :: forall s m n. (StringParser s m, Fractional n, Num n) => m n
63rational = combine <$> some digitChar <*> option [] (char '.' *> many digitChar) 62rational = 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
71timeUnit :: StringParser s m => m Time 70timeUnit :: StringParser s m => m Time
72timeUnit = label "Unit of time" . choice $ 71timeUnit = 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
105data Prefix where
106 Prefix :: forall p. ParseablePrefix p => p -> Prefix
107
108class UnitPrefix a => ParseablePrefix a where
109 parser :: StringParser s m => a -> m ()
110
111instance {-# OVERLAPPABLE #-} (UnitPrefix a, Show a) => ParseablePrefix a where
112 parser = void . string . show
113
114instance ParseablePrefix Micro where
115 parser _ = void $ choice [ string "ยต", string "u" ]
116
105siPrefix :: (StringParser s m, Fractional n) => m n 117siPrefix :: (StringParser s m, Fractional n) => m n
106siPrefix = label "SI prefix" . choice $ map (\(Prefix p) -> multiplier p <$ string (show p)) 118siPrefix = 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
7import Control.Lens 7import Control.Lens
8 8
9import Data.Time 9import Data.Time
10import Data.Time.Lens
10import Data.Time.Zones 11import Data.Time.Zones
11 12
13import Data.Functor
12import Data.AdditiveGroup 14import Data.AdditiveGroup
13 15
14import Text.Megaparsec 16import Text.Megaparsec
@@ -30,6 +32,6 @@ signed = (<*>) (lexeme sign)
30optSigned = (<*>) (option id $ lexeme sign) 32optSigned = (<*>) (option id $ lexeme sign)
31 33
32sign :: (StringParser s m, AdditiveGroup n) => m (n -> n) 34sign :: (StringParser s m, AdditiveGroup n) => m (n -> n)
33sign = choice [ id <$ char '+' 35sign = 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}:
5mkDerivation { 5mkDerivation {
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";