Skip to content

Respect year directive when sorting transactions#467

Open
jwiegley wants to merge 1 commit into
masterfrom
johnw/fix-1068
Open

Respect year directive when sorting transactions#467
jwiegley wants to merge 1 commit into
masterfrom
johnw/fix-1068

Conversation

@jwiegley

Copy link
Copy Markdown
Member

Summary

  • ledger-sort-startkey' now parses the date at point via ledger-iso-date-regexp' and correctly handles non-padded dates (e.g. `2015/5/7').
  • Dates written without a year component (e.g. 07/17') are interpreted relative to the most recent year NNNN' or `Y NNNN' directive preceding the transaction, looking through any active narrowing so sorting a sub-region still sees the enclosing directive. The current calendar year is used when no directive is in scope.
  • Previously such short dates fell through to (float-time nil)', which returns "now", so they sorted as today's date regardless of the surrounding year' context.

Fixes ledger/ledger#1068.

Test plan

  • make test in test/ passes (241 tests).
  • New ERT tests in test/sort-test.el cover:
    • `year NNNN' directive applied to year-less dates.
    • `Y NNNN' short form of the directive.
    • Non-padded dates (2015/5/7' sorts before 2015/5/10').
    • `year' directive above a narrowed sort region.

When sorting transactions, dates written without a year component (for
example `07/17` under a `year 2014' directive) were not parsed by
`ledger-parse-iso-date' -- it requires a four-digit year -- so they fell
through to `(float-time nil)', which returns the current time.  The
result was that short-dated transactions sorted as if they occurred
"today", regardless of their surrounding `year' context.

`ledger-sort-startkey' now inspects the date at point with
`ledger-iso-date-regexp' and, for bare month/day forms, consults the
most recent `year NNNN' or `Y NNNN' directive (looking through any
active narrowing) to compute the intended year.  If no directive is in
scope, the current calendar year is used -- matching Ledger's own
semantics for year-less dates.

Fixes ledger/ledger#1068.
Comment thread ledger-sort.el
(beginning-of-line)
(insert "\n; Ledger-mode: End sort\n\n"))

(defconst ledger-sort--year-directive-regex

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably go in ledger-regex.el for consistency.

Comment thread ledger-sort.el
"Regex matching a `year NNNN' or `Y NNNN' directive at the start of a line.
The year number is captured in group 1.")

(defun ledger-sort--preceding-year ()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This algorithm is very quadratic. On my personal ledger file with 13k transactions (3 MB file), if I convert it to use year directives instead of YYYY-MM-DD, ledger-sort-buffer takes 6-7 seconds just gathering sort keys.

Perhaps a better approach would be to, before sorting, scan once from the beginning of the buffer until the end of the sort region for year directives, and look up the year for a transaction based on its position in the buffer relative to the year directives found (which ought to be a pretty small number).

Comment thread ledger-sort.el
(let ((month (string-to-number (match-string 1)))
(day (string-to-number (match-string 2)))
(year (or (ledger-sort--preceding-year)
(nth 5 (decode-time)))))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems very strange for this function to depend on the actual current wall time. Is that compatible with the actual semantics of ledger here? (admittedly I do not personally use year directives)

Comment thread ledger-sort.el
(cond
((looking-at ledger-iso-date-regexp)
(ledger-parse-iso-date (match-string 0)))
((looking-at "\\([0-9]\\{1,2\\}\\)[-/]\\([0-9]\\{1,2\\}\\)\\(?:[^-/0-9]\\|$\\)")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is more-or-less equivalent to ledger-incomplete-date-regexp.

@bcc32 bcc32 linked an issue Jun 8, 2026 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Take ledger-default-date-format in to account for sorting ledger-sort-region and ledger-sort-buffer should actually parse dates (BZ#1068)

2 participants