From 4049455f26de2974e83f351d84b1f60eba149c34 Mon Sep 17 00:00:00 2001 From: Dmitry Astapov Date: Fri, 24 Nov 2017 21:51:51 +0000 Subject: [PATCH] lib: Fix splitSpan for nthdayof{week,month} - start of DateSpan was not covered Demonstration: Consider year-test.journal: ``` 2015/02/01 first half expenses $1 assets 2015/07/01 second half expenses $2 assets 2016/02/01 first half expenses $4 assets 2016/07/01 second half expenses $8 assets 2017/02/01 first half expenses $16 assets 2017/07/01 second half expenses $32 assets ``` Year balances are good: ``` $ hledger balance -f year-test.journal -p yearly Balance changes in 2015/01/01-2017/12/31: || 2015 2016 2017 ==========++================== assets || $-3 $-12 $-48 expenses || $3 $12 $48 ----------++------------------ || 0 0 0 ``` Note how first transaction in 2015 is not included. Note that this is old period expression, so this bug exsits in master: ```$ hledger balance -f year-test.journal -p 'every 2nd day of month' Balance changes in 2015/07/02-2017/07/01: || 2015/07/02-2015/08/01 2015/08/02-2015/09/01 2015/09/02-2015/10/01 2015/10/02-2015/11/01 2015/11/02-2015/12/01 2015/12/02-2016/01/01 2016/01/02-2016/02/01 2016/02/02-2016/03/01 2016/03/02-2016/04/01 2016/04/02-2016/05/01 2016/05/02-2016/06/01 2016/06/02-2016/07/01 2016/07/02-2016/08/01 2016/08/02-2016/09/01 2016/09/02-2016/10/01 2016/10/02-2016/11/01 2016/11/02-2016/12/01 2016/12/02-2017/01/01 2017/01/02-2017/02/01 2017/02/02-2017/03/01 2017/03/02-2017/04/01 2017/04/02-2017/05/01 2017/05/02-2017/06/01 2017/06/02-2017/07/01 ==========++======================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================== assets || 0 0 0 0 0 0 $-4 0 0 0 0 $-8 0 0 0 0 0 0 $-16 0 0 0 0 $-32 expenses || 0 0 0 0 0 0 $4 0 0 0 0 $8 0 0 0 0 0 0 $16 0 0 0 0 $32 ----------++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ || 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ``` Note how 2015 is absent entirely. This is new expression, but i think that general nature of bug is the same... ``` $ hledger balance -f year-test.journal -p 'every 4th Apr' Balance changes in 2016/04/04-2018/04/03: || 2016/04/04-2017/04/03 2017/04/04-2018/04/03 ==========++============================================== assets || $-24 $-32 expenses || $24 $32 ----------++---------------------------------------------- || 0 0 ``` --- hledger-lib/Hledger/Data/Dates.hs | 55 +++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/hledger-lib/Hledger/Data/Dates.hs b/hledger-lib/Hledger/Data/Dates.hs index 6ce584654..ab8590935 100644 --- a/hledger-lib/Hledger/Data/Dates.hs +++ b/hledger-lib/Hledger/Data/Dates.hs @@ -165,9 +165,9 @@ spansSpan spans = DateSpan (maybe Nothing spanStart $ headMay spans) (maybe Noth -- >>> t (Weeks 2) "2008/01/01" "2008/01/15" -- [DateSpan 2007/12/31-2008/01/13,DateSpan 2008/01/14-2008/01/27] -- >>> t (DayOfMonth 2) "2008/01/01" "2008/04/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 (DayOfWeek 2) "2011/01/01" "2011/01/15" --- [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] -- splitSpan :: Interval -> DateSpan -> [DateSpan] splitSpan _ (DateSpan Nothing Nothing) = [DateSpan Nothing Nothing] @@ -461,16 +461,51 @@ prevyear = startofyear . addGregorianYearsClip (-1) nextyear = startofyear . addGregorianYearsClip 1 startofyear day = fromGregorian y 1 1 where (y,_,_) = toGregorian day -nthdayofmonthcontaining n d | d1 >= d = d1 - | otherwise = d2 - where d1 = addDays (fromIntegral n-1) s - d2 = addDays (fromIntegral n-1) $ nextmonth s +-- | For given date d find month-long interval that starts on nth day of month +-- and covers it. +-- +-- Examples: lets take 2017-11-22. Month-long intervals covering it that +-- start on 1st-22nd of month will start in Nov. However +-- intervals that start on 23rd-30th of month should start in Oct: +-- >>> let wed22nd = parsedate "2017-11-22" +-- >>> nthdayofmonthcontaining 1 wed22nd +-- 2017-11-01 +-- >>> nthdayofmonthcontaining 12 wed22nd +-- 2017-11-12 +-- >>> nthdayofmonthcontaining 22 wed22nd +-- 2017-11-22 +-- >>> nthdayofmonthcontaining 23 wed22nd +-- 2017-10-23 +-- >>> nthdayofmonthcontaining 30 wed22nd +-- 2017-10-30 +nthdayofmonthcontaining n d | nthOfSameMonth <= d = nthOfSameMonth + | otherwise = nthOfPrevMonth + where nthOfSameMonth = addDays (fromIntegral n-1) s + nthOfPrevMonth = addDays (fromIntegral n-1) $ prevmonth s s = startofmonth d -nthdayofweekcontaining n d | d1 >= d = d1 - | otherwise = d2 - where d1 = addDays (fromIntegral n-1) s - d2 = addDays (fromIntegral n-1) $ nextweek s + +-- | For given date d find week-long interval that starts on nth day of week +-- and covers it. +-- +-- Examples: 2017-11-22 is Wed. Week-long intervals that cover it and +-- start on Mon, Tue or Wed will start in the same week. However +-- intervals that start on Thu or Fri should start in prev week: +-- >>> let wed22nd = parsedate "2017-11-22" +-- >>> nthdayofweekcontaining 1 wed22nd +-- 2017-11-20 +-- >>> nthdayofweekcontaining 2 wed22nd +-- 2017-11-21 +-- >>> nthdayofweekcontaining 3 wed22nd +-- 2017-11-22 +-- >>> nthdayofweekcontaining 4 wed22nd +-- 2017-11-16 +-- >>> nthdayofweekcontaining 5 wed22nd +-- 2017-11-17 +nthdayofweekcontaining n d | nthOfSameWeek <= d = nthOfSameWeek + | otherwise = nthOfPrevWeek + where nthOfSameWeek = addDays (fromIntegral n-1) s + nthOfPrevWeek = addDays (fromIntegral n-1) $ prevweek s s = startofweek d ----------------------------------------------------------------------