1
1
{-# LANGUAGE FlexibleContexts #-}
2
2
3
+ {- |
4
+ Module : Text.Megaparsec.Time
5
+ Description : Various parsers for types related to time.
6
+ Copyright : (c) drlkf, 2024
7
+ License : GPL-3
8
+
9
+ Stability : experimental
10
+ -}
11
+
3
12
module Text.Megaparsec.Time
4
- ( dateParser
13
+ ( DayResult
14
+
15
+ , dateParser
5
16
, dayParser
6
17
, durationParser
7
18
, gregorianDayParser
@@ -14,8 +25,8 @@ module Text.Megaparsec.Time
14
25
import Control.Applicative (optional , (<|>) )
15
26
import Control.Monad (replicateM , void )
16
27
import Control.Monad.Combinators (choice , some )
28
+ import Data.Char (toLower )
17
29
import Data.Functor (($>) )
18
- import Data.List.Extra (lower )
19
30
import Data.Maybe (fromMaybe )
20
31
import Data.Time (Day , DayOfWeek (.. ),
21
32
NominalDiffTime , TimeOfDay (.. ),
@@ -26,13 +37,22 @@ import Text.Megaparsec.Char (char, digitChar, space, space1,
26
37
string' )
27
38
import Text.Megaparsec.Utils (posNumParser )
28
39
40
+ -- | Representation of a parser result with either a number of days relative to
41
+ -- the current day, or a 'DayOfWeek'.
29
42
type DayResult = Either Int DayOfWeek
30
43
44
+ -- | Parse a tuple containing a day or not, and a 'TimeOfDay'.
31
45
dateParser
32
46
:: Ord e
33
47
=> Parsec e String (Maybe DayResult , TimeOfDay )
34
48
dateParser = (,) <$> optional (try (dayParser <* space1)) <*> timeParser
35
49
50
+ -- | Parse a day using one of the following, all case-insensitive:
51
+ --
52
+ -- * a short (3-letters) or long day name e.g @mon@ or @monday@
53
+ -- * @yesterday@ or @tomorrow@
54
+ -- * a day number relative to the current day i.e @+2@ is two days from today
55
+ -- * an absolute number for a 'DayOfWeek', refer to its 'Num' instance for more information.
36
56
dayParser
37
57
:: Ord e
38
58
=> Parsec e String DayResult
@@ -43,14 +63,16 @@ dayParser = choice
43
63
, Left <$> (string' " tomorrow" $> 1 )
44
64
, Right <$> absoluteDay
45
65
, Left <$> relativeDay
46
- ] where shortDay = choice $ map (ciString (lower . take 3 . show )) weekDays
47
- longDay = choice $ map (ciString (lower . show )) weekDays
66
+ ] where shortDay = choice $ map (ciString (fmap toLower . take 3 . show )) weekDays
67
+ longDay = choice $ map (ciString (fmap toLower . show )) weekDays
48
68
ciString f d = try (string' (f d)) $> d
49
69
weekDays = [Monday .. Friday ]
50
70
sign = (char ' -' $> negate ) <|> (char ' +' $> id )
51
71
absoluteDay = toEnum . read <$> some digitChar
52
72
relativeDay = ($) <$> sign <*> (read <$> some digitChar)
53
73
74
+ -- | Parse a 'NominalDiffTime' using strings like @1h23m45s@, with all
75
+ -- components being optional as long as one is present.
54
76
durationParser
55
77
:: Ord e
56
78
=> Parsec e String NominalDiffTime
@@ -60,14 +82,15 @@ durationParser = try hours <|> try minutes <|> secondsParser
60
82
m <- fromMaybe zero <$> optional (try minutes)
61
83
s <- fromMaybe zero <$> optional secondsParser
62
84
63
- return $ h + m + s
85
+ return ( h + m + s)
64
86
65
87
minutes = do
66
88
m <- minutesParser <* space
67
89
s <- fromMaybe zero <$> optional secondsParser
68
90
69
- return $ m + s
91
+ return ( m + s)
70
92
93
+ -- | Parse a Gregorian 'Day' from a @%d\/%m\/%Y@ format.
71
94
gregorianDayParser
72
95
:: Ord e
73
96
=> Parsec e String Day
@@ -76,30 +99,34 @@ gregorianDayParser = do
76
99
parseTimeM False defaultTimeLocale " %F" s <|>
77
100
parseTimeM False defaultTimeLocale " %d/%m/%Y" s
78
101
102
+ -- | Parse a 'NominalDiffTime' from a number of hours from a string like @1h@.
79
103
hoursParser
80
104
:: Ord e
81
105
=> Parsec e String NominalDiffTime
82
106
hoursParser = secondsToNominalDiffTime . (* 3600 ) <$> posNumParser <* char ' h'
83
107
108
+ -- | Parse a 'NominalDiffTime' from a number of minutes from a string like @1m@.
84
109
minutesParser
85
110
:: Ord e
86
111
=> Parsec e String NominalDiffTime
87
112
minutesParser = secondsToNominalDiffTime . (* 60 ) <$> posNumParser <* char ' m'
88
113
114
+ -- | Parse a 'NominalDiffTime' from a number of seconds from a string like @1s@.
89
115
secondsParser
90
116
:: Ord e
91
117
=> Parsec e String NominalDiffTime
92
118
secondsParser = secondsToNominalDiffTime <$> posNumParser <* optional (char ' s' )
93
119
120
+ -- | Parse a 'TimeOfDay' from a string like @01:23@.
94
121
timeParser
95
122
:: Ord e
96
123
=> Parsec e String TimeOfDay
97
124
timeParser = do
98
- h <- read <$> replicateM 2 digitChar
99
- void $ char ' :'
125
+ h <- read <$> replicateM 2 digitChar <* char ' :'
100
126
m <- read <$> replicateM 2 digitChar
101
127
102
128
return $ TimeOfDay h m 0
103
129
130
+ -- | Zero seconds in 'NominalDiffTime'.
104
131
zero :: NominalDiffTime
105
132
zero = secondsToNominalDiffTime 0
0 commit comments