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] | -- [DateSpan 2007/12/31-2008/01/13,DateSpan 2008/01/14-2008/01/27] | ||||||
| -- >>> t (DayOfMonth 2) "2008/01/01" "2008/04/01" | -- >>> 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] | -- [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" | -- >>> 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] | -- [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" | -- >>> 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 (Quarters n)   s = splitspan startofquarter (applyN n nextquarter) s | ||||||
| splitSpan (Years n)      s = splitspan startofyear    (applyN n nextyear)    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 (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 (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 (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 | -- 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 |           nthOfPrevWeek = addDays (fromIntegral n-1) $ prevweek s | ||||||
|           s = startofweek d |           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 | -- parsing | ||||||
| 
 | 
 | ||||||
| @ -698,8 +730,8 @@ md = do | |||||||
| months         = ["january","february","march","april","may","june", | months         = ["january","february","march","april","may","june", | ||||||
|                   "july","august","september","october","november","december"] |                   "july","august","september","october","november","december"] | ||||||
| monthabbrevs   = ["jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"] | monthabbrevs   = ["jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"] | ||||||
| -- weekdays       = ["monday","tuesday","wednesday","thursday","friday","saturday","sunday"] | weekdays       = ["monday","tuesday","wednesday","thursday","friday","saturday","sunday"] | ||||||
| -- weekdayabbrevs = ["mon","tue","wed","thu","fri","sat","sun"] | weekdayabbrevs = ["mon","tue","wed","thu","fri","sat","sun"] | ||||||
| 
 | 
 | ||||||
| monthIndex t = maybe 0 (+1) $ t `elemIndex` months | monthIndex t = maybe 0 (+1) $ t `elemIndex` months | ||||||
| monIndex t   = maybe 0 (+1) $ t `elemIndex` monthabbrevs | monIndex t   = maybe 0 (+1) $ t `elemIndex` monthabbrevs | ||||||
| @ -716,6 +748,12 @@ mon = do | |||||||
|   let i = monIndex m |   let i = monIndex m | ||||||
|   return ("",show i,"") |   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,yesterday,tomorrow :: SimpleTextParser SmartDate | ||||||
| today     = string "today"     >> return ("","","today") | today     = string "today"     >> return ("","","today") | ||||||
| yesterday = string "yesterday" >> return ("","","yesterday") | yesterday = string "yesterday" >> return ("","","yesterday") | ||||||
| @ -769,6 +807,10 @@ lastthisnextthing = do | |||||||
| -- Right (DayOfYear 11 29,DateSpan 2009/01/01-) | -- Right (DayOfYear 11 29,DateSpan 2009/01/01-) | ||||||
| -- >>> p "every 11/29 from 2009" | -- >>> p "every 11/29 from 2009" | ||||||
| -- Right (DayOfYear 11 29,DateSpan 2009/01/01-) | -- 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 :: Day -> SimpleTextParser (Interval, DateSpan) | ||||||
| periodexpr rdate = choice $ map try [ | periodexpr rdate = choice $ map try [ | ||||||
|                     intervalanddateperiodexpr rdate, |                     intervalanddateperiodexpr rdate, | ||||||
| @ -833,7 +875,14 @@ reportinginterval = choice' [ | |||||||
|                           many spacenonewline |                           many spacenonewline | ||||||
|                           ("",m,d) <- md |                           ("",m,d) <- md | ||||||
|                           optOf_ "year" |                           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 |     where | ||||||
|       of_ period = do |       of_ period = do | ||||||
|  | |||||||
| @ -89,6 +89,7 @@ data Interval = | |||||||
|   | Quarters Int |   | Quarters Int | ||||||
|   | Years Int |   | Years Int | ||||||
|   | DayOfMonth Int |   | DayOfMonth Int | ||||||
|  |   | WeekdayOfMonth Int Int | ||||||
|   | DayOfWeek Int |   | DayOfWeek Int | ||||||
|   | DayOfYear Int Int -- Month, Day |   | DayOfYear Int Int -- Month, Day | ||||||
|   -- WeekOfYear Int |   -- WeekOfYear Int | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user