cli: clarify smart dates with more examples; add support for YYYYMM

This commit is contained in:
Simon Michael 2018-04-04 17:45:23 +01:00
parent 0e5aa349c8
commit 0b5ddcebee
6 changed files with 609 additions and 490 deletions

View File

@ -650,25 +650,58 @@ parsedate s = fromMaybe (error' $ "could not parse date \"" ++ s ++ "\"")
-- #endif -- #endif
{-| {-|
Parse a date in any of the formats allowed in ledger's period expressions, Parse a date in any of the formats allowed in Ledger's period expressions, and some others.
and maybe some others:
> 2004
> 2004/10
> 2004/10/1
> 10/1
> 21
> october, oct
> yesterday, today, tomorrow
> this/next/last week/day/month/quarter/year
Returns a SmartDate, to be converted to a full date later (see fixSmartDate).
Assumes any text in the parse stream has been lowercased. Assumes any text in the parse stream has been lowercased.
Returns a SmartDate, to be converted to a full date later (see fixSmartDate).
Examples:
> 2004 (start of year, which must have 4+ digits)
> 2004/10 (start of month, which must be 1-12)
> 2004/10/1 (exact date, day must be 1-31)
> 10/1 (month and day in current year)
> 21 (day in current month)
> october, oct (start of month in current year)
> yesterday, today, tomorrow (-1, 0, 1 days from today)
> last/this/next day/week/month/quarter/year (-1, 0, 1 periods from the current period)
> 20181201 (8 digit YYYYMMDD with valid year month and day)
> 201812 (6 digit YYYYMM with valid year and month)
Note malformed digit sequences might give surprising results:
> 201813 (6 digits with an invalid month is parsed as start of 6-digit year)
> 20181301 (8 digits with an invalid month is parsed as start of 8-digit year)
> 20181232 (8 digits with an invalid day gives an error)
> 201801012 (9+ digits beginning with a valid YYYYMMDD gives an error)
Eg:
YYYYMMDD is parsed as year-month-date if those parts are valid
(>=4 digits, 1-12, and 1-31 respectively):
>>> parsewith (smartdate <* eof) "20181201"
Right ("2018","12","01")
YYYYMM is parsed as year-month-01 if year and month are valid:
>>> parsewith (smartdate <* eof) "201804"
Right ("2018","04","01")
With an invalid month, it's parsed as a year:
>>> parsewith (smartdate <* eof) "201813"
Right ("201813","","")
A 9+ digit number beginning with valid YYYYMMDD gives an error:
>>> parsewith (smartdate <* eof) "201801012"
Left (TrivialError (SourcePos {sourceName = "", sourceLine = Pos 1, sourceColumn = Pos 9} :| []) (Just (Tokens ('2' :| ""))) (fromList [EndOfInput]))
Big numbers not beginning with a valid YYYYMMDD are parsed as a year:
>>> parsewith (smartdate <* eof) "201813012"
Right ("201813012","","")
-} -}
smartdate :: SimpleTextParser SmartDate smartdate :: SimpleTextParser SmartDate
smartdate = do smartdate = do
-- XXX maybe obscures date errors ? see ledgerdate -- XXX maybe obscures date errors ? see ledgerdate
(y,m,d) <- choice' [yyyymmdd, ymd, ym, md, y, d, month, mon, today, yesterday, tomorrow, lastthisnextthing] (y,m,d) <- choice' [yyyymmdd, yyyymm, ymd, ym, md, y, d, month, mon, today, yesterday, tomorrow, lastthisnextthing]
return (y,m,d) return (y,m,d)
-- | Like smartdate, but there must be nothing other than whitespace after the date. -- | Like smartdate, but there must be nothing other than whitespace after the date.
@ -703,6 +736,13 @@ yyyymmdd = do
failIfInvalidDay d failIfInvalidDay d
return (y,m,d) return (y,m,d)
yyyymm :: SimpleTextParser SmartDate
yyyymm = do
y <- count 4 digitChar
m <- count 2 digitChar
failIfInvalidMonth m
return (y,m,"01")
ymd :: SimpleTextParser SmartDate ymd :: SimpleTextParser SmartDate
ymd = do ymd = do
y <- some digitChar y <- some digitChar
@ -946,6 +986,9 @@ periodexprdatespan rdate = choice $ map try [
justdatespan rdate justdatespan rdate
] ]
-- |
-- -- >>> parsewith (doubledatespan (parsedate "2018/01/01") <* eof) "20180101-201804"
-- Right DateSpan 2018/01/01-2018/04/01
doubledatespan :: Day -> SimpleTextParser DateSpan doubledatespan :: Day -> SimpleTextParser DateSpan
doubledatespan rdate = do doubledatespan rdate = do
optional (string "from" >> skipMany spacenonewline) optional (string "from" >> skipMany spacenonewline)

