lib: support "every 2nd Thursday of month" in period expressions
Useful for periodic transactions.
This commit is contained in:
parent
f1b4618f2d
commit
993e3f2b67
@ -167,6 +167,8 @@ spansSpan spans = DateSpan (maybe Nothing spanStart $ headMay spans) (maybe Noth
|
||||
-- [DateSpan 2007/12/31-2008/01/13,DateSpan 2008/01/14-2008/01/27]
|
||||
-- >>> t (DayOfMonth 2) "2008/01/01" "2008/04/01"
|
||||
-- [DateSpan 2007/12/02-2008/01/01,DateSpan 2008/01/02-2008/02/01,DateSpan 2008/02/02-2008/03/01,DateSpan 2008/03/02-2008/04/01]
|
||||
-- >>> t (WeekdayOfMonth 2 4) "2011/01/01" "2011/02/15"
|
||||
-- [DateSpan 2010/12/09-2011/01/12,DateSpan 2011/01/13-2011/02/09,DateSpan 2011/02/10-2011/03/09]
|
||||
-- >>> t (DayOfWeek 2) "2011/01/01" "2011/01/15"
|
||||
-- [DateSpan 2010/12/28-2011/01/03,DateSpan 2011/01/04-2011/01/10,DateSpan 2011/01/11-2011/01/17]
|
||||
-- >>> t (DayOfYear 11 29) "2011/10/01" "2011/10/15"
|
||||
@ -183,6 +185,7 @@ splitSpan (Months n) s = splitspan startofmonth (applyN n nextmonth) s
|
||||
splitSpan (Quarters n) s = splitspan startofquarter (applyN n nextquarter) s
|
||||
splitSpan (Years n) s = splitspan startofyear (applyN n nextyear) s
|
||||
splitSpan (DayOfMonth n) s = splitspan (nthdayofmonthcontaining n) (applyN (n-1) nextday . nextmonth) s
|
||||
splitSpan (WeekdayOfMonth n wd) s = splitspan (nthweekdayofmonthcontaining n wd) (advancetonthweekday n wd . nextmonth) s
|
||||
splitSpan (DayOfWeek n) s = splitspan (nthdayofweekcontaining n) (applyN (n-1) nextday . nextweek) s
|
||||
splitSpan (DayOfYear m n) s= splitspan (nthdayofyearcontaining m n) (applyN (n-1) nextday . applyN (m-1) nextmonth . nextyear) s
|
||||
-- splitSpan (WeekOfYear n) s = splitspan startofweek (applyN n nextweek) s
|
||||
@ -538,6 +541,35 @@ nthdayofweekcontaining n d | nthOfSameWeek <= d = nthOfSameWeek
|
||||
nthOfPrevWeek = addDays (fromIntegral n-1) $ prevweek s
|
||||
s = startofweek d
|
||||
|
||||
-- | For given date d find month-long interval that starts on nth weekday of month
|
||||
-- and covers it.
|
||||
--
|
||||
-- Examples: 2017-11-22 is 3rd Wed of Nov. Month-long intervals that cover it and
|
||||
-- start on 1st-4th Wed will start in Nov. However
|
||||
-- intervals that start on 4th Thu or Fri or later should start in Oct:
|
||||
-- >>> let wed22nd = parsedate "2017-11-22"
|
||||
-- >>> nthweekdayofmonthcontaining 1 3 wed22nd
|
||||
-- 2017-11-01
|
||||
-- >>> nthweekdayofmonthcontaining 3 2 wed22nd
|
||||
-- 2017-11-21
|
||||
-- >>> nthweekdayofmonthcontaining 4 3 wed22nd
|
||||
-- 2017-11-22
|
||||
-- >>> nthweekdayofmonthcontaining 4 4 wed22nd
|
||||
-- 2017-10-26
|
||||
-- >>> nthweekdayofmonthcontaining 4 5 wed22nd
|
||||
-- 2017-10-27
|
||||
nthweekdayofmonthcontaining n wd d | nthWeekdaySameMonth <= d = nthWeekdaySameMonth
|
||||
| otherwise = nthWeekdayPrevMonth
|
||||
where nthWeekdaySameMonth = advancetonthweekday n wd $ startofmonth d
|
||||
nthWeekdayPrevMonth = advancetonthweekday n wd $ prevmonth d
|
||||
|
||||
-- | Advance to nth weekday wd after given start day s
|
||||
advancetonthweekday n wd s = addWeeks (n-1) . firstMatch (>=s) . iterate (addWeeks 1) $ firstweekday s
|
||||
where
|
||||
addWeeks k = addDays (7 * fromIntegral k)
|
||||
firstMatch p = head . dropWhile (not . p)
|
||||
firstweekday = addDays (fromIntegral wd-1) . startofweek
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- parsing
|
||||
|
||||
@ -698,8 +730,8 @@ md = do
|
||||
months = ["january","february","march","april","may","june",
|
||||
"july","august","september","october","november","december"]
|
||||
monthabbrevs = ["jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"]
|
||||
-- weekdays = ["monday","tuesday","wednesday","thursday","friday","saturday","sunday"]
|
||||
-- weekdayabbrevs = ["mon","tue","wed","thu","fri","sat","sun"]
|
||||
weekdays = ["monday","tuesday","wednesday","thursday","friday","saturday","sunday"]
|
||||
weekdayabbrevs = ["mon","tue","wed","thu","fri","sat","sun"]
|
||||
|
||||
monthIndex t = maybe 0 (+1) $ t `elemIndex` months
|
||||
monIndex t = maybe 0 (+1) $ t `elemIndex` monthabbrevs
|
||||
@ -716,6 +748,12 @@ mon = do
|
||||
let i = monIndex m
|
||||
return ("",show i,"")
|
||||
|
||||
weekday :: SimpleTextParser Int
|
||||
weekday = do
|
||||
wday <- choice . map string' $ weekdays ++ weekdayabbrevs
|
||||
let i = head . catMaybes $ [wday `elemIndex` weekdays, wday `elemIndex` weekdayabbrevs]
|
||||
return (i+1)
|
||||
|
||||
today,yesterday,tomorrow :: SimpleTextParser SmartDate
|
||||
today = string "today" >> return ("","","today")
|
||||
yesterday = string "yesterday" >> return ("","","yesterday")
|
||||
@ -769,6 +807,10 @@ lastthisnextthing = do
|
||||
-- Right (DayOfYear 11 29,DateSpan 2009/01/01-)
|
||||
-- >>> p "every 11/29 from 2009"
|
||||
-- Right (DayOfYear 11 29,DateSpan 2009/01/01-)
|
||||
-- >>> p "every 2nd Thursday of month to 2009"
|
||||
-- Right (WeekdayOfMonth 2 4,DateSpan -2008/12/31)
|
||||
-- >>> p "every 1st monday of month to 2009"
|
||||
-- Right (WeekdayOfMonth 1 1,DateSpan -2008/12/31)
|
||||
periodexpr :: Day -> SimpleTextParser (Interval, DateSpan)
|
||||
periodexpr rdate = choice $ map try [
|
||||
intervalanddateperiodexpr rdate,
|
||||
@ -833,7 +875,14 @@ reportinginterval = choice' [
|
||||
many spacenonewline
|
||||
("",m,d) <- md
|
||||
optOf_ "year"
|
||||
return $ DayOfYear (read m) (read d)
|
||||
return $ DayOfYear (read m) (read d),
|
||||
do string "every"
|
||||
many spacenonewline
|
||||
n <- nth
|
||||
many spacenonewline
|
||||
wd <- weekday
|
||||
optOf_ "month"
|
||||
return $ WeekdayOfMonth n wd
|
||||
]
|
||||
where
|
||||
of_ period = do
|
||||
|
||||
@ -89,6 +89,7 @@ data Interval =
|
||||
| Quarters Int
|
||||
| Years Int
|
||||
| DayOfMonth Int
|
||||
| WeekdayOfMonth Int Int
|
||||
| DayOfWeek Int
|
||||
| DayOfYear Int Int -- Month, Day
|
||||
-- WeekOfYear Int
|
||||
|
||||
Loading…
Reference in New Issue
Block a user