summaryrefslogtreecommitdiff
path: root/lib/Postdelay/TimeSpec/Units.hs
blob: 330997ae8da8172e0ba4d2416b3ed8e101954bc1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
{-# LANGUAGE TemplateHaskell, TypeFamilies, GADTs, RankNTypes, DataKinds, ViewPatterns, ScopedTypeVariables #-}

module Postdelay.TimeSpec.Units
  ( Time
  
  , Second, Minute, Hour
  , Day, Week, Month, Year

  , timeLength

  , module Data.Units.SI.Prefixes
  ) where

import Postdelay.TimeSpec.Utils

import Control.Applicative

import Data.Metrology
import Data.Metrology.TH
import Data.Metrology.SI.Mono ()

import Data.Units.SI
import Data.Units.SI.Prefixes
import Data.Units.SI.Parser
import qualified Data.Dimensions.SI as D

import Data.Foldable
import Data.Function
import Data.VectorSpace

import Data.Fixed (Fixed, HasResolution)
import qualified Data.Fixed as Fixed


import Text.Megaparsec
  

declareDerivedUnit "Day"   [t| Hour |] 24     Nothing
declareDerivedUnit "Week"  [t| Day  |] 7      Nothing
declareDerivedUnit "Month" [t| Day  |] 30     Nothing
declareDerivedUnit "Year"  [t| Day  |] 365.25 Nothing

type Time = MkQu_DLN D.Time DefaultLCSU Fixed.Pico

data Prefix where
  Prefix :: forall p. (UnitPrefix p, Show p) => p -> Prefix

instance HasResolution p => AdditiveGroup (Fixed p) where
  zeroV = 0
  (^+^) = (+)
  negateV = negate
  (^-^) = (-)

instance HasResolution p => VectorSpace (Fixed p) where
  type Scalar (Fixed p) = Fixed p
  (*^) = (*)


timeLength :: StringParser s m => m Time
timeLength = (*^) <$> lexeme rational <*> timeUnit

rational :: forall s m n. (StringParser s m, Fractional n, Num n) => m n
rational = combine <$> some digitChar <*> option [] (char '.' *> many digitChar)
  where
    combine :: [Char] -> [Char] -> n
    combine (map asN -> whole) (map asN -> fractional)
      = foldl' (\i l -> i * 10 + l) 0 whole + (foldr' (\h t -> h + t / 10) 0 fractional) / 10
    asN :: Char -> n
    asN c = fromIntegral $ ((-) `on` fromEnum) c '0'

timeUnit :: StringParser s m => m Time
timeUnit = label "Unit of time" . choice $
           [ 1 % Second <$ choice [ string' "seconds"
                                  , string' "second"
                                  , string' "secs"
                                  , string' "sec"
                                  ]
           , 1 % Minute <$ choice [ string' "minutes"
                                  , string' "minute"
                                  , string' "mins"
                                  , string' "min"
                                  ]
           , 1 % Hour   <$ choice [ string' "hours"
                                  , string' "hour"
                                  ]
           , 1 % Day    <$ choice [ string' "days"
                                  , string' "day"
                                  ]
           , 1 % Week   <$ choice [ string' "weeks"
                                  , string' "week"
                                  ]
           , 1 % Month  <$ choice [ string' "months"
                                  , string' "month"
                                  ]
           , 1 % Year   <$ choice [ string' "years"
                                  , string' "year"
                                  ]
           ] ++
           [ (% Second) <$> option 1 siPrefix <* string "s"
           , (% Hour) <$> option 1 siPrefix <* string "h"
           , (% Day) <$> option 1 siPrefix <* string "d"
           , (% Year) <$> option 1 siPrefix <* choice [ string "a", string "yr", string "yrs" ]
           ]

siPrefix :: (StringParser s m, Fractional n) => m n
siPrefix = label "SI prefix" . choice $ map (\(Prefix p) -> multiplier p <$ string (show p))
           [ Prefix Deca, Prefix Hecto, Prefix Kilo, Prefix Mega, Prefix Giga
           , Prefix Tera, Prefix Peta, Prefix Exa, Prefix Zetta, Prefix Yotta
           , Prefix Deci, Prefix Centi, Prefix Milli, Prefix Micro, Prefix Nano
           , Prefix Pico, Prefix Femto, Prefix Atto, Prefix Zepto, Prefix Yocto
           ]