module Darcs.Util.DateMatcher
(
parseDateMatcher
, DateMatcher(..)
, getMatchers
, testDate
, testDateAt
) where
import Darcs.Prelude
import Control.Exception ( catchJust )
import Data.Maybe ( isJust )
import System.IO.Error ( isUserError, ioeGetErrorString )
import System.Time
import Text.ParserCombinators.Parsec ( eof, parse, ParseError )
import Darcs.Util.IsoDate
( parseDate, englishDateTime, englishInterval, englishLast
, iso8601Interval, resetCalendar, subtractFromMCal, getLocalTz
, MCalendarTime(..), toMCalendarTime, unsafeToCalendarTime
, unsetTime, readUTCDate
)
withinDay :: CalendarTime -> CalendarTime -> Bool
withinDay :: CalendarTime -> CalendarTime -> Bool
withinDay CalendarTime
a CalendarTime
b = Maybe ClockTime -> Maybe ClockTime -> ClockTime -> Bool
within (ClockTime -> Maybe ClockTime
forall a. a -> Maybe a
Just (ClockTime -> Maybe ClockTime) -> ClockTime -> Maybe ClockTime
forall a b. (a -> b) -> a -> b
$ CalendarTime -> ClockTime
toClockTime CalendarTime
a)
(ClockTime -> Maybe ClockTime
forall a. a -> Maybe a
Just (TimeDiff -> ClockTime -> ClockTime
addToClockTime TimeDiff
day (ClockTime -> ClockTime) -> ClockTime -> ClockTime
forall a b. (a -> b) -> a -> b
$ CalendarTime -> ClockTime
toClockTime CalendarTime
a))
(CalendarTime -> ClockTime
toClockTime CalendarTime
b)
where
day :: TimeDiff
day = Int -> Int -> Int -> Int -> Int -> Int -> Integer -> TimeDiff
TimeDiff Int
0 Int
0 Int
1 Int
0 Int
0 Int
0 Integer
0
dateRange :: Maybe MCalendarTime -> Maybe MCalendarTime -> CalendarTime -> Bool
dateRange :: Maybe MCalendarTime -> Maybe MCalendarTime -> CalendarTime -> Bool
dateRange Maybe MCalendarTime
a Maybe MCalendarTime
b = Maybe CalendarTime -> Maybe CalendarTime -> CalendarTime -> Bool
cDateRange ((MCalendarTime -> CalendarTime)
-> Maybe MCalendarTime -> Maybe CalendarTime
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap MCalendarTime -> CalendarTime
unsafeToCalendarTime Maybe MCalendarTime
a)
((MCalendarTime -> CalendarTime)
-> Maybe MCalendarTime -> Maybe CalendarTime
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap MCalendarTime -> CalendarTime
unsafeToCalendarTime Maybe MCalendarTime
b)
cDateRange :: Maybe CalendarTime -> Maybe CalendarTime -> CalendarTime -> Bool
cDateRange :: Maybe CalendarTime -> Maybe CalendarTime -> CalendarTime -> Bool
cDateRange Maybe CalendarTime
a Maybe CalendarTime
b CalendarTime
c = Maybe ClockTime -> Maybe ClockTime -> ClockTime -> Bool
within ((CalendarTime -> ClockTime)
-> Maybe CalendarTime -> Maybe ClockTime
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap CalendarTime -> ClockTime
toClockTime Maybe CalendarTime
a)
((CalendarTime -> ClockTime)
-> Maybe CalendarTime -> Maybe ClockTime
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap CalendarTime -> ClockTime
toClockTime Maybe CalendarTime
b) (CalendarTime -> ClockTime
toClockTime CalendarTime
c)
within :: Maybe ClockTime -> Maybe ClockTime -> ClockTime -> Bool
within :: Maybe ClockTime -> Maybe ClockTime -> ClockTime -> Bool
within (Just ClockTime
start) (Just ClockTime
end) ClockTime
time = ClockTime
start ClockTime -> ClockTime -> Bool
forall a. Ord a => a -> a -> Bool
<= ClockTime
time Bool -> Bool -> Bool
&& ClockTime
time ClockTime -> ClockTime -> Bool
forall a. Ord a => a -> a -> Bool
< ClockTime
end
within Maybe ClockTime
Nothing (Just ClockTime
end) ClockTime
time = ClockTime
time ClockTime -> ClockTime -> Bool
forall a. Ord a => a -> a -> Bool
< ClockTime
end
within (Just ClockTime
start) Maybe ClockTime
Nothing ClockTime
time = ClockTime
start ClockTime -> ClockTime -> Bool
forall a. Ord a => a -> a -> Bool
<= ClockTime
time
within Maybe ClockTime
_ Maybe ClockTime
_ ClockTime
_ = Bool
forall a. HasCallStack => a
undefined
samePartialDate :: MCalendarTime -> CalendarTime -> Bool
samePartialDate :: MCalendarTime -> CalendarTime -> Bool
samePartialDate MCalendarTime
a CalendarTime
b_ =
Maybe ClockTime -> Maybe ClockTime -> ClockTime -> Bool
within (ClockTime -> Maybe ClockTime
forall a. a -> Maybe a
Just ClockTime
clockA)
(ClockTime -> Maybe ClockTime
forall a. a -> Maybe a
Just (ClockTime -> Maybe ClockTime) -> ClockTime -> Maybe ClockTime
forall a b. (a -> b) -> a -> b
$ TimeDiff -> ClockTime -> ClockTime
addToClockTime TimeDiff
interval ClockTime
clockA)
(CalendarTime -> ClockTime
toClockTime CalendarTime
calB)
where
interval :: TimeDiff
interval
| Maybe Int -> Bool
forall a. Maybe a -> Bool
isJust (MCalendarTime -> Maybe Int
mctSec MCalendarTime
a) = TimeDiff
second
| Maybe Int -> Bool
forall a. Maybe a -> Bool
isJust (MCalendarTime -> Maybe Int
mctMin MCalendarTime
a) = TimeDiff
minute
| Maybe Int -> Bool
forall a. Maybe a -> Bool
isJust (MCalendarTime -> Maybe Int
mctHour MCalendarTime
a) = TimeDiff
hour
| Maybe Int -> Bool
forall a. Maybe a -> Bool
isJust (MCalendarTime -> Maybe Int
mctYDay MCalendarTime
a) = TimeDiff
day
| MCalendarTime -> Bool
mctWeek MCalendarTime
a = TimeDiff -> (Day -> TimeDiff) -> Maybe Day -> TimeDiff
forall b a. b -> (a -> b) -> Maybe a -> b
maybe TimeDiff
week (TimeDiff -> Day -> TimeDiff
forall a b. a -> b -> a
const TimeDiff
day) (MCalendarTime -> Maybe Day
mctWDay MCalendarTime
a)
| Maybe Int -> Bool
forall a. Maybe a -> Bool
isJust (MCalendarTime -> Maybe Int
mctDay MCalendarTime
a) = TimeDiff
day
| Maybe Month -> Bool
forall a. Maybe a -> Bool
isJust (MCalendarTime -> Maybe Month
mctMonth MCalendarTime
a) = TimeDiff
month
| Bool
otherwise = TimeDiff
year
year :: TimeDiff
year = Int -> Int -> Int -> Int -> Int -> Int -> Integer -> TimeDiff
TimeDiff Int
1 Int
0 Int
0 Int
0 Int
0 Int
0 Integer
0
month :: TimeDiff
month = Int -> Int -> Int -> Int -> Int -> Int -> Integer -> TimeDiff
TimeDiff Int
0 Int
1 Int
0 Int
0 Int
0 Int
0 Integer
0
week :: TimeDiff
week = Int -> Int -> Int -> Int -> Int -> Int -> Integer -> TimeDiff
TimeDiff Int
0 Int
0 Int
7 Int
0 Int
0 Int
0 Integer
0
day :: TimeDiff
day = Int -> Int -> Int -> Int -> Int -> Int -> Integer -> TimeDiff
TimeDiff Int
0 Int
0 Int
1 Int
0 Int
0 Int
0 Integer
0
hour :: TimeDiff
hour = Int -> Int -> Int -> Int -> Int -> Int -> Integer -> TimeDiff
TimeDiff Int
0 Int
0 Int
0 Int
1 Int
0 Int
0 Integer
0
minute :: TimeDiff
minute = Int -> Int -> Int -> Int -> Int -> Int -> Integer -> TimeDiff
TimeDiff Int
0 Int
0 Int
0 Int
0 Int
1 Int
0 Integer
0
second :: TimeDiff
second = Int -> Int -> Int -> Int -> Int -> Int -> Integer -> TimeDiff
TimeDiff Int
0 Int
0 Int
0 Int
0 Int
0 Int
1 Integer
0
clockA :: ClockTime
clockA = CalendarTime -> ClockTime
toClockTime (CalendarTime -> ClockTime) -> CalendarTime -> ClockTime
forall a b. (a -> b) -> a -> b
$ MCalendarTime -> CalendarTime
unsafeToCalendarTime MCalendarTime
a
calB :: CalendarTime
calB = CalendarTime -> CalendarTime
resetCalendar CalendarTime
b_
data DateMatcher = forall d . (Show d) => DM
String
(Either ParseError d)
(d -> CalendarTime -> Bool)
parseDateMatcher :: String -> IO (CalendarTime -> Bool)
parseDateMatcher :: String -> IO (CalendarTime -> Bool)
parseDateMatcher String
d = IO (CalendarTime -> Bool)
testDateMatcher IO (CalendarTime -> Bool)
-> (String -> IO (CalendarTime -> Bool))
-> IO (CalendarTime -> Bool)
forall {a}. IO a -> (String -> IO a) -> IO a
`catchUserError` String -> IO (CalendarTime -> Bool)
forall {a}. String -> a
handleError
where
catchUserError :: IO a -> (String -> IO a) -> IO a
catchUserError = (IOError -> Maybe String) -> IO a -> (String -> IO a) -> IO a
forall e b a.
Exception e =>
(e -> Maybe b) -> IO a -> (b -> IO a) -> IO a
catchJust ((IOError -> Maybe String) -> IO a -> (String -> IO a) -> IO a)
-> (IOError -> Maybe String) -> IO a -> (String -> IO a) -> IO a
forall a b. (a -> b) -> a -> b
$ \IOError
e ->
if IOError -> Bool
isUserError IOError
e then String -> Maybe String
forall a. a -> Maybe a
Just (IOError -> String
ioeGetErrorString IOError
e) else Maybe String
forall a. Maybe a
Nothing
handleError :: String -> a
handleError String
e = if String
e String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"Time.toClockTime: invalid input"
then String -> a
forall a. HasCallStack => String -> a
error String
"Can't handle dates that far back!"
else String -> a
forall a. HasCallStack => String -> a
error String
e
testDateMatcher :: IO (CalendarTime -> Bool)
testDateMatcher = do
CalendarTime -> Bool
matcher <- [DateMatcher] -> CalendarTime -> Bool
tryMatchers ([DateMatcher] -> CalendarTime -> Bool)
-> IO [DateMatcher] -> IO (CalendarTime -> Bool)
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` String -> IO [DateMatcher]
getMatchers String
d
CalendarTime -> Bool
matcher (CalendarTime -> Bool) -> IO CalendarTime -> IO Bool
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` IO CalendarTime
now IO Bool
-> (Bool -> IO (CalendarTime -> Bool)) -> IO (CalendarTime -> Bool)
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (Bool -> IO (CalendarTime -> Bool) -> IO (CalendarTime -> Bool)
forall a b. a -> b -> b
`seq` (CalendarTime -> Bool) -> IO (CalendarTime -> Bool)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return CalendarTime -> Bool
matcher)
getMatchers :: String -> IO [DateMatcher]
getMatchers :: String -> IO [DateMatcher]
getMatchers String
d = do
CalendarTime
rightNow <- IO CalendarTime
now
let midnightToday :: CalendarTime
midnightToday = CalendarTime -> CalendarTime
unsetTime CalendarTime
rightNow
mRightNow :: MCalendarTime
mRightNow = CalendarTime -> MCalendarTime
toMCalendarTime CalendarTime
rightNow
matchIsoInterval :: Either TimeDiff (MCalendarTime, MCalendarTime)
-> CalendarTime -> Bool
matchIsoInterval (Left TimeDiff
dur) =
let durAgo :: MCalendarTime
durAgo = TimeDiff
dur TimeDiff -> MCalendarTime -> MCalendarTime
`subtractFromMCal` MCalendarTime
mRightNow in
Maybe MCalendarTime -> Maybe MCalendarTime -> CalendarTime -> Bool
dateRange (MCalendarTime -> Maybe MCalendarTime
forall a. a -> Maybe a
Just MCalendarTime
durAgo) (MCalendarTime -> Maybe MCalendarTime
forall a. a -> Maybe a
Just MCalendarTime
mRightNow)
matchIsoInterval (Right (MCalendarTime
a,MCalendarTime
b)) = Maybe MCalendarTime -> Maybe MCalendarTime -> CalendarTime -> Bool
dateRange (MCalendarTime -> Maybe MCalendarTime
forall a. a -> Maybe a
Just MCalendarTime
a) (MCalendarTime -> Maybe MCalendarTime
forall a. a -> Maybe a
Just MCalendarTime
b)
Int
tzNow <- IO Int
getLocalTz
[DateMatcher] -> IO [DateMatcher]
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return
[ String
-> Either ParseError (CalendarTime, CalendarTime)
-> ((CalendarTime, CalendarTime) -> CalendarTime -> Bool)
-> DateMatcher
forall d.
Show d =>
String
-> Either ParseError d
-> (d -> CalendarTime -> Bool)
-> DateMatcher
DM String
"from English date"
(ParsecT String () Identity (CalendarTime, CalendarTime)
-> Either ParseError (CalendarTime, CalendarTime)
forall {a}. ParsecT String () Identity a -> Either ParseError a
parseDateWith (ParsecT String () Identity (CalendarTime, CalendarTime)
-> Either ParseError (CalendarTime, CalendarTime))
-> ParsecT String () Identity (CalendarTime, CalendarTime)
-> Either ParseError (CalendarTime, CalendarTime)
forall a b. (a -> b) -> a -> b
$ CalendarTime
-> ParsecT String () Identity (CalendarTime, CalendarTime)
forall a. CalendarTime -> CharParser a (CalendarTime, CalendarTime)
englishLast CalendarTime
midnightToday)
(\(CalendarTime
a,CalendarTime
_) -> Maybe CalendarTime -> Maybe CalendarTime -> CalendarTime -> Bool
cDateRange (CalendarTime -> Maybe CalendarTime
forall a. a -> Maybe a
Just CalendarTime
a) Maybe CalendarTime
forall a. Maybe a
Nothing)
, String
-> Either ParseError CalendarTime
-> (CalendarTime -> CalendarTime -> Bool)
-> DateMatcher
forall d.
Show d =>
String
-> Either ParseError d
-> (d -> CalendarTime -> Bool)
-> DateMatcher
DM String
"specific English date"
(ParsecT String () Identity CalendarTime
-> Either ParseError CalendarTime
forall {a}. ParsecT String () Identity a -> Either ParseError a
parseDateWith (ParsecT String () Identity CalendarTime
-> Either ParseError CalendarTime)
-> ParsecT String () Identity CalendarTime
-> Either ParseError CalendarTime
forall a b. (a -> b) -> a -> b
$ CalendarTime -> ParsecT String () Identity CalendarTime
forall a. CalendarTime -> CharParser a CalendarTime
englishDateTime CalendarTime
midnightToday)
CalendarTime -> CalendarTime -> Bool
withinDay
, String
-> Either ParseError TimeInterval
-> (TimeInterval -> CalendarTime -> Bool)
-> DateMatcher
forall d.
Show d =>
String
-> Either ParseError d
-> (d -> CalendarTime -> Bool)
-> DateMatcher
DM String
"English interval"
(ParsecT String () Identity TimeInterval
-> Either ParseError TimeInterval
forall {a}. ParsecT String () Identity a -> Either ParseError a
parseDateWith (ParsecT String () Identity TimeInterval
-> Either ParseError TimeInterval)
-> ParsecT String () Identity TimeInterval
-> Either ParseError TimeInterval
forall a b. (a -> b) -> a -> b
$ CalendarTime -> ParsecT String () Identity TimeInterval
forall a. CalendarTime -> CharParser a TimeInterval
englishInterval CalendarTime
rightNow)
((Maybe CalendarTime -> Maybe CalendarTime -> CalendarTime -> Bool)
-> TimeInterval -> CalendarTime -> Bool
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Maybe CalendarTime -> Maybe CalendarTime -> CalendarTime -> Bool
cDateRange)
, String
-> Either
ParseError (Either TimeDiff (MCalendarTime, MCalendarTime))
-> (Either TimeDiff (MCalendarTime, MCalendarTime)
-> CalendarTime -> Bool)
-> DateMatcher
forall d.
Show d =>
String
-> Either ParseError d
-> (d -> CalendarTime -> Bool)
-> DateMatcher
DM String
"ISO 8601 interval"
(ParsecT
String () Identity (Either TimeDiff (MCalendarTime, MCalendarTime))
-> Either
ParseError (Either TimeDiff (MCalendarTime, MCalendarTime))
forall {a}. ParsecT String () Identity a -> Either ParseError a
parseDateWith (ParsecT
String () Identity (Either TimeDiff (MCalendarTime, MCalendarTime))
-> Either
ParseError (Either TimeDiff (MCalendarTime, MCalendarTime)))
-> ParsecT
String () Identity (Either TimeDiff (MCalendarTime, MCalendarTime))
-> Either
ParseError (Either TimeDiff (MCalendarTime, MCalendarTime))
forall a b. (a -> b) -> a -> b
$ Int
-> ParsecT
String () Identity (Either TimeDiff (MCalendarTime, MCalendarTime))
forall a.
Int
-> CharParser a (Either TimeDiff (MCalendarTime, MCalendarTime))
iso8601Interval Int
tzNow)
Either TimeDiff (MCalendarTime, MCalendarTime)
-> CalendarTime -> Bool
matchIsoInterval
, String
-> Either ParseError MCalendarTime
-> (MCalendarTime -> CalendarTime -> Bool)
-> DateMatcher
forall d.
Show d =>
String
-> Either ParseError d
-> (d -> CalendarTime -> Bool)
-> DateMatcher
DM String
"CVS, ISO 8601, old style, or RFC2822 date"
(Int -> String -> Either ParseError MCalendarTime
parseDate Int
tzNow String
d)
MCalendarTime -> CalendarTime -> Bool
samePartialDate
]
where
tillEof :: ParsecT s u m b -> ParsecT s u m b
tillEof ParsecT s u m b
p = do { b
x <- ParsecT s u m b
p; ParsecT s u m ()
forall s (m :: * -> *) t u.
(Stream s m t, Show t) =>
ParsecT s u m ()
eof; b -> ParsecT s u m b
forall a. a -> ParsecT s u m a
forall (m :: * -> *) a. Monad m => a -> m a
return b
x }
parseDateWith :: ParsecT String () Identity a -> Either ParseError a
parseDateWith ParsecT String () Identity a
p = ParsecT String () Identity a
-> String -> String -> Either ParseError a
forall s t a.
Stream s Identity t =>
Parsec s () a -> String -> s -> Either ParseError a
parse (ParsecT String () Identity a -> ParsecT String () Identity a
forall {s} {m :: * -> *} {t} {u} {b}.
(Stream s m t, Show t) =>
ParsecT s u m b -> ParsecT s u m b
tillEof ParsecT String () Identity a
p) String
"" String
d
tryMatchers :: [DateMatcher] -> CalendarTime -> Bool
tryMatchers :: [DateMatcher] -> CalendarTime -> Bool
tryMatchers (DM String
_ Either ParseError d
parsed d -> CalendarTime -> Bool
matcher : [DateMatcher]
ms) =
case Either ParseError d
parsed of
Left ParseError
_ -> [DateMatcher] -> CalendarTime -> Bool
tryMatchers [DateMatcher]
ms
Right d
d -> d -> CalendarTime -> Bool
matcher d
d
tryMatchers [] = String -> CalendarTime -> Bool
forall a. HasCallStack => String -> a
error String
"Can't support fancy dates."
now :: IO CalendarTime
now :: IO CalendarTime
now = IO ClockTime
getClockTime IO ClockTime -> (ClockTime -> IO CalendarTime) -> IO CalendarTime
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= ClockTime -> IO CalendarTime
toCalendarTime
testDate :: String -> IO ()
testDate :: String -> IO ()
testDate String
d = do CalendarTime
cnow <- IO CalendarTime
now
CalendarTime -> String -> IO ()
testDateAtCal CalendarTime
cnow String
d
testDateAt :: String -> String -> IO ()
testDateAt :: String -> String -> IO ()
testDateAt String
iso = CalendarTime -> String -> IO ()
testDateAtCal (String -> CalendarTime
readUTCDate String
iso)
testDateAtCal :: CalendarTime -> String -> IO ()
testDateAtCal :: CalendarTime -> String -> IO ()
testDateAtCal CalendarTime
c String
d =
do [DateMatcher]
ms <- String -> IO [DateMatcher]
getMatchers String
d
String -> IO ()
putStr (String -> IO ())
-> ([DateMatcher] -> String) -> [DateMatcher] -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [String] -> String
unlines ([String] -> String)
-> ([DateMatcher] -> [String]) -> [DateMatcher] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (DateMatcher -> String) -> [DateMatcher] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (CalendarTime -> DateMatcher -> String
showMatcher CalendarTime
c) ([DateMatcher] -> IO ()) -> [DateMatcher] -> IO ()
forall a b. (a -> b) -> a -> b
$ [DateMatcher]
ms
showMatcher :: CalendarTime -> DateMatcher -> String
showMatcher :: CalendarTime -> DateMatcher -> String
showMatcher CalendarTime
cnow (DM String
n Either ParseError d
p d -> CalendarTime -> Bool
m) =
String
"==== " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
n String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" ====\n" String -> String -> String
forall a. [a] -> [a] -> [a]
++
(case Either ParseError d
p of
Left ParseError
err -> ParseError -> String -> String
forall a. Show a => a -> String -> String
shows ParseError
err String
""
Right d
x -> d -> String
forall a. Show a => a -> String
show d
x String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\n" String -> String -> String
forall a. [a] -> [a] -> [a]
++ Bool -> String
forall a. Show a => a -> String
show (d -> CalendarTime -> Bool
m d
x CalendarTime
cnow))