Skip to content

Instantly share code, notes, and snippets.

@hsiuhsiu
Last active January 9, 2024 07:50
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hsiuhsiu/efdae5e2ae9333ce197a38e6a994eb1b to your computer and use it in GitHub Desktop.
Save hsiuhsiu/efdae5e2ae9333ce197a38e6a994eb1b to your computer and use it in GitHub Desktop.
sort bean count entries by time
"""Sort Beancount Entries by meta['time']
Although beancount says that time is meaningless, there are situations
where an account that should not be negative may have a negative balance
at some point because transfers that occurred on the same day are
scheduled later and spending records are scheduled earlier.
This is not a big deal, but it is a bit odd.
2021-10-01 balance Assets:Cash 500 USD
2021-10-01 *
Assets:Cash -800 USD
Expenses:Blah
2021-10-01 * "trans"
Assets:Cash 1000 USD
Assets:Bank:Blah
Then the balance of Assets:Cash after first transaction will be -300 USD.
Now you can add a meta_data named 'time' to fix it:
2021-10-01 *
time: "18:18:18"
Assets:Cash -800 USD
Expenses:Blah
2021-10-01 * "trans"
time: "12:12:12"
Assets:Cash 1000 USD
Assets:Bank:Blah
Then the cash balance changes will be: 500 -> 1500 -> 700
Just active as plugin in <your-main>.bean file.
"""
from beancount.core import data
from datetime import datetime
import pytz
def __parse_time(time_str):
# Define possible formats with floating-point seconds and optional timezone
formats = ['%H:%M:%S.%f%z', '%H:%M:%S.%f', '%H:%M:%S%z', '%H:%M:%S']
# Check for timezone and floating-point seconds
has_timezone = '+' in time_str or '-' in time_str
has_fraction = '.' in time_str
# Select the appropriate format
if has_timezone and has_fraction:
fmt = formats[0]
elif has_fraction:
fmt = formats[1]
elif has_timezone:
fmt = formats[2]
else:
fmt = formats[3]
dt = datetime.strptime(time_str, fmt)
# If the datetime object is not timezone-aware, attach the default timezone
if dt.tzinfo is None:
dt = pytz.UTC.localize(dt)
return dt
__original_entry_sortkey = data.entry_sortkey
__default_time_value = __parse_time('12:00:00')
def __entry_sortkey(entry):
original_result = __original_entry_sortkey(entry)
time_meta = entry.meta.get(
"time", None) if entry.meta is not None else None
time_value = __parse_time(
time_meta) if time_meta is not None else __default_time_value
return original_result[0], original_result[1], time_value, original_result[2]
def __posting_sortkey(entry):
if isinstance(entry, data.TxnPosting):
entry = entry.txn
return __entry_sortkey(entry)
data.entry_sortkey = __entry_sortkey
data.posting_sortkey = __posting_sortkey
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment