diff options
-rw-r--r-- | config.cc | 14 | ||||
-rw-r--r-- | config.h | 1 | ||||
-rw-r--r-- | datetime.cc | 129 | ||||
-rw-r--r-- | datetime.h | 28 | ||||
-rw-r--r-- | main.cc | 3 | ||||
-rw-r--r-- | main.py | 5 | ||||
-rw-r--r-- | pyledger.cc | 2 | ||||
-rw-r--r-- | valexpr.cc | 5 | ||||
-rw-r--r-- | walk.cc | 6 | ||||
-rw-r--r-- | walk.h | 12 |
10 files changed, 141 insertions, 64 deletions
@@ -42,7 +42,6 @@ config_t::config_t() download_quotes = false; use_cache = false; cache_dirty = false; - interval_begin = 0; } static void @@ -226,25 +225,22 @@ void config_t::process_options(const std::string& command, if (! report_interval && ! interval_text.empty()) { try { std::istringstream stream(interval_text); - std::time_t begin = -1, end = -1; - report_interval = interval_t::parse(stream, &begin, &end); - - if (begin != -1) { - interval_begin = begin; + report_interval.parse(stream); + if (report_interval.begin) { if (! predicate.empty()) predicate += "&"; char buf[32]; - std::sprintf(buf, "d>=%lu", begin); + std::sprintf(buf, "d>=%lu", report_interval.begin); predicate += buf; } - if (end != -1) { + if (report_interval.end) { if (! predicate.empty()) predicate += "&"; char buf[32]; - std::sprintf(buf, "d<%lu", end); + std::sprintf(buf, "d<%lu", report_interval.end); predicate += buf; } } @@ -55,7 +55,6 @@ struct config_t bool use_cache; bool cache_dirty; interval_t report_interval; - std::time_t interval_begin; format_t format; format_t nformat; diff --git a/datetime.cc b/datetime.cc index 7aa26b13..0d39d3b3 100644 --- a/datetime.cc +++ b/datetime.cc @@ -30,6 +30,30 @@ static const char * formats[] = { NULL }; +std::time_t interval_t::first(const std::time_t moment) +{ + std::time_t quant = begin; + + if (moment && std::difftime(moment, quant) > 0) { + if (! seconds) { + struct std::tm * desc = std::localtime(&moment); + if (years) + desc->tm_mon = 0; + desc->tm_mday = 1; + desc->tm_hour = 0; + desc->tm_min = 0; + desc->tm_sec = 0; + quant = std::mktime(desc); + } + + std::time_t temp; + while (std::difftime(moment, temp = increment(quant)) > 0) + quant = temp; + } + + return quant; +} + std::time_t interval_t::increment(const std::time_t moment) { std::time_t then = moment; @@ -93,15 +117,10 @@ static void parse_inclusion_specifier(const std::string& word, saw_year ? 1 : 0).increment(*begin); } -interval_t interval_t::parse(std::istream& in, - std::time_t * begin, - std::time_t * end) +void interval_t::parse(std::istream& in) { - unsigned long years = 0; - unsigned long months = 0; - unsigned long seconds = 0; - std::string word; + while (! in.eof()) { in >> word; if (word == "every") { @@ -165,57 +184,47 @@ interval_t interval_t::parse(std::istream& in, word = buf; } - parse_inclusion_specifier(word, begin, end); + parse_inclusion_specifier(word, &begin, &end); if (type == "last") { if (mon_spec) { - if (begin) - *begin = interval_t(0, -1, 0).increment(*begin); - if (end) - *end = interval_t(0, -1, 0).increment(*end); + begin = interval_t(0, -1, 0).increment(begin); + end = interval_t(0, -1, 0).increment(end); } else { - if (begin) - *begin = interval_t(0, 0, -1).increment(*begin); - if (end) - *end = interval_t(0, 0, -1).increment(*end); + begin = interval_t(0, 0, -1).increment(begin); + end = interval_t(0, 0, -1).increment(end); } } else if (type == "next") { if (mon_spec) { - if (begin) - *begin = interval_t(0, 1, 0).increment(*begin); - if (end) - *end = interval_t(0, 1, 0).increment(*end); + begin = interval_t(0, 1, 0).increment(begin); + end = interval_t(0, 1, 0).increment(end); } else { - if (begin) - *begin = interval_t(0, 0, 1).increment(*begin); - if (end) - *end = interval_t(0, 0, 1).increment(*end); + begin = interval_t(0, 0, 1).increment(begin); + end = interval_t(0, 0, 1).increment(end); } } } else if (word == "in") { in >> word; - parse_inclusion_specifier(word, begin, end); + parse_inclusion_specifier(word, &begin, &end); } else if (word == "from") { in >> word; - if (! parse_date(word.c_str(), begin)) + if (! parse_date(word.c_str(), &begin)) throw interval_expr_error("Could not parse 'from' date"); if (! in.eof()) in >> word; } else if (word == "to") { in >> word; - if (! parse_date(word.c_str(), end)) + if (! parse_date(word.c_str(), &end)) throw interval_expr_error("Could not parse 'to' date"); } else { - parse_inclusion_specifier(word, begin, end); + parse_inclusion_specifier(word, &begin, &end); } } - - return interval_t(seconds, months, years); } bool parse_date_mask(const char * date_str, struct std::tm * result) @@ -308,3 +317,63 @@ bool quick_parse_date(char * date_str, std::time_t * result) } } // namespace ledger + +#ifdef USE_BOOST_PYTHON + +#include <boost/python.hpp> + +using namespace boost::python; +using namespace ledger; + +unsigned int interval_len(interval_t& interval) +{ + int periods = 1; + std::time_t when = interval.first(); + while (interval.end && when < interval.end) { + when = interval.increment(when); + if (when < interval.end) + periods++; + } + return periods; +} + +std::time_t interval_getitem(interval_t& interval, int i) +{ + static std::time_t last_index = 0; + static std::time_t last_moment = 0; + + if (i == 0) { + last_index = 0; + last_moment = interval.first(); + } + else { + last_moment = interval.increment(last_moment); + if (interval.end && last_moment >= interval.end) { + PyErr_SetString(PyExc_IndexError, "Index out of range"); + throw_error_already_set(); + } + } + return last_moment; +} + +void export_datetime() +{ + class_< interval_t > + ("Interval", init<optional<int, int, int, std::time_t, std::time_t> >()) + .def(init<std::string>()) + .def(! self) + + .def_readwrite("years", &interval_t::years) + .def_readwrite("months", &interval_t::months) + .def_readwrite("seconds", &interval_t::seconds) + .def_readwrite("begin", &interval_t::begin) + .def_readwrite("end", &interval_t::end) + + .def("__len__", interval_len) + .def("__getitem__", interval_getitem) + + .def("increment", &interval_t::increment) + ; +} + +#endif // USE_BOOST_PYTHON @@ -4,18 +4,29 @@ #include "ledger.h" #include <ctime> +#include <sstream> namespace ledger { struct interval_t { - int years; - int months; - int seconds; - - interval_t(int _seconds = 0, int _months = 0, int _years = 0) - : years(_years), months(_months), seconds(_seconds) { + unsigned int years; + unsigned int months; + unsigned int seconds; + std::time_t begin; + std::time_t end; + + interval_t(int _seconds = 0, int _months = 0, int _years = 0, + std::time_t _begin = 0, std::time_t _end = 0) + : years(_years), months(_months), seconds(_seconds), + begin(_begin), end(_end) { + DEBUG_PRINT("ledger.memory.ctors", "ctor interval_t"); + } + interval_t(const std::string& desc) + : years(0), months(0), seconds(0), begin(0), end(0){ DEBUG_PRINT("ledger.memory.ctors", "ctor interval_t"); + std::istringstream stream(desc); + parse(stream); } #ifdef DEBUG_ENABLED ~interval_t() { @@ -27,11 +38,10 @@ struct interval_t return seconds > 0 || months > 0 || years > 0; } + std::time_t first(const std::time_t moment = 0); std::time_t increment(const std::time_t); - static interval_t parse(std::istream& in, - std::time_t * begin, - std::time_t * end); + void parse(std::istream& in); }; extern std::time_t now; @@ -202,8 +202,7 @@ chain_formatters(const std::string& command, else if (config.report_interval) ptrs.push_back(formatter = new interval_transactions(formatter, - config.report_interval, - config.interval_begin)); + config.report_interval)); else if (config.days_of_the_week) ptrs.push_back(formatter = new dow_transactions(formatter)); } @@ -1,5 +1,6 @@ import sys import os +import time from ledger import * @@ -29,3 +30,7 @@ handler = FilterTransactions (handler, "/Checking/") for entry in journal: for xact in entry: handler (xact) + +span = Interval ("monthly last year") +for date in span: + print time.strftime ("%c", time.localtime (date)) diff --git a/pyledger.cc b/pyledger.cc index 55cf0f3a..41910044 100644 --- a/pyledger.cc +++ b/pyledger.cc @@ -19,6 +19,7 @@ void export_gnucash(); void export_option(); void export_walk(); void export_format(); +void export_datetime(); BOOST_PYTHON_MODULE(ledger) { export_amount(); @@ -35,4 +36,5 @@ BOOST_PYTHON_MODULE(ledger) { export_option(); export_walk(); export_format(); + export_datetime(); } @@ -512,9 +512,8 @@ value_expr_t * parse_value_term(std::istream& in) node.reset(new value_expr_t(value_expr_t::CONSTANT_T)); - std::string datespec = buf; - std::istringstream stream(datespec); - interval_t::parse(stream, &node->constant_t, NULL); + interval_t timespan(buf); + node->constant_t = timespan.first(); break; } @@ -256,10 +256,10 @@ void subtotal_transactions::operator()(transaction_t& xact) void interval_transactions::operator()(transaction_t& xact) { - std::time_t quant = interval.increment(begin); + std::time_t quant = interval.increment(interval.begin); if (std::difftime(xact.entry->date, quant) > 0) { if (last_xact) { - start = begin; + start = interval.begin; finish = quant; flush(); } @@ -279,7 +279,7 @@ void interval_transactions::operator()(transaction_t& xact) while (std::difftime(xact.entry->date, temp = interval.increment(quant)) > 0) quant = temp; - begin = quant; + interval.begin = quant; } subtotal_transactions::operator()(xact); @@ -379,20 +379,18 @@ class subtotal_transactions : public item_handler<transaction_t> class interval_transactions : public subtotal_transactions { - std::time_t begin; interval_t interval; transaction_t * last_xact; public: interval_transactions(item_handler<transaction_t> * handler, - const interval_t& _interval, - const std::time_t _begin = 0) - : subtotal_transactions(handler), begin(_begin), - interval(_interval), last_xact(NULL) {} + const interval_t& _interval) + : subtotal_transactions(handler), interval(_interval), + last_xact(NULL) {} virtual ~interval_transactions() { - start = begin; - finish = interval.increment(begin); + start = interval.begin; + finish = interval.increment(interval.begin); } virtual void operator()(transaction_t& xact); |