View File

@ -306,6 +306,7 @@ tests_parseQueryTerm = [
"real:1" `gives` (Left $ Real True) "real:1" `gives` (Left $ Real True)
"date:2008" `gives` (Left $ Date $ DateSpan (Just $ parsedate "2008/01/01") (Just $ parsedate "2009/01/01")) "date:2008" `gives` (Left $ Date $ DateSpan (Just $ parsedate "2008/01/01") (Just $ parsedate "2009/01/01"))
"date:from 2012/5/17" `gives` (Left $ Date $ DateSpan (Just $ parsedate "2012/05/17") Nothing) "date:from 2012/5/17" `gives` (Left $ Date $ DateSpan (Just $ parsedate "2012/05/17") Nothing)
"date:20180101-201804" `gives` (Left $ Date $ DateSpan (Just $ parsedate "2018/01/01") (Just $ parsedate "2018/04/01"))
"inacct:a" `gives` (Right $ QueryOptInAcct "a") "inacct:a" `gives` (Right $ QueryOptInAcct "a")
"tag:a" `gives` (Left $ Tag "a" Nothing) "tag:a" `gives` (Left $ Tag "a" Nothing)
"tag:a=some value" `gives` (Left $ Tag "a" (Just "some value")) "tag:a=some value" `gives` (Left $ Tag "a" (Just "some value"))

View File

@ -493,49 +493,83 @@ Examples:
tab(@); tab(@);
l l. l l.
T{ T{
\f[C]2009/1/1\f[], \f[C]2009/01/01\f[], \f[C]2009\-1\-1\f[], \f[C]2004/10/1\f[], \f[C]2004\-01\-01\f[], \f[C]2004.9.1\f[]
\f[C]2009.1.1\f[]
T}@T{ T}@T{
simple dates, several separators allowed exact date, several separators allowed.
Year is 4+ digits, month is 1\-12, day is 1\-31
T} T}
T{ T{
\f[C]2009/1\f[], \f[C]2009\f[] \f[C]2004\f[]
T}@T{ T}@T{
same as above \- a missing day or month defaults to 1 start of year
T} T}
T{ T{
\f[C]1/1\f[], \f[C]january\f[], \f[C]jan\f[], \f[C]this\ year\f[] \f[C]2004/10\f[]
T}@T{ T}@T{
relative dates, meaning january 1 of the current year start of month
T} T}
T{ T{
\f[C]next\ year\f[] \f[C]10/1\f[]
T}@T{ T}@T{
january 1 of next year month and day in current year
T} T}
T{ T{
\f[C]this\ month\f[] \f[C]21\f[]
T}@T{ T}@T{
the 1st of the current month day in current month
T} T}
T{ T{
\f[C]this\ week\f[] \f[C]october,\ oct\f[]
T}@T{ T}@T{
the most recent monday start of month in current year
T} T}
T{ T{
\f[C]last\ week\f[] \f[C]yesterday,\ today,\ tomorrow\f[]
T}@T{ T}@T{
the monday of the week before this one \-1, 0, 1 days from today
T} T}
T{ T{
\f[C]lastweek\f[] \f[C]last/this/next\ day/week/month/quarter/year\f[]
T}@T{ T}@T{
spaces are optional \-1, 0, 1 periods from the current period
T} T}
T{ T{
\f[C]today\f[], \f[C]yesterday\f[], \f[C]tomorrow\f[] \f[C]20181201\f[]
T}@T{ T}@T{
8 digit YYYYMMDD with valid year month and day
T}
T{
\f[C]201812\f[]
T}@T{
6 digit YYYYMM with valid year and month
T}
.TE
.PP
Counterexamples \- malformed digit sequences might give surprising
results:
.PP
.TS
tab(@);
l l.
T{
\f[C]201813\f[]
T}@T{
6 digits with an invalid month is parsed as start of 6\-digit year
T}
T{
\f[C]20181301\f[]
T}@T{
8 digits with an invalid month is parsed as start of 8\-digit year
T}
T{
\f[C]20181232\f[]
T}@T{
8 digits with an invalid day gives an error
T}
T{
\f[C]201801012\f[]
T}@T{
9+ digits beginning with a valid YYYYMMDD gives an error
T} T}
.TE .TE
.SS Report start & end date .SS Report start & end date

View File

@ -390,15 +390,25 @@ omitted (defaulting to 1).
Examples: Examples:
'2009/1/1', '2009/01/01', '2009-1-1', '2009.1.1' simple dates, several separators allowed '2004/10/1', '2004-01-01', '2004.9.1' exact date, several separators allowed. Year is 4+ digits, month is 1-12, day is 1-31
'2009/1', '2009' same as above - a missing day or month defaults to 1 '2004' start of year
'1/1', 'january', 'jan', 'this year' relative dates, meaning january 1 of the current year '2004/10' start of month
'next year' january 1 of next year '10/1' month and day in current year
'this month' the 1st of the current month '21' day in current month
'this week' the most recent monday 'october, oct' start of month in current year
'last week' the monday of the week before this one 'yesterday, today, tomorrow' -1, 0, 1 days from today
'lastweek' spaces are optional 'last/this/next -1, 0, 1 periods from the current period
'today', 'yesterday', 'tomorrow' day/week/month/quarter/year'
'20181201' 8 digit YYYYMMDD with valid year month and day
'201812' 6 digit YYYYMM with valid year and month
Counterexamples - malformed digit sequences might give surprising
results:
'201813' 6 digits with an invalid month is parsed as start of 6-digit year
'20181301' 8 digits with an invalid month is parsed as start of 8-digit year
'20181232' 8 digits with an invalid day gives an error
'201801012' 9+ digits beginning with a valid YYYYMMDD gives an error
 
File: hledger.info, Node: Report start & end date, Next: Report intervals, Prev: Smart dates, Up: OPTIONS File: hledger.info, Node: Report start & end date, Next: Report intervals, Prev: Smart dates, Up: OPTIONS
@ -2411,117 +2421,117 @@ Node: Input files9632
Ref: #input-files9768 Ref: #input-files9768
Node: Smart dates11738 Node: Smart dates11738
Ref: #smart-dates11879 Ref: #smart-dates11879
Node: Report start & end date12858 Node: Report start & end date13285
Ref: #report-start-end-date13028 Ref: #report-start-end-date13455
Node: Report intervals14093 Node: Report intervals14520
Ref: #report-intervals14256 Ref: #report-intervals14683
Node: Period expressions14657 Node: Period expressions15084
Ref: #period-expressions14817 Ref: #period-expressions15244
Node: Depth limiting18774 Node: Depth limiting19201
Ref: #depth-limiting18918 Ref: #depth-limiting19345
Node: Pivoting19260 Node: Pivoting19687
Ref: #pivoting19378 Ref: #pivoting19805
Node: Cost21054 Node: Cost21481
Ref: #cost21162 Ref: #cost21589
Node: Market value21280 Node: Market value21707
Ref: #market-value21415 Ref: #market-value21842
Node: Combining -B and -V22598 Node: Combining -B and -V23025
Ref: #combining--b-and--v22761 Ref: #combining--b-and--v23188
Node: Output destination22908 Node: Output destination23335
Ref: #output-destination23070 Ref: #output-destination23497
Node: Output format23353 Node: Output format23780
Ref: #output-format23505 Ref: #output-format23932
Node: Regular expressions23890 Node: Regular expressions24317
Ref: #regular-expressions24027 Ref: #regular-expressions24454
Node: QUERIES25388 Node: QUERIES25815
Ref: #queries25490 Ref: #queries25917
Node: COMMANDS29457 Node: COMMANDS29884
Ref: #commands29569 Ref: #commands29996
Node: accounts30551 Node: accounts30978
Ref: #accounts30649 Ref: #accounts31076
Node: activity31895 Node: activity32322
Ref: #activity32005 Ref: #activity32432
Node: add32365 Node: add32792
Ref: #add32464 Ref: #add32891
Node: balance35125 Node: balance35552
Ref: #balance35236 Ref: #balance35663
Node: Flat mode38740 Node: Flat mode39167
Ref: #flat-mode38865 Ref: #flat-mode39292
Node: Depth limited balance reports39285 Node: Depth limited balance reports39712
Ref: #depth-limited-balance-reports39486 Ref: #depth-limited-balance-reports39913
Node: Multicolumn balance reports39906 Node: Multicolumn balance reports40333
Ref: #multicolumn-balance-reports40101 Ref: #multicolumn-balance-reports40528
Node: Budgets44790 Node: Budgets45217
Ref: #budgets44937 Ref: #budgets45364
Node: Custom balance output48906 Node: Custom balance output49333
Ref: #custom-balance-output49068 Ref: #custom-balance-output49495
Node: Colour support51234 Node: Colour support51661
Ref: #colour-support51366 Ref: #colour-support51793
Node: balancesheet51539 Node: balancesheet51966
Ref: #balancesheet51675 Ref: #balancesheet52102
Node: balancesheetequity53986 Node: balancesheetequity54413
Ref: #balancesheetequity54135 Ref: #balancesheetequity54562
Node: cashflow54672 Node: cashflow55099
Ref: #cashflow54800 Ref: #cashflow55227
Node: check-dates56923 Node: check-dates57350
Ref: #check-dates57050 Ref: #check-dates57477
Node: check-dupes57167 Node: check-dupes57594
Ref: #check-dupes57291 Ref: #check-dupes57718
Node: close57428 Node: close57855
Ref: #close57535 Ref: #close57962
Node: help57865 Node: help58292
Ref: #help57965 Ref: #help58392
Node: import59039 Node: import59466
Ref: #import59153 Ref: #import59580
Node: incomestatement59883 Node: incomestatement60310
Ref: #incomestatement60017 Ref: #incomestatement60444
Node: prices62421 Node: prices62848
Ref: #prices62536 Ref: #prices62963
Node: print62579 Node: print63006
Ref: #print62689 Ref: #print63116
Node: print-unique67583 Node: print-unique68010
Ref: #print-unique67709 Ref: #print-unique68136
Node: register67777 Node: register68204
Ref: #register67904 Ref: #register68331
Node: Custom register output72405 Node: Custom register output72832
Ref: #custom-register-output72534 Ref: #custom-register-output72961
Node: register-match73764 Node: register-match74191
Ref: #register-match73898 Ref: #register-match74325
Node: rewrite74081 Node: rewrite74508
Ref: #rewrite74198 Ref: #rewrite74625
Node: stats74267 Node: stats74694
Ref: #stats74370 Ref: #stats74797
Node: tags75240 Node: tags75667
Ref: #tags75338 Ref: #tags75765
Node: test75574 Node: test76001
Ref: #test75658 Ref: #test76085
Node: ADD-ON COMMANDS76026 Node: ADD-ON COMMANDS76453
Ref: #add-on-commands76136 Ref: #add-on-commands76563
Node: Official add-ons77423 Node: Official add-ons77850
Ref: #official-add-ons77563 Ref: #official-add-ons77990
Node: api77650 Node: api78077
Ref: #api77739 Ref: #api78166
Node: ui77791 Node: ui78218
Ref: #ui77890 Ref: #ui78317
Node: web77948 Node: web78375
Ref: #web78037 Ref: #web78464
Node: Third party add-ons78083 Node: Third party add-ons78510
Ref: #third-party-add-ons78258 Ref: #third-party-add-ons78685
Node: diff78393 Node: diff78820
Ref: #diff78490 Ref: #diff78917
Node: iadd78589 Node: iadd79016
Ref: #iadd78703 Ref: #iadd79130
Node: interest78786 Node: interest79213
Ref: #interest78907 Ref: #interest79334
Node: irr79002 Node: irr79429
Ref: #irr79100 Ref: #irr79527
Node: Experimental add-ons79178 Node: Experimental add-ons79605
Ref: #experimental-add-ons79330 Ref: #experimental-add-ons79757
Node: autosync79610 Node: autosync80037
Ref: #autosync79721 Ref: #autosync80148
Node: chart79960 Node: chart80387
Ref: #chart80079 Ref: #chart80506
Node: check80150 Node: check80577
Ref: #check80252 Ref: #check80679
 
End Tag Table End Tag Table

File diff suppressed because it is too large Load Diff

View File

@ -128,17 +128,27 @@ and can have less-significant date parts omitted (defaulting to 1).
Examples: Examples:
------------------------------------------------- ----------------------------------------------------------------------------- --------------------------------------------- -----------------------------------------------------------------------------
`2009/1/1`, `2009/01/01`, `2009-1-1`, `2009.1.1` simple dates, several separators allowed `2004/10/1`, `2004-01-01`, `2004.9.1` exact date, several separators allowed. Year is 4+ digits, month is 1-12, day is 1-31
`2009/1`, `2009` same as above - a missing day or month defaults to 1 `2004` start of year
`1/1`, `january`, `jan`, `this year` relative dates, meaning january 1 of the current year `2004/10` start of month
`next year` january 1 of next year `10/1` month and day in current year
`this month` the 1st of the current month `21` day in current month
`this week` the most recent monday `october, oct` start of month in current year
`last week` the monday of the week before this one `yesterday, today, tomorrow` -1, 0, 1 days from today
`lastweek` spaces are optional `last/this/next day/week/month/quarter/year` -1, 0, 1 periods from the current period
`today`, `yesterday`, `tomorrow` `20181201` 8 digit YYYYMMDD with valid year month and day
--- `201812` 6 digit YYYYMM with valid year and month
--------------------------------------------- -----------------------------------------------------------------------------
Counterexamples - malformed digit sequences might give surprising results:
--------------------------------------------- -----------------------------------------------------------------------------
`201813` 6 digits with an invalid month is parsed as start of 6-digit year
`20181301` 8 digits with an invalid month is parsed as start of 8-digit year
`20181232` 8 digits with an invalid day gives an error
`201801012` 9+ digits beginning with a valid YYYYMMDD gives an error
--------------------------------------------- -----------------------------------------------------------------------------
## Report start & end date ## Report start & end date