diff options
-rw-r--r-- | amount.cc | 51 | ||||
-rw-r--r-- | amount.h | 51 | ||||
-rw-r--r-- | balance.cc | 12 | ||||
-rw-r--r-- | balance.h | 16 | ||||
-rw-r--r-- | binary.cc | 319 | ||||
-rw-r--r-- | config.cc | 19 | ||||
-rw-r--r-- | config.h | 4 | ||||
-rw-r--r-- | datetime.cc | 384 | ||||
-rw-r--r-- | datetime.h | 317 | ||||
-rw-r--r-- | debug.h | 9 | ||||
-rw-r--r-- | derive.cc | 6 | ||||
-rw-r--r-- | format.cc | 22 | ||||
-rw-r--r-- | gnucash.cc | 11 | ||||
-rw-r--r-- | journal.cc | 15 | ||||
-rw-r--r-- | journal.h | 37 | ||||
-rw-r--r-- | main.cc | 16 | ||||
-rw-r--r-- | option.cc | 48 | ||||
-rw-r--r-- | option.h | 4 | ||||
-rw-r--r-- | qif.cc | 3 | ||||
-rw-r--r-- | quotes.cc | 24 | ||||
-rw-r--r-- | quotes.h | 6 | ||||
-rw-r--r-- | reconcile.cc | 2 | ||||
-rw-r--r-- | reconcile.h | 7 | ||||
-rw-r--r-- | report.cc | 9 | ||||
-rw-r--r-- | report.h | 3 | ||||
-rw-r--r-- | textual.cc | 182 | ||||
-rw-r--r-- | valexpr.cc | 43 | ||||
-rw-r--r-- | valexpr.h | 6 | ||||
-rw-r--r-- | value.cc | 12 | ||||
-rw-r--r-- | value.h | 2 | ||||
-rw-r--r-- | walk.cc | 68 | ||||
-rw-r--r-- | walk.h | 24 | ||||
-rw-r--r-- | xml.cc | 41 |
33 files changed, 1026 insertions, 747 deletions
@@ -1,5 +1,4 @@ #include "amount.h" -#include "datetime.h" #include "util.h" #include <list> @@ -607,7 +606,7 @@ bool amount_t::realzero() const return mpz_sgn(MPZ(quantity)) == 0; } -amount_t amount_t::value(const std::time_t moment) const +amount_t amount_t::value(const datetime_t& moment) const { if (quantity) { amount_t amt(commodity().value(moment)); @@ -999,7 +998,7 @@ void parse_commodity(std::istream& in, std::string& symbol) } void parse_annotations(std::istream& in, amount_t& price, - std::time_t& date, std::string& tag) + datetime_t& date, std::string& tag) { do { char buf[256]; @@ -1036,7 +1035,7 @@ void parse_annotations(std::istream& in, amount_t& price, else throw new amount_error("Commodity date lacks closing bracket"); - parse_date(buf, &date); + date = buf; } else if (c == '(') { if (! tag.empty()) @@ -1073,7 +1072,7 @@ void amount_t::parse(std::istream& in, unsigned char flags) std::string symbol; std::string quant; amount_t price; - std::time_t date = 0; + datetime_t date; std::string tag; unsigned int comm_flags = COMMODITY_STYLE_DEFAULTS; bool negative = false; @@ -1378,7 +1377,7 @@ bool amount_t::valid() const } void amount_t::annotate_commodity(const amount_t& price, - const std::time_t date, + const datetime_t& date, const std::string& tag) { const commodity_t * this_base; @@ -1401,7 +1400,7 @@ void amount_t::annotate_commodity(const amount_t& price, commodity_t * ann_comm = annotated_commodity_t::find_or_create (*this_base, ! price && this_ann ? this_ann->price : price, - date == 0 && this_ann ? this_ann->date : date, + ! date && this_ann ? this_ann->date : date, tag.empty() && this_ann ? this_ann->tag : tag); if (ann_comm) set_commodity(*ann_comm); @@ -1435,7 +1434,8 @@ amount_t amount_t::strip_annotations(const bool _keep_price, { new_comm = annotated_commodity_t::find_or_create (*ann_comm.ptr, _keep_price ? ann_comm.price : amount_t(), - _keep_date ? ann_comm.date : 0, _keep_tag ? ann_comm.tag : ""); + _keep_date ? ann_comm.date : datetime_t(), + _keep_tag ? ann_comm.tag : ""); } else { new_comm = commodity_t::find_or_create(ann_comm.base_symbol()); } @@ -1461,7 +1461,7 @@ amount_t amount_t::price() const return *this; } -std::time_t amount_t::date() const +datetime_t amount_t::date() const { if (commodity_ && commodity_->annotated) { DEBUG_PRINT("amounts.commodities", @@ -1473,7 +1473,8 @@ std::time_t amount_t::date() const } -void commodity_base_t::add_price(const std::time_t date, const amount_t& price) +void commodity_base_t::add_price(const datetime_t& date, + const amount_t& price) { if (! history) history = new history_t; @@ -1488,7 +1489,7 @@ void commodity_base_t::add_price(const std::time_t date, const amount_t& price) } } -bool commodity_base_t::remove_price(const std::time_t date) +bool commodity_base_t::remove_price(const datetime_t& date) { if (history) { history_map::size_type n = history->prices.erase(date); @@ -1595,15 +1596,15 @@ commodity_t * commodity_t::find(const std::string& symbol) return NULL; } -amount_t commodity_base_t::value(const std::time_t moment) +amount_t commodity_base_t::value(const datetime_t& moment) { - std::time_t age = 0; - amount_t price; + datetime_t age; + amount_t price; if (history) { assert(history->prices.size() > 0); - if (moment == 0) { + if (! moment) { history_map::reverse_iterator r = history->prices.rbegin(); age = (*r).first; price = (*r).second; @@ -1615,7 +1616,7 @@ amount_t commodity_base_t::value(const std::time_t moment) price = (*r).second; } else { age = (*i).first; - if (std::difftime(moment, age) != 0) { + if (moment != age) { if (i != history->prices.begin()) { --i; age = (*i).first; @@ -1633,7 +1634,7 @@ amount_t commodity_base_t::value(const std::time_t moment) if (updater && ! (flags & COMMODITY_STYLE_NOMARKET)) (*updater)(*this, moment, age, (history && history->prices.size() > 0 ? - (*history->prices.rbegin()).first : 0), price); + (*history->prices.rbegin()).first : datetime_t()), price); return price; } @@ -1665,14 +1666,14 @@ bool annotated_commodity_t::operator==(const commodity_t& comm) const void annotated_commodity_t::write_annotations(std::ostream& out, const amount_t& price, - const std::time_t date, + const datetime_t& date, const std::string& tag) { if (price) out << " {" << price << '}'; if (date) - out << " [" << datetime_t(date) << ']'; + out << " [" << date << ']'; if (! tag.empty()) out << " (" << tag << ')'; @@ -1681,7 +1682,7 @@ annotated_commodity_t::write_annotations(std::ostream& out, commodity_t * annotated_commodity_t::create(const commodity_t& comm, const amount_t& price, - const std::time_t date, + const datetime_t& date, const std::string& tag, const std::string& mapping_key) { @@ -1719,7 +1720,7 @@ annotated_commodity_t::create(const commodity_t& comm, namespace { std::string make_qualified_name(const commodity_t& comm, const amount_t& price, - const std::time_t date, + const datetime_t& date, const std::string& tag) { if (price < 0) @@ -1745,7 +1746,7 @@ namespace { commodity_t * annotated_commodity_t::find_or_create(const commodity_t& comm, const amount_t& price, - const std::time_t date, + const datetime_t& date, const std::string& tag) { std::string name = make_qualified_name(comm, price, date, tag); @@ -1862,9 +1863,9 @@ struct commodity_updater_wrap : public commodity_base_t::updater_t commodity_updater_wrap(PyObject * self_) : self(self_) {} virtual void operator()(commodity_base_t& commodity, - const std::time_t moment, - const std::time_t date, - const std::time_t last, + const datetime_t& moment, + const datetime_t& date, + const datetime_t& last, amount_t& price) { call_method<void>(self, "__call__", commodity, moment, date, last, price); } @@ -4,13 +4,13 @@ #include <map> #include <stack> #include <string> -#include <ctime> #include <cctype> #include <iostream> #include <sstream> #include <cassert> #include <exception> +#include "datetime.h" #include "debug.h" #include "error.h" @@ -81,8 +81,8 @@ class amount_t void set_commodity(commodity_t& comm) { commodity_ = &comm; } - void annotate_commodity(const amount_t& price, - const std::time_t date = 0, + void annotate_commodity(const amount_t& price, + const datetime_t& date = datetime_t(), const std::string& tag = ""); amount_t strip_annotations(const bool _keep_price = keep_price, const bool _keep_date = keep_date, @@ -91,7 +91,7 @@ class amount_t commodity_ = NULL; } amount_t price() const; - std::time_t date() const; + datetime_t date() const; bool null() const { return ! quantity && ! commodity_; @@ -253,7 +253,7 @@ class amount_t return ! (*this == num); } - amount_t value(const std::time_t moment) const; + amount_t value(const datetime_t& moment) const; void abs() { if (*this < 0) @@ -297,7 +297,7 @@ class amount_t char * item_pool_end); friend void parse_annotations(std::istream& in, amount_t& price, - std::time_t& date, std::string& tag); + datetime_t& date, std::string& tag); }; unsigned int sizeof_bigint_t(); @@ -356,8 +356,8 @@ inline std::istream& operator>>(std::istream& in, amount_t& amt) { #define COMMODITY_STYLE_NOMARKET 0x0010 #define COMMODITY_STYLE_BUILTIN 0x0020 -typedef std::map<const std::time_t, amount_t> history_map; -typedef std::pair<const std::time_t, amount_t> history_pair; +typedef std::map<const datetime_t, amount_t> history_map; +typedef std::pair<const datetime_t, amount_t> history_pair; class commodity_base_t; @@ -403,24 +403,24 @@ class commodity_base_t struct history_t { history_map prices; - std::time_t last_lookup; - std::time_t bogus_time; + datetime_t last_lookup; + datetime_t bogus_time; history_t() : last_lookup(0), bogus_time(0) {} }; history_t * history; - void add_price(const std::time_t date, const amount_t& price); - bool remove_price(const std::time_t date); - amount_t value(const std::time_t moment = std::time(NULL)); + void add_price(const datetime_t& date, const amount_t& price); + bool remove_price(const datetime_t& date); + amount_t value(const datetime_t& moment = datetime_t::now); class updater_t { public: virtual ~updater_t() {} virtual void operator()(commodity_base_t& commodity, - const std::time_t moment, - const std::time_t date, - const std::time_t last, - amount_t& price) = 0; + const datetime_t& moment, + const datetime_t& date, + const datetime_t& last, + amount_t& price) = 0; }; friend class updater_t; @@ -543,13 +543,13 @@ class commodity_t return base->history; } - void add_price(const std::time_t date, const amount_t& price) { + void add_price(const datetime_t& date, const amount_t& price) { return base->add_price(date, price); } - bool remove_price(const std::time_t date) { + bool remove_price(const datetime_t& date) { return base->remove_price(date); } - amount_t value(const std::time_t moment = std::time(NULL)) const { + amount_t value(const datetime_t& moment = datetime_t::now) const { return base->value(moment); } @@ -562,7 +562,7 @@ class annotated_commodity_t : public commodity_t const commodity_t * ptr; amount_t price; - std::time_t date; + datetime_t date; std::string tag; explicit annotated_commodity_t() { @@ -577,25 +577,26 @@ class annotated_commodity_t : public commodity_t static void write_annotations(std::ostream& out, const amount_t& price, - const std::time_t date, + const datetime_t& date, const std::string& tag); private: static commodity_t * create(const commodity_t& comm, const amount_t& price, - const std::time_t date, + const datetime_t& date, const std::string& tag, const std::string& mapping_key); static commodity_t * find_or_create(const commodity_t& comm, const amount_t& price, - const std::time_t date, + const datetime_t& date, const std::string& tag); friend class amount_t; }; -inline std::ostream& operator<<(std::ostream& out, const commodity_t& comm) { +inline std::ostream& operator<<(std::ostream& out, + const commodity_t& comm) { out << comm.symbol(); return out; } @@ -33,7 +33,7 @@ amount_t balance_t::amount(const commodity_t& commodity) const return amount_t(); } -balance_t balance_t::value(const std::time_t moment) const +balance_t balance_t::value(const datetime_t& moment) const { balance_t temp; @@ -57,18 +57,18 @@ balance_t balance_t::price() const return temp; } -std::time_t balance_t::date() const +datetime_t balance_t::date() const { - std::time_t temp = 0; + datetime_t temp; for (amounts_map::const_iterator i = amounts.begin(); i != amounts.end(); i++) { - std::time_t date = (*i).second.date(); - if (temp == 0 && date != 0) + datetime_t date = (*i).second.date(); + if (! temp && date) temp = date; else if (temp != date) - return 0; + return datetime_t(); } return temp; @@ -2,10 +2,8 @@ #define _BALANCE_H #include "amount.h" -#include "datetime.h" #include <map> -#include <ctime> #include <iostream> namespace ledger { @@ -428,11 +426,11 @@ class balance_t return true; } - amount_t amount(const commodity_t& commodity = - *commodity_t::null_commodity) const; - balance_t value(const std::time_t moment = now) const; - balance_t price() const; - std::time_t date() const; + amount_t amount(const commodity_t& commodity = + *commodity_t::null_commodity) const; + balance_t value(const datetime_t& moment = datetime_t::now) const; + balance_t price() const; + datetime_t date() const; balance_t strip_annotations(const bool keep_price = amount_t::keep_price, @@ -870,13 +868,13 @@ class balance_pair_t *commodity_t::null_commodity) const { return quantity.amount(commodity); } - balance_t value(const std::time_t moment = now) const { + balance_t value(const datetime_t& moment = datetime_t::now) const { return quantity.value(moment); } balance_t price() const { return quantity.price(); } - std::time_t date() const { + datetime_t date() const { return quantity.date(); } @@ -3,7 +3,6 @@ #include "binary.h" #include <fstream> -#include <ctime> #include <sys/stat.h> #define TIMELOG_SUPPORT 1 @@ -12,9 +11,9 @@ namespace ledger { static unsigned long binary_magic_number = 0xFFEED765; #ifdef DEBUG_ENABLED -static unsigned long format_version = 0x00020609; +static unsigned long format_version = 0x0002060b; #else -static unsigned long format_version = 0x00020608; +static unsigned long format_version = 0x0002060a; #endif static account_t ** accounts; @@ -34,43 +33,83 @@ extern char * bigints_next; extern unsigned int bigints_index; extern unsigned int bigints_count; +template <typename T> +inline void read_binary_number_nocheck(std::istream& in, T& num) { + in.read((char *)&num, sizeof(num)); +} + +template <typename T> +inline T read_binary_number_nocheck(std::istream& in) { + T num; + read_binary_number_nocheck(in, num); + return num; +} + +template <typename T> +inline void read_binary_number_nocheck(char *& data, T& num) { + num = *((T *) data); + data += sizeof(T); +} + +template <typename T> +inline T read_binary_number_nocheck(char *& data) { + T num; + read_binary_number_nocheck(data, num); + return num; +} + #if DEBUG_LEVEL >= ALPHA -#define read_binary_guard(in, id) { \ - unsigned short guard; \ - in.read((char *)&guard, sizeof(guard)); \ - assert(guard == id); \ +static void assert_failed() { + assert(0); } +#define read_binary_guard(in, id) \ + if (read_binary_number_nocheck<unsigned short>(in) != id) \ + assert_failed(); #else #define read_binary_guard(in, id) #endif template <typename T> inline void read_binary_number(std::istream& in, T& num) { + read_binary_guard(in, 0x2003); in.read((char *)&num, sizeof(num)); + read_binary_guard(in, 0x2004); +} + +inline void read_binary_bool(std::istream& in, bool& num) { + read_binary_guard(in, 0x2005); + unsigned char val; + in.read((char *)&val, sizeof(val)); + num = val == 1; + read_binary_guard(in, 0x2006); } template <typename T> inline void read_binary_long(std::istream& in, T& num) { + read_binary_guard(in, 0x2001); + unsigned char len; - in.read((char *)&len, sizeof(unsigned char)); + read_binary_number_nocheck(in, len); num = 0; unsigned char temp; if (len > 3) { - in.read((char *)&temp, sizeof(unsigned char)); + read_binary_number_nocheck(in, temp); num |= ((unsigned long)temp) << 24; } if (len > 2) { - in.read((char *)&temp, sizeof(unsigned char)); + read_binary_number_nocheck(in, temp); num |= ((unsigned long)temp) << 16; } if (len > 1) { - in.read((char *)&temp, sizeof(unsigned char)); + read_binary_number_nocheck(in, temp); num |= ((unsigned long)temp) << 8; } - in.read((char *)&temp, sizeof(unsigned char)); + read_binary_number_nocheck(in, temp); num |= ((unsigned long)temp); + + read_binary_guard(in, 0x2002); } template <typename T> @@ -80,6 +119,12 @@ inline T read_binary_number(std::istream& in) { return num; } +inline bool read_binary_bool(std::istream& in) { + bool num; + read_binary_bool(in, num); + return num; +} + template <typename T> inline T read_binary_long(std::istream& in) { T num; @@ -92,10 +137,10 @@ inline void read_binary_string(std::istream& in, std::string& str) read_binary_guard(in, 0x3001); unsigned char len; - read_binary_number(in, len); + read_binary_number_nocheck(in, len); if (len == 0xff) { unsigned short slen; - read_binary_number(in, slen); + read_binary_number_nocheck(in, slen); char * buf = new char[slen + 1]; in.read(buf, slen); buf[slen] = '\0'; @@ -114,8 +159,7 @@ inline void read_binary_string(std::istream& in, std::string& str) read_binary_guard(in, 0x3002); } -inline std::string read_binary_string(std::istream& in) -{ +inline std::string read_binary_string(std::istream& in) { std::string temp; read_binary_string(in, temp); return temp; @@ -123,31 +167,46 @@ inline std::string read_binary_string(std::istream& in) template <typename T> inline void read_binary_number(char *& data, T& num) { + read_binary_guard(data, 0x2003); num = *((T *) data); data += sizeof(T); + read_binary_guard(data, 0x2004); +} + +inline void read_binary_bool(char *& data, bool& num) { + read_binary_guard(data, 0x2005); + unsigned char val = *((unsigned char *) data); + data += sizeof(unsigned char); + num = val == 1; + read_binary_guard(data, 0x2006); } template <typename T> inline void read_binary_long(char *& data, T& num) { - unsigned char len = *((unsigned char *)data++); + read_binary_guard(data, 0x2001); + + unsigned char len; + read_binary_number_nocheck(data, len); num = 0; unsigned char temp; if (len > 3) { - temp = *((unsigned char *)data++); + read_binary_number_nocheck(data, temp); num |= ((unsigned long)temp) << 24; } if (len > 2) { - temp = *((unsigned char *)data++); + read_binary_number_nocheck(data, temp); num |= ((unsigned long)temp) << 16; } if (len > 1) { - temp = *((unsigned char *)data++); + read_binary_number_nocheck(data, temp); num |= ((unsigned long)temp) << 8; } - temp = *((unsigned char *)data++); + read_binary_number_nocheck(data, temp); num |= ((unsigned long)temp); + + read_binary_guard(data, 0x2002); } template <typename T> @@ -157,6 +216,12 @@ inline T read_binary_number(char *& data) { return num; } +inline bool read_binary_bool(char *& data) { + bool num; + read_binary_bool(data, num); + return num; +} + template <typename T> inline T read_binary_long(char *& data) { T num; @@ -166,18 +231,15 @@ inline T read_binary_long(char *& data) { inline void read_binary_string(char *& data, std::string& str) { -#if DEBUG_LEVEL >= ALPHA - unsigned short guard; - guard = *((unsigned short *) data); - data += sizeof(unsigned short); - assert(guard == 0x3001); -#endif + read_binary_guard(data, 0x3001); - unsigned char len = *data++; + unsigned char len; + read_binary_number_nocheck(data, len); if (len == 0xff) { - unsigned short slen = *((unsigned short *) data); - str = std::string(data + sizeof(unsigned short), slen); - data += sizeof(unsigned short) + slen; + unsigned short slen; + read_binary_number_nocheck(data, slen); + str = std::string(data, slen); + data += slen; } else if (len) { str = std::string(data, len); @@ -187,11 +249,7 @@ inline void read_binary_string(char *& data, std::string& str) str = ""; } -#if DEBUG_LEVEL >= ALPHA - guard = *((unsigned short *) data); - data += sizeof(unsigned short); - assert(guard == 0x3002); -#endif + read_binary_guard(data, 0x3002); } inline std::string read_binary_string(char *& data) @@ -203,18 +261,15 @@ inline std::string read_binary_string(char *& data) inline void read_binary_string(char *& data, std::string * str) { -#if DEBUG_LEVEL >= ALPHA - unsigned short guard; - guard = *((unsigned short *) data); - data += sizeof(unsigned short); - assert(guard == 0x3001); -#endif + read_binary_guard(data, 0x3001); - unsigned char len = *data++; + unsigned char len; + read_binary_number_nocheck(data, len); if (len == 0xff) { - unsigned short slen = *((unsigned short *) data); - new(str) std::string(data + sizeof(unsigned short), slen); - data += sizeof(unsigned short) + slen; + unsigned short slen; + read_binary_number_nocheck(data, slen); + new(str) std::string(data, slen); + data += slen; } else if (len) { new(str) std::string(data, len); @@ -224,11 +279,7 @@ inline void read_binary_string(char *& data, std::string * str) new(str) std::string(""); } -#if DEBUG_LEVEL >= ALPHA - guard = *((unsigned short *) data); - data += sizeof(unsigned short); - assert(guard == 0x3002); -#endif + read_binary_guard(data, 0x3002); } inline void read_binary_amount(char *& data, amount_t& amt) @@ -251,13 +302,13 @@ inline void read_binary_value(char *& data, value_t& val) switch (val.type) { case value_t::BOOLEAN: - *((bool *) val.data) = read_binary_number<char>(data) == 1; + read_binary_bool(data, *((bool *) val.data)); break; case value_t::INTEGER: read_binary_long(data, *((long *) val.data)); break; case value_t::DATETIME: - read_binary_number(data, ((datetime_t *) val.data)->when); + read_binary_number(data, *((datetime_t *) val.data)); break; case value_t::AMOUNT: read_binary_amount(data, *((amount_t *) val.data)); @@ -283,7 +334,7 @@ inline void read_binary_mask(char *& data, mask_t *& mask) inline void read_binary_value_expr(char *& data, value_expr_t *& expr) { - if (read_binary_number<unsigned char>(data) == 0) { + if (! read_binary_bool(data)) { expr = NULL; return; } @@ -314,7 +365,7 @@ inline void read_binary_value_expr(char *& data, value_expr_t *& expr) case value_expr_t::F_ACCOUNT_MASK: case value_expr_t::F_SHORT_ACCOUNT_MASK: case value_expr_t::F_COMMODITY_MASK: - if (read_binary_number<unsigned char>(data) == 1) + if (read_binary_bool(data)) read_binary_mask(data, expr->mask); break; @@ -330,11 +381,11 @@ inline void read_binary_value_expr(char *& data, value_expr_t *& expr) inline void read_binary_transaction(char *& data, transaction_t * xact) { - read_binary_long(data, xact->_date); - read_binary_long(data, xact->_date_eff); + read_binary_number(data, xact->_date); + read_binary_number(data, xact->_date_eff); xact->account = accounts[read_binary_long<account_t::ident_t>(data) - 1]; - char flag = read_binary_number<char>(data); + unsigned char flag = read_binary_number<unsigned char>(data); if (flag == 0) { read_binary_amount(data, xact->amount); } @@ -350,7 +401,7 @@ inline void read_binary_transaction(char *& data, transaction_t * xact) read_binary_string(data, xact->amount_expr.expr); } - if (*data++ == 1) { + if (read_binary_bool(data)) { xact->cost = new amount_t; read_binary_amount(data, *xact->cost); read_binary_string(data, xact->cost_expr); @@ -383,7 +434,7 @@ inline void read_binary_entry_base(char *& data, entry_base_t * entry, entry->end_pos = read_binary_long<unsigned long>(data); read_binary_long(data, entry->end_line); - bool ignore_calculated = read_binary_number<char>(data) == 1; + bool ignore_calculated = read_binary_bool(data); for (unsigned long i = 0, count = read_binary_long<unsigned long>(data); i < count; @@ -400,8 +451,8 @@ inline void read_binary_entry(char *& data, entry_t * entry, transaction_t *& xact_pool, bool& finalize) { read_binary_entry_base(data, entry, xact_pool, finalize); - read_binary_long(data, entry->_date); - read_binary_long(data, entry->_date_eff); + read_binary_number(data, entry->_date); + read_binary_number(data, entry->_date_eff); read_binary_string(data, &entry->code); read_binary_string(data, &entry->payee); } @@ -449,8 +500,8 @@ inline void read_binary_commodity_base_extra(char *& data, for (unsigned long i = 0, count = read_binary_long<unsigned long>(data); i < count; i++) { - std::time_t when; - read_binary_long(data, when); + datetime_t when; + read_binary_number(data, when); amount_t amt; read_binary_amount(data, amt); @@ -460,22 +511,19 @@ inline void read_binary_commodity_base_extra(char *& data, if (! commodity->history) commodity->history = new commodity_base_t::history_t; commodity->history->prices.insert(history_pair(when, amt)); + read_history = true; } if (read_history) - read_binary_long(data, commodity->history->last_lookup); + read_binary_number(data, commodity->history->last_lookup); - unsigned char flag; - - flag = read_binary_number<unsigned char>(data); - if (flag) { + if (read_binary_bool(data)) { amount_t amt; read_binary_amount(data, amt); commodity->smaller = new amount_t(amt); } - flag = read_binary_number<unsigned char>(data); - if (flag) { + if (read_binary_bool(data)) { amount_t amt; read_binary_amount(data, amt); commodity->larger = new amount_t(amt); @@ -517,7 +565,7 @@ inline commodity_t * read_binary_commodity_annotated(char *& data) read_binary_amount(data, amt); commodity->price = amt; - read_binary_long(data, commodity->date); + read_binary_number(data, commodity->date); read_binary_string(data, commodity->tag); return commodity; @@ -584,7 +632,7 @@ unsigned int read_binary_journal(std::istream& in, i++) { std::string path = read_binary_string(in); std::time_t old_mtime; - read_binary_long(in, old_mtime); + read_binary_number(in, old_mtime); struct stat info; stat(path.c_str(), &info); if (std::difftime(info.st_mtime, old_mtime) > 0) @@ -618,7 +666,7 @@ unsigned int read_binary_journal(std::istream& in, delete journal->master; journal->master = read_binary_account(data, journal, master); - if (read_binary_number<bool>(data)) + if (read_binary_bool(data)) journal->basket = accounts[read_binary_long<account_t::ident_t>(data) - 1]; // Allocate the memory needed for the entries and transactions in @@ -693,7 +741,7 @@ unsigned int read_binary_journal(std::istream& in, commodity_t * commodity; std::string mapping_key; - if (read_binary_number<char>(data) == 0) { + if (! read_binary_bool(data)) { commodity = read_binary_commodity(data); mapping_key = commodity->base->symbol; } else { @@ -768,8 +816,8 @@ unsigned int read_binary_journal(std::istream& in, bool binary_parser_t::test(std::istream& in) const { - if (read_binary_number<unsigned long>(in) == binary_magic_number && - read_binary_number<unsigned long>(in) == format_version) + if (read_binary_number_nocheck<unsigned long>(in) == binary_magic_number && + read_binary_number_nocheck<unsigned long>(in) == format_version) return true; in.clear(); @@ -787,22 +835,36 @@ unsigned int binary_parser_t::parse(std::istream& in, journal, master); } -#if DEBUG_LEVEL >= ALPHA -#define write_binary_guard(in, id) { \ - unsigned short guard = id; \ - out.write((char *)&guard, sizeof(guard)); \ +template <typename T> +inline void write_binary_number_nocheck(std::ostream& out, T num) { + out.write((char *)&num, sizeof(num)); } + +#if DEBUG_LEVEL >= ALPHA +#define write_binary_guard(out, id) \ + write_binary_number_nocheck<unsigned short>(out, id) #else #define write_binary_guard(in, id) #endif template <typename T> inline void write_binary_number(std::ostream& out, T num) { + write_binary_guard(out, 0x2003); out.write((char *)&num, sizeof(num)); + write_binary_guard(out, 0x2004); +} + +inline void write_binary_bool(std::ostream& out, bool num) { + write_binary_guard(out, 0x2005); + unsigned char val = num ? 1 : 0; + out.write((char *)&val, sizeof(val)); + write_binary_guard(out, 0x2006); } template <typename T> inline void write_binary_long(std::ostream& out, T num) { + write_binary_guard(out, 0x2001); + unsigned char len = 4; if (((unsigned long)num) < 0x00000100UL) len = 1; @@ -810,23 +872,26 @@ inline void write_binary_long(std::ostream& out, T num) { len = 2; else if (((unsigned long)num) < 0x01000000UL) len = 3; - out.write((char *)&len, sizeof(unsigned char)); + write_binary_number_nocheck<unsigned char>(out, len); + unsigned char temp; if (len > 3) { - unsigned char temp = (((unsigned long)num) & 0xFF000000UL) >> 24; - out.write((char *)&temp, sizeof(unsigned char)); + temp = (((unsigned long)num) & 0xFF000000UL) >> 24; + write_binary_number_nocheck(out, temp); } if (len > 2) { - unsigned char temp = (((unsigned long)num) & 0x00FF0000UL) >> 16; - out.write((char *)&temp, sizeof(unsigned char)); + temp = (((unsigned long)num) & 0x00FF0000UL) >> 16; + write_binary_number_nocheck(out, temp); } if (len > 1) { - unsigned char temp = (((unsigned long)num) & 0x0000FF00UL) >> 8; - out.write((char *)&temp, sizeof(unsigned char)); + temp = (((unsigned long)num) & 0x0000FF00UL) >> 8; + write_binary_number_nocheck(out, temp); } - unsigned char temp = (((unsigned long)num) & 0x000000FFUL); - out.write((char *)&temp, sizeof(unsigned char)); + temp = (((unsigned long)num) & 0x000000FFUL); + write_binary_number_nocheck(out, temp); + + write_binary_guard(out, 0x2002); } inline void write_binary_string(std::ostream& out, const std::string& str) @@ -836,10 +901,10 @@ inline void write_binary_string(std::ostream& out, const std::string& str) unsigned long len = str.length(); if (len > 255) { assert(len < 65536); - write_binary_number<unsigned char>(out, 0xff); - write_binary_number<unsigned short>(out, len); + write_binary_number_nocheck<unsigned char>(out, 0xff); + write_binary_number_nocheck<unsigned short>(out, len); } else { - write_binary_number<unsigned char>(out, len); + write_binary_number_nocheck<unsigned char>(out, len); } if (len) @@ -864,13 +929,13 @@ void write_binary_value(std::ostream& out, const value_t& val) switch (val.type) { case value_t::BOOLEAN: - write_binary_number<char>(out, *((bool *) val.data) ? 1 : 0); + write_binary_bool(out, *((bool *) val.data)); break; case value_t::INTEGER: write_binary_long(out, *((long *) val.data)); break; case value_t::DATETIME: - write_binary_number(out, ((datetime_t *) val.data)->when); + write_binary_number(out, *((datetime_t *) val.data)); break; case value_t::AMOUNT: write_binary_amount(out, *((amount_t *) val.data)); @@ -891,11 +956,10 @@ void write_binary_mask(std::ostream& out, mask_t * mask) void write_binary_value_expr(std::ostream& out, const value_expr_t * expr) { if (! expr) { - write_binary_number<unsigned char>(out, 0); + write_binary_bool(out, false); return; } - write_binary_number<unsigned char>(out, 1); - + write_binary_bool(out, true); write_binary_number(out, expr->kind); if (expr->kind > value_expr_t::TERMINALS) @@ -917,10 +981,10 @@ void write_binary_value_expr(std::ostream& out, const value_expr_t * expr) case value_expr_t::F_SHORT_ACCOUNT_MASK: case value_expr_t::F_COMMODITY_MASK: if (expr->mask) { - write_binary_number<char>(out, 1); + write_binary_bool(out, true); write_binary_mask(out, expr->mask); } else { - write_binary_number<char>(out, 0); + write_binary_bool(out, false); } break; @@ -935,36 +999,36 @@ void write_binary_value_expr(std::ostream& out, const value_expr_t * expr) void write_binary_transaction(std::ostream& out, transaction_t * xact, bool ignore_calculated) { - write_binary_long(out, xact->_date); - write_binary_long(out, xact->_date_eff); + write_binary_number(out, xact->_date); + write_binary_number(out, xact->_date_eff); write_binary_long(out, xact->account->ident); if (ignore_calculated && xact->flags & TRANSACTION_CALCULATED) { - write_binary_number<char>(out, 0); + write_binary_number<unsigned char>(out, 0); write_binary_amount(out, amount_t()); } else if (xact->amount_expr) { - write_binary_number<char>(out, 2); + write_binary_number<unsigned char>(out, 2); write_binary_value_expr(out, xact->amount_expr.get()); write_binary_string(out, xact->amount_expr.expr); } else if (! xact->amount_expr.expr.empty()) { - write_binary_number<char>(out, 1); + write_binary_number<unsigned char>(out, 1); write_binary_amount(out, xact->amount); write_binary_string(out, xact->amount_expr.expr); } else { - write_binary_number<char>(out, 0); + write_binary_number<unsigned char>(out, 0); write_binary_amount(out, xact->amount); } if (xact->cost && (! (ignore_calculated && xact->flags & TRANSACTION_CALCULATED))) { - write_binary_number<char>(out, 1); + write_binary_bool(out, true); write_binary_amount(out, *xact->cost); write_binary_string(out, xact->cost_expr); } else { - write_binary_number<char>(out, 0); + write_binary_bool(out, false); } write_binary_number(out, xact->state); @@ -994,7 +1058,7 @@ void write_binary_entry_base(std::ostream& out, entry_base_t * entry) break; } - write_binary_number<char>(out, ignore_calculated ? 1 : 0); + write_binary_bool(out, ignore_calculated); write_binary_long(out, entry->transactions.size()); for (transactions_list::const_iterator i = entry->transactions.begin(); @@ -1006,8 +1070,8 @@ void write_binary_entry_base(std::ostream& out, entry_base_t * entry) void write_binary_entry(std::ostream& out, entry_t * entry) { write_binary_entry_base(out, entry); - write_binary_long(out, entry->_date); - write_binary_long(out, entry->_date_eff); + write_binary_number(out, entry->_date); + write_binary_number(out, entry->_date_eff); write_binary_string(out, entry->code); write_binary_string(out, entry->payee); } @@ -1048,24 +1112,24 @@ void write_binary_commodity_base_extra(std::ostream& out, for (history_map::const_iterator i = commodity->history->prices.begin(); i != commodity->history->prices.end(); i++) { - write_binary_long(out, (*i).first); + write_binary_number(out, (*i).first); write_binary_amount(out, (*i).second); } - write_binary_long(out, commodity->history->last_lookup); + write_binary_number(out, commodity->history->last_lookup); } if (commodity->smaller) { - write_binary_number<unsigned char>(out, 1); + write_binary_bool(out, true); write_binary_amount(out, *commodity->smaller); } else { - write_binary_number<unsigned char>(out, 0); + write_binary_bool(out, false); } if (commodity->larger) { - write_binary_number<unsigned char>(out, 1); + write_binary_bool(out, true); write_binary_amount(out, *commodity->larger); } else { - write_binary_number<unsigned char>(out, 0); + write_binary_bool(out, false); } } @@ -1090,7 +1154,7 @@ void write_binary_commodity_annotated(std::ostream& out, write_binary_long(out, ann_comm->base->ident); write_binary_amount(out, ann_comm->price); - write_binary_long(out, ann_comm->date); + write_binary_number(out, ann_comm->date); write_binary_string(out, ann_comm->tag); } @@ -1132,13 +1196,13 @@ void write_binary_journal(std::ostream& out, journal_t * journal) base_commodity_index = commodity_index = 0; - write_binary_number(out, binary_magic_number); - write_binary_number(out, format_version); + write_binary_number_nocheck(out, binary_magic_number); + write_binary_number_nocheck(out, format_version); // Write out the files that participated in this journal, so that // they can be checked for changes on reading. - if (journal->sources.size() == 0) { + if (journal->sources.empty()) { write_binary_number<unsigned short>(out, 0); } else { write_binary_number<unsigned short>(out, journal->sources.size()); @@ -1148,7 +1212,7 @@ void write_binary_journal(std::ostream& out, journal_t * journal) write_binary_string(out, *i); struct stat info; stat((*i).c_str(), &info); - write_binary_long(out, std::time_t(info.st_mtime)); + write_binary_number(out, std::time_t(info.st_mtime)); } // Write out the price database that relates to this data file, so @@ -1165,10 +1229,10 @@ void write_binary_journal(std::ostream& out, journal_t * journal) write_binary_account(out, journal->master); if (journal->basket) { - write_binary_number<bool>(out, true); + write_binary_bool(out, true); write_binary_long(out, journal->basket->ident); } else { - write_binary_number<bool>(out, false); + write_binary_bool(out, false); } // Write out the number of entries, transactions, and amounts @@ -1178,10 +1242,11 @@ void write_binary_journal(std::ostream& out, journal_t * journal) write_binary_long<unsigned long>(out, journal->period_entries.size()); ostream_pos_type xacts_val = out.tellp(); - write_binary_number<unsigned long>(out, 0); + ostream_pos_type bigints_val = out.tellp(); write_binary_number<unsigned long>(out, 0); + bigints_count = 0; // Write out the commodities @@ -1202,7 +1267,7 @@ void write_binary_journal(std::ostream& out, journal_t * journal) i != commodity_t::commodities.end(); i++) { if (! (*i).second->annotated) { - write_binary_number<char>(out, 0); + write_binary_bool(out, false); write_binary_commodity(out, (*i).second); } } @@ -1211,7 +1276,7 @@ void write_binary_journal(std::ostream& out, journal_t * journal) i != commodity_t::commodities.end(); i++) { if ((*i).second->annotated) { - write_binary_number<char>(out, 1); + write_binary_bool(out, true); write_binary_string(out, (*i).first); // the mapping key write_binary_commodity_annotated(out, (*i).second); } @@ -7,7 +7,6 @@ #include "walk.h" #include <fstream> -#include <ctime> #include <cstdlib> #ifdef WIN32 #include <io.h> @@ -96,12 +95,14 @@ config_t::config_t() prices_format = "%[%Y/%m/%d %H:%M:%S %Z] %-10A %12t %12T\n"; pricesdb_format = "P %[%Y/%m/%d %H:%M:%S] %A %t\n"; - download_quotes = false; - use_cache = false; - cache_dirty = false; - debug_mode = false; - verbose_mode = false; - trace_mode = false; + pricing_leeway = 24 * 3600; + + download_quotes = false; + use_cache = false; + cache_dirty = false; + debug_mode = false; + verbose_mode = false; + trace_mode = false; } ////////////////////////////////////////////////////////////////////// @@ -109,9 +110,7 @@ config_t::config_t() void trace(const std::string& cat, const std::string& str) { char buf[32]; - std::time_t now = std::time(NULL); - std::strftime(buf, 31, "%H:%M:%S", std::localtime(&now)); - + std::strftime(buf, 31, "%H:%M:%S", datetime_t::now.localtime()); std::cerr << buf << " " << cat << ": " << str << std::endl; } @@ -31,9 +31,13 @@ class config_t std::string prices_format; std::string pricesdb_format; + std::string date_input_format; + std::string account; std::string pager; + unsigned long pricing_leeway; + bool download_quotes; bool use_cache; bool cache_dirty; diff --git a/datetime.cc b/datetime.cc index 74efce89..d8668cb8 100644 --- a/datetime.cc +++ b/datetime.cc @@ -7,19 +7,12 @@ #include <ctime> #include <cctype> -std::time_t now = std::time(NULL); -int now_year = std::localtime(&now)->tm_year; +date_t date_t::now(std::time(NULL)); +int date_t::current_year = date_t::now.year(); +std::string date_t::input_format; +std::string date_t::output_format = "%Y/%m/%d"; -static std::time_t base = -1; -static int base_year = -1; - -static const int month_days[12] = { - 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 -}; - -char input_format[128]; - -const char * formats[] = { +const char * date_t::formats[] = { "%Y/%m/%d", "%m/%d", "%Y.%m.%d", @@ -34,29 +27,70 @@ const char * formats[] = { NULL }; -std::string datetime_t::date_format = "%Y/%m/%d"; +datetime_t datetime_t::now(std::time(NULL)); + +namespace { + static std::time_t base = -1; + static int base_year = -1; + + static const int month_days[12] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + + bool parse_date_mask(const char * date_str, struct std::tm * result); + bool parse_date(const char * date_str, std::time_t * result, + const int year = -1); + bool quick_parse_date(const char * date_str, std::time_t * result); +} + +date_t::date_t(const std::string& _when) +{ + if (! quick_parse_date(_when.c_str(), &when)) + throw new date_error + (std::string("Invalid date string: ") + _when); +} -std::time_t interval_t::first(const std::time_t moment) const +datetime_t::datetime_t(const std::string& _when) { - std::time_t quant = begin; + if (const char * p = std::strchr(_when.c_str(), ' ')) { + date_t date(std::string(_when, 0, p - _when.c_str())); - if (moment && std::difftime(moment, quant) > 0) { + struct std::tm moment = *std::localtime(&date.when); + if (! strptime(++p, "%H:%M:%S", &moment)) + throw new datetime_error + (std::string("Invalid date/time string: ") + _when); + + when = std::mktime(&moment); + } else { + when = date_t(_when).when; + } +} + +datetime_t interval_t::first(const datetime_t& moment) const +{ + datetime_t quant(begin); + + if (moment && moment > quant) { // Find an efficient starting point for the upcoming while loop. // We want a date early enough that the range will be correct, but // late enough that we don't spend hundreds of thousands of loops // skipping through time. - struct std::tm * desc = std::localtime(&moment); + struct std::tm * desc = std::localtime(&moment.when); + if (years) desc->tm_mon = 0; - desc->tm_mday = 1; - desc->tm_hour = 0; - desc->tm_min = 0; - desc->tm_sec = 0; + desc->tm_mday = 1; + + desc->tm_hour = 0; + desc->tm_min = 0; + desc->tm_sec = 0; + desc->tm_isdst = -1; + quant = std::mktime(desc); - std::time_t temp; - while (std::difftime(moment, temp = increment(quant)) >= 0) { + datetime_t temp; + while (moment >= (temp = increment(quant))) { if (quant == temp) break; quant = temp; @@ -66,145 +100,134 @@ std::time_t interval_t::first(const std::time_t moment) const return quant; } -std::time_t interval_t::increment(const std::time_t moment) const +datetime_t interval_t::increment(const datetime_t& moment) const { - std::time_t then = moment; + struct std::tm * desc = std::localtime(&moment.when); - if (years || months) { - struct std::tm * desc = std::localtime(&then); + if (years) + desc->tm_year += years; + if (months) + desc->tm_mon += months; + if (days) + desc->tm_mon += days; - if (years) - desc->tm_year += years; + desc->tm_hour += hours; + desc->tm_min += minutes; + desc->tm_sec += seconds; - if (months) { - desc->tm_mon += months; + desc->tm_isdst = -1; - if (desc->tm_mon > 11) { - desc->tm_year++; - desc->tm_mon -= 12; - } - else if (desc->tm_mon < 0) { - desc->tm_year--; - desc->tm_mon += 12; - } - } - - desc->tm_hour = 0; - desc->tm_min = 0; - desc->tm_sec = 0; - desc->tm_isdst = 0; - - then = std::mktime(desc); - } - - return then + seconds; + return std::mktime(desc); } -static void parse_inclusion_specifier(const std::string& word, - std::time_t * begin, - std::time_t * end) -{ - struct std::tm when; +namespace { + void parse_inclusion_specifier(const std::string& word, + datetime_t * begin, datetime_t * end) + { + struct std::tm when; - if (! parse_date_mask(word.c_str(), &when)) - throw new datetime_error(std::string("Could not parse date mask: ") + word); + if (! parse_date_mask(word.c_str(), &when)) + throw new datetime_error(std::string("Could not parse date mask: ") + word); - when.tm_hour = 0; - when.tm_min = 0; - when.tm_sec = 0; + when.tm_hour = 0; + when.tm_min = 0; + when.tm_sec = 0; + when.tm_isdst = -1; - bool saw_year = true; - bool saw_mon = true; - bool saw_day = true; + bool saw_year = true; + bool saw_mon = true; + bool saw_day = true; - if (when.tm_year == -1) { - when.tm_year = now_year; - saw_year = false; - } - if (when.tm_mon == -1) { - when.tm_mon = 0; - saw_mon = false; - } else { - saw_year = false; // don't increment by year if month used - } - if (when.tm_mday == -1) { - when.tm_mday = 1; - saw_day = false; - } else { - saw_mon = false; // don't increment by month if day used - saw_year = false; // don't increment by year if day used - } + if (when.tm_year == -1) { + when.tm_year = date_t::current_year; + saw_year = false; + } + if (when.tm_mon == -1) { + when.tm_mon = 0; + saw_mon = false; + } else { + saw_year = false; // don't increment by year if month used + } + if (when.tm_mday == -1) { + when.tm_mday = 1; + saw_day = false; + } else { + saw_mon = false; // don't increment by month if day used + saw_year = false; // don't increment by year if day used + } - if (begin) { - *begin = std::mktime(&when); - if (end) - *end = interval_t(saw_day ? 86400 : 0, saw_mon ? 1 : 0, - saw_year ? 1 : 0).increment(*begin); - } - else if (end) { - *end = std::mktime(&when); + if (begin) { + *begin = std::mktime(&when); + if (end) + *end = interval_t(saw_day ? 86400 : 0, saw_mon ? 1 : 0, + saw_year ? 1 : 0).increment(*begin); + } + else if (end) { + *end = std::mktime(&when); + } } -} - -static inline void read_lower_word(std::istream& in, std::string& word) { - in >> word; - for (int i = 0, l = word.length(); i < l; i++) - word[i] = std::tolower(word[i]); -} -static void parse_date_words(std::istream& in, std::string& word, - std::time_t * begin, std::time_t * end) -{ - std::string type; - bool mon_spec = false; - char buf[32]; - - if (word == "this" || word == "last" || word == "next") { - type = word; - if (! in.eof()) - read_lower_word(in, word); - else - word = "month"; - } else { - type = "this"; + inline void read_lower_word(std::istream& in, std::string& word) { + in >> word; + for (int i = 0, l = word.length(); i < l; i++) + word[i] = std::tolower(word[i]); } - if (word == "month") { - std::strftime(buf, 31, "%B", std::localtime(&now)); - word = buf; - mon_spec = true; - } - else if (word == "year") { - std::strftime(buf, 31, "%Y", std::localtime(&now)); - word = buf; - } + void parse_date_words(std::istream& in, std::string& word, + datetime_t * begin, datetime_t * end) + { + std::string type; - parse_inclusion_specifier(word, begin, end); + bool mon_spec = false; + char buf[32]; - 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); + if (word == "this" || word == "last" || word == "next") { + type = word; + if (! in.eof()) + read_lower_word(in, word); + else + word = "month"; } else { - if (begin) - *begin = interval_t(0, 0, -1).increment(*begin); - if (end) - *end = interval_t(0, 0, -1).increment(*end); + type = "this"; } - } - 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); - } else { - if (begin) - *begin = interval_t(0, 0, 1).increment(*begin); - if (end) - *end = interval_t(0, 0, 1).increment(*end); + + if (word == "month") { + std::strftime(buf, 31, "%B", datetime_t::now.localtime()); + word = buf; + mon_spec = true; + } + else if (word == "year") { + std::strftime(buf, 31, "%Y", datetime_t::now.localtime()); + word = buf; + } + + 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); + } else { + if (begin) + *begin = interval_t(0, 0, -1).increment(*begin); + if (end) + *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); + } else { + if (begin) + *begin = interval_t(0, 0, 1).increment(*begin); + if (end) + *end = interval_t(0, 0, 1).increment(*end); + } } } } @@ -221,9 +244,9 @@ void interval_t::parse(std::istream& in) int quantity = std::atol(word.c_str()); read_lower_word(in, word); if (word == "days") - seconds = 86400 * quantity; + days = quantity; else if (word == "weeks") - seconds = 7 * 86400 * quantity; + days = 7 * quantity; else if (word == "months") months = quantity; else if (word == "quarters") @@ -232,9 +255,9 @@ void interval_t::parse(std::istream& in) years = quantity; } else if (word == "day") - seconds = 86400; + days = 1; else if (word == "week") - seconds = 7 * 86400; + days = 7; else if (word == "month") months = 1; else if (word == "quarter") @@ -243,11 +266,11 @@ void interval_t::parse(std::istream& in) years = 1; } else if (word == "daily") - seconds = 86400; + days = 1; else if (word == "weekly") - seconds = 7 * 86400; + days = 7; else if (word == "biweekly") - seconds = 14 * 86400; + days = 14; else if (word == "monthly") months = 1; else if (word == "bimonthly") @@ -277,42 +300,49 @@ void interval_t::parse(std::istream& in) } } -bool parse_date_mask(const char * date_str, struct std::tm * result) -{ - for (const char ** f = formats; *f; f++) { - memset(result, INT_MAX, sizeof(struct std::tm)); - if (strptime(date_str, *f, result)) - return true; +namespace { + bool parse_date_mask(const char * date_str, struct std::tm * result) + { + if (! date_t::input_format.empty()) { + std::memset(result, INT_MAX, sizeof(struct std::tm)); + if (strptime(date_str, date_t::input_format.c_str(), result)) + return true; + } + for (const char ** f = date_t::formats; *f; f++) { + std::memset(result, INT_MAX, sizeof(struct std::tm)); + if (strptime(date_str, *f, result)) + return true; + } + return false; } - return false; -} -bool parse_date(const char * date_str, std::time_t * result, const int year) -{ - struct std::tm when; + bool parse_date(const char * date_str, std::time_t * result, const int year) + { + struct std::tm when; - if (! parse_date_mask(date_str, &when)) - return false; + if (! parse_date_mask(date_str, &when)) + return false; - when.tm_hour = 0; - when.tm_min = 0; - when.tm_sec = 0; + when.tm_hour = 0; + when.tm_min = 0; + when.tm_sec = 0; - if (when.tm_year == -1) - when.tm_year = ((year == -1) ? now_year : (year - 1900)); + if (when.tm_year == -1) + when.tm_year = ((year == -1) ? date_t::current_year : (year - 1900)); - if (when.tm_mon == -1) - when.tm_mon = 0; + if (when.tm_mon == -1) + when.tm_mon = 0; - if (when.tm_mday == -1) - when.tm_mday = 1; + if (when.tm_mday == -1) + when.tm_mday = 1; - *result = std::mktime(&when); + *result = std::mktime(&when); - return true; -} + return true; + } -bool quick_parse_date(const char * date_str, std::time_t * result) -{ - return parse_date(date_str, result, now_year + 1900); + bool quick_parse_date(const char * date_str, std::time_t * result) + { + return parse_date(date_str, result, date_t::current_year + 1900); + } } @@ -6,130 +6,307 @@ #include "error.h" +class date_error : public error { + public: + date_error(const std::string& reason) throw() : error(reason) {} + virtual ~date_error() throw() {} +}; + struct interval_t; +class datetime_t; -struct datetime_t +class date_t { + date_t(const datetime_t& _when); + + protected: std::time_t when; - static std::string date_format; + public: + static date_t now; + static const char * formats[]; + static int current_year; + static std::string input_format; + static std::string output_format; - datetime_t(const std::time_t _when) : when(_when) {} + date_t() : when(0) {} + date_t(const date_t& _when) : when(_when.when) {} - datetime_t& operator+=(const long secs) { - when += secs; - return *this; + date_t(const std::time_t _when) : when(_when) { +#if 0 + struct std::tm * moment = std::localtime(&_when); + moment->tm_hour = 0; + moment->tm_min = 0; + moment->tm_sec = 0; + when = std::mktime(moment); +#endif } - datetime_t& operator-=(const long secs) { - when -= secs; + date_t(const interval_t& period); + date_t(const std::string& _when); + + virtual ~date_t() {} + + date_t& operator=(const date_t& _when) { + when = _when.when; return *this; } + date_t& operator=(const std::time_t _when) { + return *this = date_t(_when); + } + date_t& operator=(const datetime_t& _when) { + return *this = date_t(_when); + } + date_t& operator=(const interval_t& period) { + return *this = date_t(period); + } + date_t& operator=(const std::string& _when) { + return *this = date_t(_when); + } + + date_t& operator+=(const interval_t& period); + + long operator-=(const date_t& date) { + return (when - date.when) / 86400; + } - datetime_t& operator=(const interval_t& period); - datetime_t& operator+=(const interval_t& period); + virtual date_t& operator+=(const long days) { + // jww (2006-03-26): This is not accurate enough when DST is in effect! + assert(0); + when += days * 86400; + return *this; + } + virtual date_t& operator-=(const long days) { + assert(0); + when -= days * 86400; + return *this; + } -#define DEF_DATETIME_OP(OP) \ - bool operator OP(const datetime_t& other) { \ +#define DEF_DATE_OP(OP) \ + bool operator OP(const date_t& other) const { \ return when OP other.when; \ } - DEF_DATETIME_OP(<) - DEF_DATETIME_OP(<=) - DEF_DATETIME_OP(>) - DEF_DATETIME_OP(>=) - DEF_DATETIME_OP(==) - DEF_DATETIME_OP(!=) + DEF_DATE_OP(<) + DEF_DATE_OP(<=) + DEF_DATE_OP(>) + DEF_DATE_OP(>=) + DEF_DATE_OP(==) + DEF_DATE_OP(!=) operator bool() const { return when != 0; } - operator long() const { - return (long)when; + operator std::time_t() const { + return when; } - operator double() const { - return (double)when; + operator std::string() const { + return to_string(); } + std::string to_string(const std::string& format = output_format) const { + char buf[64]; + std::strftime(buf, 63, format.c_str(), localtime()); + return buf; + } + int year() const { - struct std::tm * desc = std::localtime(&when); - return desc->tm_year + 1900; + return localtime()->tm_year + 1900; } int month() const { - struct std::tm * desc = std::localtime(&when); - return desc->tm_mon + 1; + return localtime()->tm_mon + 1; } int day() const { - struct std::tm * desc = std::localtime(&when); - return desc->tm_mday; + return localtime()->tm_mday; + } + int wday() const { + return localtime()->tm_wday; } + + std::tm * localtime() const { + return std::localtime(&when); + } + + void write(std::ostream& out, + const std::string& format = output_format) const { + out << to_string(format); + } + + friend class datetime_t; + friend struct interval_t; }; -inline std::ostream& operator<<(std::ostream& out, const datetime_t& moment) { - char buf[32]; - std::strftime(buf, 31, datetime_t::date_format.c_str(), - std::localtime(&moment.when)); - out << buf; +inline long operator-(const date_t& left, const date_t& right) { + date_t temp(left); + temp -= right; + return temp; +} + +inline date_t operator+(const date_t& left, const long days) { + date_t temp(left); + temp += days; + return temp; +} + +inline date_t operator-(const date_t& left, const long days) { + date_t temp(left); + temp -= days; + return temp; } +inline std::ostream& operator<<(std::ostream& out, const date_t& moment) { + moment.write(out); +} + +class datetime_error : public error { + public: + datetime_error(const std::string& reason) throw() : error(reason) {} + virtual ~datetime_error() throw() {} +}; + +class datetime_t : public date_t +{ + public: + static datetime_t now; + + datetime_t() : date_t() {} + datetime_t(const datetime_t& _when) : date_t(_when.when) {} + datetime_t(const date_t& _when) : date_t(_when) {} + + datetime_t(const std::time_t _when) : date_t(_when) {} + datetime_t(const std::string& _when); + + datetime_t& operator=(const datetime_t& _when) { + when = _when.when; + return *this; + } + datetime_t& operator=(const date_t& _when) { + when = _when.when; + return *this; + } + datetime_t& operator=(const std::time_t _when) { + return *this = datetime_t(_when); + } + datetime_t& operator=(const std::string& _when) { + return *this = datetime_t(_when); + } + + long operator-=(const datetime_t& date) { + return when - date.when; + } + + virtual datetime_t& operator+=(const long secs) { + when += secs; + return *this; + } + virtual datetime_t& operator-=(const long secs) { + when -= secs; + return *this; + } + +#define DEF_DATETIME_OP(OP) \ + bool operator OP(const datetime_t& other) const { \ + return when OP other.when; \ + } + + DEF_DATETIME_OP(<) + DEF_DATETIME_OP(<=) + DEF_DATETIME_OP(>) + DEF_DATETIME_OP(>=) + DEF_DATETIME_OP(==) + DEF_DATETIME_OP(!=) + + int hour() const { + return localtime()->tm_hour; + } + int min() const { + return localtime()->tm_min; + } + int sec() const { + return localtime()->tm_sec; + } +}; + inline long operator-(const datetime_t& left, const datetime_t& right) { - return (long)left.when - (long)right.when; + datetime_t temp(left); + temp -= right; + return temp; +} + +inline datetime_t operator+(const datetime_t& left, const long seconds) { + datetime_t temp(left); + temp += seconds; + return temp; +} + +inline datetime_t operator-(const datetime_t& left, const long seconds) { + datetime_t temp(left); + temp -= seconds; + return temp; +} + +inline std::ostream& operator<<(std::ostream& out, + const datetime_t& moment) { + char buf[64]; + std::strftime(buf, 63, (date_t::output_format + " %H:%M:%S").c_str(), + moment.localtime()); + out << buf; } struct interval_t { - unsigned int years; - unsigned int months; - unsigned int seconds; - std::time_t begin; - std::time_t end; + unsigned short years; + unsigned short months; + unsigned short days; + unsigned short hours; + unsigned short minutes; + unsigned short seconds; + + datetime_t begin; + datetime_t end; + + interval_t(int _days = 0, int _months = 0, int _years = 0, + const date_t& _begin = date_t(), + const date_t& _end = date_t()) + : years(_years), months(_months), days(_days), + hours(0), minutes(0), seconds(0), + begin(_begin), end(_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) { - } interval_t(const std::string& desc) - : years(0), months(0), seconds(0), begin(0), end(0) { + : years(0), months(0), days(0), + hours(0), minutes(0), seconds(0) { std::istringstream stream(desc); parse(stream); } operator bool() const { - return seconds > 0 || months > 0 || years > 0; + return (years > 0 || months > 0 || days > 0 || + hours > 0 || minutes > 0 || seconds > 0); } - void start(const std::time_t moment) { + void start(const datetime_t& moment) { begin = first(moment); } - std::time_t first(const std::time_t moment = 0) const; - std::time_t increment(const std::time_t) const; + datetime_t first(const datetime_t& moment = datetime_t()) const; + datetime_t increment(const datetime_t&) const; void parse(std::istream& in); }; -inline datetime_t& datetime_t::operator=(const interval_t& period) { - when = period.first(); - return *this; +inline date_t::date_t(const interval_t& period) { + when = period.first().when; } -inline datetime_t& datetime_t::operator+=(const interval_t& period) { - when = period.increment(when); - return *this; -} - -extern std::time_t now; -extern int now_year; -extern char input_format[128]; -extern const char * formats[]; -bool parse_date_mask(const char * date_str, struct std::tm * result); -bool parse_date(const char * date_str, std::time_t * result, - const int year = -1); -bool quick_parse_date(const char * date_str, std::time_t * result); +inline date_t& date_t::operator+=(const interval_t& period) { + return *this = period.increment(*this); +} -class datetime_error : public error { - public: - datetime_error(const std::string& reason) throw() : error(reason) {} - virtual ~datetime_error() throw() {} -}; +inline date_t::date_t(const datetime_t& _when) { + assert(0); + struct std::tm * moment = _when.localtime(); + moment->tm_hour = 0; + moment->tm_min = 0; + moment->tm_sec = 0; + when = std::mktime(moment); +} #endif // _DATETIME_H @@ -62,7 +62,8 @@ void debug_assert(const std::string& reason, #include <new> #include <iostream> #include <cstdlib> -#include <ctime> + +#include "datetime.h" #define DEBUG_ENABLED @@ -85,10 +86,8 @@ bool _debug_active(const char * const cls); } #define DEBUG_PRINT_(x) DEBUG_PRINT(_debug_cls, x) -#define DEBUG_PRINT_TIME(cls, x) { \ - char buf[32]; \ - std::strftime(buf, 31, "%Y/%m/%d:%H", std::localtime(&x)); \ - DEBUG_PRINT(cls, #x << " is " << buf); \ +#define DEBUG_PRINT_TIME(cls, x) { \ + DEBUG_PRINT(cls, #x << " is " << x); \ } #define DEBUG_PRINT_TIME_(x) DEBUG_PRINT_TIME(_debug_cls, x) @@ -16,10 +16,8 @@ entry_t * derive_new_entry(journal_t& journal, entry_t * matching = NULL; - if (! parse_date((*i).c_str(), &added->_date)) - throw new error("Bad date passed to 'entry'"); - - if (++i == end) + added->_date = *i++; + if (i == end) throw new error("Too few arguments to 'entry'"); mask_t regexp(*i++); @@ -3,7 +3,6 @@ #include "util.h" #include <cstdlib> -#include <ctime> namespace ledger { @@ -208,11 +207,11 @@ element_t * format_t::parse_elements(const std::string& fmt) case 'd': current->type = element_t::COMPLETE_DATE_STRING; - current->chars = datetime_t::date_format; + current->chars = datetime_t::output_format; break; case 'D': current->type = element_t::DATE_STRING; - current->chars = datetime_t::date_format; + current->chars = datetime_t::output_format; break; case 'S': current->type = element_t::SOURCE; break; @@ -536,21 +535,21 @@ void format_t::format(std::ostream& out_str, const details_t& details) const break; case element_t::DATE_STRING: { - std::time_t date = 0; + datetime_t date; if (details.xact) date = details.xact->date(); else if (details.entry) date = details.entry->date(); char buf[256]; - std::strftime(buf, 255, elem->chars.c_str(), std::localtime(&date)); + std::strftime(buf, 255, elem->chars.c_str(), date.localtime()); out << (elem->max_width == 0 ? buf : truncated(buf, elem->max_width)); break; } case element_t::COMPLETE_DATE_STRING: { - std::time_t actual_date = 0; - std::time_t effective_date = 0; + datetime_t actual_date; + datetime_t effective_date; if (details.xact) { actual_date = details.xact->actual_date(); effective_date = details.xact->effective_date(); @@ -561,14 +560,13 @@ void format_t::format(std::ostream& out_str, const details_t& details) const } char abuf[256]; - std::strftime(abuf, 255, elem->chars.c_str(), - std::localtime(&actual_date)); + std::strftime(abuf, 255, elem->chars.c_str(), actual_date.localtime()); - if (effective_date != 0 && effective_date != actual_date) { + if (effective_date && effective_date != actual_date) { char buf[512]; char ebuf[256]; std::strftime(ebuf, 255, elem->chars.c_str(), - std::localtime(&effective_date)); + effective_date.localtime()); std::strcpy(buf, abuf); std::strcat(buf, "="); @@ -892,7 +890,7 @@ format_equity::format_equity(std::ostream& _output_stream, entry_t header_entry; header_entry.payee = "Opening Balances"; - header_entry._date = now; + header_entry._date = datetime_t::now; first_line_format.format(output_stream, details_t(header_entry)); } @@ -292,12 +292,9 @@ static void dataHandler(void *userData, const char *s, int len) curr_entry->code = std::string(s, len); break; - case ENTRY_DATE: { - struct tm when; - strptime(std::string(s, len).c_str(), "%Y-%m-%d %H:%M:%S %z", &when); - curr_entry->_date = std::mktime(&when); + case ENTRY_DATE: + curr_entry->_date = std::string(s, len); break; - } case ENTRY_DESC: curr_entry->payee = std::string(s, len); @@ -375,6 +372,10 @@ unsigned int gnucash_parser_t::parse(std::istream& in, { char buf[BUFSIZ]; + // This is the date format used by Gnucash, so override whatever the + // user specified. + date_t::input_format = "%Y-%m-%d %H:%M:%S %z"; + count = 0; action = NO_ACTION; curr_journal = journal; @@ -19,16 +19,16 @@ transaction_t::~transaction_t() if (cost) delete cost; } -std::time_t transaction_t::actual_date() const +datetime_t transaction_t::actual_date() const { - if (_date == 0 && entry) + if (! _date && entry) return entry->actual_date(); return _date; } -std::time_t transaction_t::effective_date() const +datetime_t transaction_t::effective_date() const { - if (_date_eff == 0 && entry) + if (! _date_eff && entry) return entry->effective_date(); return _date_eff; } @@ -176,9 +176,10 @@ bool entry_base_t::finalize() if ((*x)->amount.commodity() && ! (*x)->amount.commodity().annotated) - (*x)->amount.annotate_commodity(abs(per_unit_cost), - entry ? entry->actual_date() : 0, - entry ? entry->code : ""); + (*x)->amount.annotate_commodity + (abs(per_unit_cost), + entry ? entry->actual_date() : datetime_t(), + entry ? entry->code : ""); (*x)->cost = new amount_t(- (per_unit_cost * (*x)->amount)); balance += *(*x)->cost; @@ -4,7 +4,6 @@ #include <map> #include <list> #include <string> -#include <ctime> #include <iostream> #include "amount.h" @@ -34,8 +33,8 @@ class transaction_t enum state_t { UNCLEARED, CLEARED, PENDING }; entry_t * entry; - std::time_t _date; - std::time_t _date_eff; + datetime_t _date; + datetime_t _date_eff; account_t * account; amount_t amount; value_expr amount_expr; @@ -53,8 +52,8 @@ class transaction_t static bool use_effective_date; transaction_t(account_t * _account = NULL) - : entry(NULL), _date(0), _date_eff(0), account(_account), - cost(NULL), state(UNCLEARED), flags(TRANSACTION_NORMAL), + : entry(NULL), account(_account), cost(NULL), + state(UNCLEARED), flags(TRANSACTION_NORMAL), beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) { DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t"); } @@ -62,24 +61,24 @@ class transaction_t const amount_t& _amount, unsigned int _flags = TRANSACTION_NORMAL, const std::string& _note = "") - : entry(NULL), _date(0), _date_eff(0), account(_account), - amount(_amount), cost(NULL), state(UNCLEARED), flags(_flags), + : entry(NULL), account(_account), amount(_amount), cost(NULL), + state(UNCLEARED), flags(_flags), note(_note), beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) { DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t"); } transaction_t(const transaction_t& xact) - : entry(xact.entry), _date(0), _date_eff(0), account(xact.account), - amount(xact.amount), cost(xact.cost ? new amount_t(*xact.cost) : NULL), + : entry(xact.entry), account(xact.account), amount(xact.amount), + cost(xact.cost ? new amount_t(*xact.cost) : NULL), state(xact.state), flags(xact.flags), note(xact.note), beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) { DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t"); } ~transaction_t(); - std::time_t actual_date() const; - std::time_t effective_date() const; - std::time_t date() const { + datetime_t actual_date() const; + datetime_t effective_date() const; + datetime_t date() const { if (use_effective_date) return effective_date(); else @@ -162,12 +161,12 @@ class entry_base_t class entry_t : public entry_base_t { public: - std::time_t _date; - std::time_t _date_eff; + datetime_t _date; + datetime_t _date_eff; std::string code; std::string payee; - entry_t() : _date(0), _date_eff(0) { + entry_t() { DEBUG_PRINT("ledger.memory.ctors", "ctor entry_t"); } entry_t(const entry_t& e); @@ -176,15 +175,15 @@ class entry_t : public entry_base_t DEBUG_PRINT("ledger.memory.dtors", "dtor entry_t"); } - std::time_t actual_date() const { + datetime_t actual_date() const { return _date; } - std::time_t effective_date() const { - if (_date_eff == 0) + datetime_t effective_date() const { + if (! _date_eff) return _date; return _date_eff; } - std::time_t date() const { + datetime_t date() const { if (transaction_t::use_effective_date) return effective_date(); else @@ -8,7 +8,6 @@ #include <cstdio> #include <cstdlib> #include <cstring> -#include <ctime> #include "acconf.h" @@ -28,7 +27,7 @@ int parse_and_report(config_t& config, report_t& report, { // Configure the terminus for value expressions - ledger::terminus = now; + ledger::terminus = datetime_t::now; // Parse command-line arguments, and those set in the environment @@ -49,7 +48,8 @@ int parse_and_report(config_t& config, report_t& report, TRACE(main, "Processing options and environment variables"); - process_environment(ledger::config_options, envp, "LEDGER_"); + process_environment(ledger::config_options, + const_cast<const char **>(envp), "LEDGER_"); #if 1 // These are here for backwards compatability, but are deprecated. @@ -175,11 +175,10 @@ int parse_and_report(config_t& config, report_t& report, // If downloading is to be supported, configure the updater - // jww (2006-03-23): Should the pricing_leeway be in config_t? - // Should download_quotes be in report_t? if (! commodity_base_t::updater && config.download_quotes) commodity_base_t::updater = - new quotes_by_script(config.price_db, report.pricing_leeway, config.cache_dirty); + new quotes_by_script(config.price_db, config.pricing_leeway, + config.cache_dirty); std::auto_ptr<entry_t> new_entry; if (command == "e") { @@ -325,13 +324,8 @@ appending the output of this command to your Ledger file if you so choose." formatter = new format_entries(*out, *format); else if (command == "x") formatter = new format_emacs_transactions(*out); -#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE) else if (command == "X") formatter = new format_xml_entries(*out, report.show_totals); -#else - else if (command == "X") - throw new error("XML support was not compiled into this copy of Ledger"); -#endif else formatter = new format_transactions(*out, *format); @@ -93,7 +93,7 @@ void process_arguments(option_t * options, int argc, char ** argv, if ((*i)[2] == '\0') break; - char * name = *i + 2; + char * name = *i + 2; if (char * p = std::strchr(name, '=')) { *p++ = '\0'; value = p; @@ -132,17 +132,17 @@ void process_arguments(option_t * options, int argc, char ** argv, } } -void process_environment(option_t * options, char ** envp, +void process_environment(option_t * options, const char ** envp, const std::string& tag) { const char * tag_p = tag.c_str(); unsigned int tag_len = tag.length(); - for (char ** p = envp; *p; p++) + for (const char ** p = envp; *p; p++) if (! tag_p || std::strncmp(*p, tag_p, tag_len) == 0) { char buf[128]; char * r = buf; - char * q; + const char * q; for (q = *p + tag_len; *q && *q != '=' && r - buf < 128; q++) @@ -459,32 +459,28 @@ OPT_BEGIN(effective, "") { OPT_BEGIN(begin, "b:") { char buf[128]; interval_t interval(optarg); - if (interval.begin) - std::strftime(buf, 127, formats[0], std::localtime(&interval.begin)); - else + if (! interval.begin) throw new error(std::string("Could not determine beginning of period '") + optarg + "'"); if (! report->predicate.empty()) report->predicate += "&"; report->predicate += "d>=["; - report->predicate += buf; + report->predicate += interval.begin.to_string(); report->predicate += "]"; } OPT_END(begin); OPT_BEGIN(end, "e:") { char buf[128]; interval_t interval(optarg); - if (interval.end) - std::strftime(buf, 127, formats[0], std::localtime(&interval.end)); - else + if (! interval.end) throw new error(std::string("Could not determine end of period '") + optarg + "'"); if (! report->predicate.empty()) report->predicate += "&"; report->predicate += "d<["; - report->predicate += buf; + report->predicate += interval.end.to_string(); report->predicate += "]"; terminus = interval.end; @@ -547,12 +543,11 @@ OPT_BEGIN(format, "F:") { } OPT_END(format); OPT_BEGIN(date_format, "y:") { - report->date_format = optarg; + report->date_output_format = optarg; } OPT_END(date_format); OPT_BEGIN(input_date_format, ":") { - std::strcpy(input_format, optarg); - formats[0] = input_format; + config->date_input_format = optarg; } OPT_END(input_date_format); OPT_BEGIN(balance_format, ":") { @@ -684,26 +679,21 @@ OPT_BEGIN(period, "p:") { // modify the calculation predicate (via the --begin and --end // options) to take this into account. - char buf[128]; interval_t interval(report->report_period); if (interval.begin) { - std::strftime(buf, 127, formats[0], std::localtime(&interval.begin)); - if (! report->predicate.empty()) report->predicate += "&"; report->predicate += "d>=["; - report->predicate += buf; + report->predicate += interval.begin.to_string(); report->predicate += "]"; } if (interval.end) { - std::strftime(buf, 127, formats[0], std::localtime(&interval.end)); - if (! report->predicate.empty()) report->predicate += "&"; report->predicate += "d<["; - report->predicate += buf; + report->predicate += interval.end.to_string(); report->predicate += "]"; terminus = interval.end; @@ -731,6 +721,13 @@ OPT_BEGIN(monthly, "M") { report->report_period = std::string("monthly ") + report->report_period; } OPT_END(monthly); +OPT_BEGIN(quarterly, "") { + if (report->report_period.empty()) + report->report_period = "quarterly"; + else + report->report_period = std::string("quarterly ") + report->report_period; +} OPT_END(quarterly); + OPT_BEGIN(yearly, "Y") { if (report->report_period.empty()) report->report_period = "yearly"; @@ -841,7 +838,7 @@ OPT_BEGIN(price_db, ":") { } OPT_END(price_db); OPT_BEGIN(price_exp, "Z:") { - report->pricing_leeway = std::atol(optarg) * 60; + config->pricing_leeway = std::atol(optarg) * 60; } OPT_END(price_exp); OPT_BEGIN(download, "Q") { @@ -886,8 +883,8 @@ namespace { amount_t price(equals + 1); if (commodity_t * commodity = commodity_t::find_or_create(symbol)) { - commodity->add_price(now, price); - commodity->history()->bogus_time = now; + commodity->add_price(datetime_t::now, price); + commodity->history()->bogus_time = datetime_t::now; } } } @@ -1012,6 +1009,7 @@ option_t config_options[CONFIG_OPTIONS_SIZE] = { { "prices-format", '\0', true, opt_prices_format, false }, { "print-format", '\0', true, opt_print_format, false }, { "quantity", 'O', false, opt_quantity, false }, + { "quarterly", '\0', false, opt_quarterly, false }, { "real", 'R', false, opt_real, false }, { "reconcile", '\0', true, opt_reconcile, false }, { "reconcile-date", '\0', true, opt_reconcile_date, false }, @@ -27,7 +27,7 @@ bool process_option(option_t * options, const std::string& opt, const char * arg = NULL); void process_arguments(option_t * options, int argc, char ** argv, const bool anywhere, std::list<std::string>& args); -void process_environment(option_t * options, char ** envp, +void process_environment(option_t * options, const char ** envp, const std::string& tag); namespace ledger { @@ -38,7 +38,7 @@ class report_t; extern config_t * config; extern report_t * report; -#define CONFIG_OPTIONS_SIZE 94 +#define CONFIG_OPTIONS_SIZE 95 extern option_t config_options[CONFIG_OPTIONS_SIZE]; void option_help(std::ostream& out); @@ -104,8 +104,7 @@ unsigned int qif_parser_t::parse(std::istream& in, case 'D': SET_BEG_POS_AND_LINE(); get_line(in); - if (! parse_date(line, &entry->_date)) - throw new parse_error("Failed to parse date"); + entry->_date = line; break; case 'T': @@ -10,15 +10,15 @@ namespace ledger { void quotes_by_script::operator()(commodity_base_t& commodity, - const std::time_t moment, - const std::time_t date, - const std::time_t last, + const datetime_t& moment, + const datetime_t& date, + const datetime_t& last, amount_t& price) { DEBUG_CLASS("ledger.quotes.download"); DEBUG_PRINT_("commodity: " << commodity.symbol); - DEBUG_PRINT_TIME_(now); + DEBUG_PRINT_TIME_(datetime_t::now); DEBUG_PRINT_TIME_(moment); DEBUG_PRINT_TIME_(date); DEBUG_PRINT_TIME_(last); @@ -27,10 +27,9 @@ void quotes_by_script::operator()(commodity_base_t& commodity, DEBUG_PRINT_("pricing_leeway is " << pricing_leeway); if ((commodity.history && - std::difftime(now, commodity.history->last_lookup) < pricing_leeway) || - std::difftime(now, last) < pricing_leeway || - (price && std::difftime(moment, date) > 0 && - std::difftime(moment, date) <= pricing_leeway)) + (datetime_t::now - commodity.history->last_lookup) < pricing_leeway) || + (datetime_t::now - last) < pricing_leeway || + (price && moment > date && (moment - date) <= pricing_leeway)) return; using namespace std; @@ -59,20 +58,19 @@ void quotes_by_script::operator()(commodity_base_t& commodity, DEBUG_PRINT_("downloaded quote: " << buf); price.parse(buf); - commodity.add_price(now, price); + commodity.add_price(datetime_t::now, price); - commodity.history->last_lookup = now; + commodity.history->last_lookup = datetime_t::now; cache_dirty = true; if (price && ! price_db.empty()) { - strftime(buf, 127, "%Y/%m/%d %H:%M:%S", localtime(&now)); #if defined(__GNUG__) && __GNUG__ < 3 ofstream database(price_db.c_str(), ios::out | ios::app); #else ofstream database(price_db.c_str(), ios_base::out | ios_base::app); #endif - database << "P " << buf << " " << commodity.symbol - << " " << price << endl; + database << "P " << datetime_t::now.to_string("%Y/%m/%d %H:%M:%S") + << " " << commodity.symbol << " " << price << endl; } } else { throw new error(std::string("Failed to download price for '") + @@ -19,9 +19,9 @@ class quotes_by_script : public commodity_base_t::updater_t cache_dirty(_cache_dirty) {} virtual void operator()(commodity_base_t& commodity, - const std::time_t moment, - const std::time_t date, - const std::time_t last, + const datetime_t& moment, + const datetime_t& date, + const datetime_t& last, amount_t& price); }; diff --git a/reconcile.cc b/reconcile.cc index b56ebcf2..5b6dba24 100644 --- a/reconcile.cc +++ b/reconcile.cc @@ -44,7 +44,7 @@ void reconcile_transactions::flush() for (transactions_list::iterator x = xacts.begin(); x != xacts.end(); x++) { - if (! cutoff || std::difftime((*x)->date(), cutoff) < 0) { + if (! cutoff || (*x)->date() < cutoff) { switch ((*x)->state) { case transaction_t::CLEARED: cleared_balance += (*x)->amount; diff --git a/reconcile.h b/reconcile.h index c49ed205..7fd0d581 100644 --- a/reconcile.h +++ b/reconcile.h @@ -8,14 +8,15 @@ namespace ledger { class reconcile_transactions : public item_handler<transaction_t> { - value_t balance; - time_t cutoff; + value_t balance; + datetime_t cutoff; transactions_list xacts; public: reconcile_transactions(item_handler<transaction_t> * handler, - const value_t& _balance, const time_t _cutoff) + const value_t& _balance, + const datetime_t& _cutoff) : item_handler<transaction_t>(handler), balance(_balance), cutoff(_cutoff) {} @@ -12,7 +12,6 @@ report_t::report_t() display_predicate = ""; descend_expr = ""; - pricing_leeway = 24 * 3600; budget_flags = BUDGET_NO_BUDGET; head_entries = 0; @@ -204,8 +203,8 @@ void report_t::process_options(const std::string& command, // Now setup the various formatting strings - if (! date_format.empty()) - datetime_t::date_format = date_format; + if (! date_output_format.empty()) + date_t::output_format = date_output_format; amount_t::keep_price = keep_price; amount_t::keep_date = keep_date; @@ -278,9 +277,9 @@ report_t::chain_xact_handlers(const std::string& command, // transactions which can be reconciled to a given balance // (calculated against the transactions which it receives). if (! reconcile_balance.empty()) { - std::time_t cutoff = now; + datetime_t cutoff = datetime_t::now; if (! reconcile_date.empty()) - parse_date(reconcile_date.c_str(), &cutoff); + cutoff = reconcile_date; ptrs.push_back(formatter = new reconcile_transactions (formatter, value_t(reconcile_balance), cutoff)); @@ -27,10 +27,9 @@ class report_t std::string forecast_limit; std::string reconcile_balance; std::string reconcile_date; - std::string date_format; + std::string date_output_format; unsigned long budget_flags; - unsigned long pricing_leeway; int head_entries; int tail_entries; @@ -16,7 +16,6 @@ #include <fstream> #include <sstream> #include <cstring> -#include <ctime> #include <cctype> #include <cstdio> #include <cstdlib> @@ -36,10 +35,15 @@ static unsigned int linenum; static unsigned int src_idx; static accounts_map account_aliases; +static std::list<std::pair<std::string, int> > include_stack; + #ifdef TIMELOG_SUPPORT -static std::time_t time_in; -static account_t * last_account; -static std::string last_desc; +struct time_entry_t { + datetime_t checkin; + account_t * account; + std::string desc; +}; +std::list<time_entry_t> time_entries; #endif inline char * next_element(char * buf, bool variable = false) @@ -85,7 +89,7 @@ static value_expr parse_amount_expr(std::istream& in, amount_t& amount, if (! compute_amount(expr, amount, xact)) throw new parse_error("Amount expression failed to compute"); - + if (expr->kind == value_expr_t::CONSTANT) expr = NULL; @@ -285,12 +289,10 @@ transaction_t * parse_transaction(char * line, account_t * account, if (char * p = std::strchr(buf, '=')) { *p++ = '\0'; - if (! quick_parse_date(p, &xact->_date_eff)) - throw new parse_error("Failed to parse effective date"); + xact->_date_eff = p; } - - if (buf[0] && ! quick_parse_date(buf, &xact->_date)) - throw new parse_error("Failed to parse date"); + if (buf[0]) + xact->_date = buf; } } } @@ -358,12 +360,9 @@ entry_t * parse_entry(std::istream& in, char * line, account_t * master, if (char * p = std::strchr(line, '=')) { *p++ = '\0'; - if (! quick_parse_date(p, &curr->_date_eff)) - throw new parse_error("Failed to parse effective date"); + curr->_date_eff = p; } - - if (! quick_parse_date(line, &curr->_date)) - throw new parse_error("Failed to parse date"); + curr->_date = line; TIMER_STOP(entry_date); @@ -492,22 +491,61 @@ bool textual_parser_t::test(std::istream& in) const return true; } -static void clock_out_from_timelog(const std::time_t when, - journal_t * journal) +static void clock_out_from_timelog(const datetime_t& when, + account_t * account, + const char * desc, + journal_t * journal) { + time_entry_t event; + bool found = false; + + if (time_entries.size() == 1) { + event = time_entries.back(); + time_entries.clear(); + } + else if (time_entries.empty()) { + throw new parse_error("Timelog check-out event without a check-in"); + } + else if (! account) { + throw new parse_error + ("When multiple check-ins are active, checking out requires an account"); + } + else { + for (std::list<time_entry_t>::iterator i = time_entries.begin(); + i != time_entries.end(); + i++) + if (account == (*i).account) { + event = *i; + found = true; + time_entries.erase(i); + break; + } + if (! found) + throw new parse_error + ("Timelog check-out event does not match any current check-ins"); + } + + if (desc && event.desc.empty()) { + event.desc = desc; + desc = NULL; + } + std::auto_ptr<entry_t> curr(new entry_t); curr->_date = when; - curr->code = ""; - curr->payee = last_desc; + curr->code = desc ? desc : ""; + curr->payee = event.desc; - double diff = std::difftime(curr->_date, time_in); - char buf[32]; - std::sprintf(buf, "%lds", long(diff)); + if (curr->_date < event.checkin) + throw new parse_error + ("Timelog check-out date less than corresponding check-in"); + + char buf[32]; + std::sprintf(buf, "%lds", curr->_date - event.checkin); amount_t amt; amt.parse(buf); transaction_t * xact - = new transaction_t(last_account, amt, TRANSACTION_VIRTUAL); + = new transaction_t(event.account, amt, TRANSACTION_VIRTUAL); xact->state = transaction_t::CLEARED; curr->add_transaction(xact); @@ -517,8 +555,6 @@ static void clock_out_from_timelog(const std::time_t when, curr.release(); } -static std::list<std::pair<std::string, int> > include_stack; - unsigned int textual_parser_t::parse(std::istream& in, config_t& config, journal_t * journal, @@ -577,37 +613,38 @@ unsigned int textual_parser_t::parse(std::istream& in, char * p = skip_ws(line + 22); char * n = next_element(p, true); - last_desc = n ? n : ""; - struct std::tm when; - if (strptime(date.c_str(), "%Y/%m/%d %H:%M:%S", &when)) { - time_in = std::mktime(&when); - last_account = account_stack.front()->find_account(p); - } else { - last_account = NULL; - throw new parse_error("Cannot parse timelog entry date"); - } + time_entry_t event; + event.desc = n ? n : ""; + event.checkin = date; + event.account = account_stack.front()->find_account(p); + + if (! time_entries.empty()) + for (std::list<time_entry_t>::iterator i = time_entries.begin(); + i != time_entries.end(); + i++) + if (event.account == (*i).account) + throw new parse_error + ("Cannot double check-in to the same account"); + + time_entries.push_back(event); break; } case 'o': case 'O': - if (last_account) { + if (time_entries.empty()) { + throw new parse_error("Timelog check-out event without a check-in"); + } else { std::string date(line, 2, 19); char * p = skip_ws(line + 22); - if (last_desc.empty() && *p) - last_desc = p; + char * n = next_element(p, true); - struct std::tm when; - if (strptime(date.c_str(), "%Y/%m/%d %H:%M:%S", &when)) { - clock_out_from_timelog(std::mktime(&when), journal); - count++; - } else { - throw new parse_error("Cannot parse timelog entry date"); - } - - last_account = NULL; + clock_out_from_timelog + (date, p ? account_stack.front()->find_account(p) : NULL, n, + journal); + count++; } break; #endif // TIMELOG_SUPPORT @@ -631,39 +668,21 @@ unsigned int textual_parser_t::parse(std::istream& in, break; case 'P': { // a pricing entry - char * date_field = skip_ws(line + 1); - char * time_field = next_element(date_field); - if (! time_field) break; + char * date_field_ptr = skip_ws(line + 1); + char * time_field_ptr = next_element(date_field_ptr); + if (! time_field_ptr) break; + std::string date_field = date_field_ptr; - char * symbol_and_price; - std::time_t date; - struct std::tm when; + char * symbol_and_price; + datetime_t datetime; - if (std::isdigit(time_field[0])) { - symbol_and_price = next_element(time_field); + if (std::isdigit(time_field_ptr[0])) { + symbol_and_price = next_element(time_field_ptr); if (! symbol_and_price) break; - - char date_buffer[64]; - std::strcpy(date_buffer, date_field); - date_buffer[std::strlen(date_field)] = ' '; - std::strcpy(&date_buffer[std::strlen(date_field) + 1], time_field); - - if (strptime(date_buffer, "%Y/%m/%d %H:%M:%S", &when)) { - date = std::mktime(&when); - } else { - throw new parse_error("Failed to parse date/time"); - } + datetime = date_field + " " + time_field_ptr; } else { - symbol_and_price = time_field; - - if (strptime(date_field, "%Y/%m/%d", &when)) { - when.tm_hour = 0; - when.tm_min = 0; - when.tm_sec = 0; - date = std::mktime(&when); - } else { - throw new parse_error("Failed to parse date"); - } + symbol_and_price = time_field_ptr; + datetime = date_t(date_field); } std::string symbol; @@ -671,7 +690,7 @@ unsigned int textual_parser_t::parse(std::istream& in, amount_t price(symbol_and_price); if (commodity_t * commodity = commodity_t::find_or_create(symbol)) - commodity->add_price(date, price); + commodity->add_price(datetime, price); break; } @@ -686,7 +705,7 @@ unsigned int textual_parser_t::parse(std::istream& in, } case 'Y': // set the current year - now_year = std::atoi(skip_ws(line + 1)) - 1900; + date_t::current_year = std::atoi(skip_ws(line + 1)) - 1900; break; #ifdef TIMELOG_SUPPORT @@ -858,9 +877,12 @@ unsigned int textual_parser_t::parse(std::istream& in, } done: - if (last_account) { - clock_out_from_timelog(now, journal); - last_account = NULL; + if (! time_entries.empty()) { + for (std::list<time_entry_t>::iterator i = time_entries.begin(); + i != time_entries.end(); + i++) + clock_out_from_timelog(datetime_t::now, (*i).account, NULL, journal); + time_entries.clear(); } if (added_auto_entry_hook) @@ -11,7 +11,7 @@ value_expr amount_expr; value_expr total_expr; std::auto_ptr<scope_t> global_scope; -std::time_t terminus; +datetime_t terminus; details_t::details_t(const transaction_t& _xact) : entry(_xact.entry), xact(&_xact), account(xact_account(_xact)) @@ -150,7 +150,7 @@ void value_expr_t::compute(value_t& result, const details_t& details, break; case F_NOW: - result = datetime_t(terminus); + result = terminus; break; case AMOUNT: @@ -257,37 +257,37 @@ void value_expr_t::compute(value_t& result, const details_t& details, case DATE: if (details.xact && transaction_has_xdata(*details.xact) && transaction_xdata_(*details.xact).date) - result = datetime_t(transaction_xdata_(*details.xact).date); + result = transaction_xdata_(*details.xact).date; else if (details.xact) - result = datetime_t(details.xact->date()); + result = details.xact->date(); else if (details.entry) - result = datetime_t(details.entry->date()); + result = details.entry->date(); else - result = datetime_t(terminus); + result = terminus; break; case ACT_DATE: if (details.xact && transaction_has_xdata(*details.xact) && transaction_xdata_(*details.xact).date) - result = datetime_t(transaction_xdata_(*details.xact).date); + result = transaction_xdata_(*details.xact).date; else if (details.xact) - result = datetime_t(details.xact->actual_date()); + result = details.xact->actual_date(); else if (details.entry) - result = datetime_t(details.entry->actual_date()); + result = details.entry->actual_date(); else - result = datetime_t(terminus); + result = terminus; break; case EFF_DATE: if (details.xact && transaction_has_xdata(*details.xact) && transaction_xdata_(*details.xact).date) - result = datetime_t(transaction_xdata_(*details.xact).date); + result = transaction_xdata_(*details.xact).date; else if (details.xact) - result = datetime_t(details.xact->effective_date()); + result = details.xact->effective_date(); else if (details.entry) - result = datetime_t(details.entry->effective_date()); + result = details.entry->effective_date(); else - result = datetime_t(terminus); + result = terminus; break; case CLEARED: @@ -388,19 +388,20 @@ void value_expr_t::compute(value_t& result, const details_t& details, value_expr_t * expr = find_leaf(context, 0, arg_index); expr->compute(result, details, context); - // jww (2006-03-05): Generate an error if result is not a DATETIME - std::time_t moment = (long)result; - struct std::tm * desc = std::localtime(&moment); + if (result.type != value_t::DATETIME) + throw new compute_error("Invalid date passed to year|month|day(date)", + new valexpr_context(expr)); + datetime_t& moment(*((datetime_t *)result.data)); switch (kind) { case F_YEAR: - result = (long)desc->tm_year + 1900L; + result = (long)moment.year(); break; case F_MONTH: - result = (long)desc->tm_mon + 1L; + result = (long)moment.month(); break; case F_DAY: - result = (long)desc->tm_mday; + result = (long)moment.day(); break; } break; @@ -1036,7 +1037,7 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope, node.reset(new value_expr_t(value_expr_t::CONSTANT)); interval_t timespan(buf); - node->value = new value_t(datetime_t(timespan.first())); + node->value = new value_t(timespan.first()); break; } @@ -224,7 +224,7 @@ struct scope_t typedef std::map<const std::string, value_expr_t *> symbol_map; typedef std::pair<const std::string, value_expr_t *> symbol_pair; - + symbol_map symbols; scope_t(scope_t * _parent = NULL) : parent(_parent) { @@ -267,8 +267,8 @@ struct scope_t extern std::auto_ptr<scope_t> global_scope; -extern std::time_t terminus; -extern bool initialized; +extern datetime_t terminus; +extern bool initialized; void init_value_expr(); @@ -836,7 +836,7 @@ value_t::operator double() const case INTEGER: return *((long *) data); case DATETIME: - return *((datetime_t *) data); + throw new value_error("Cannot convert a date/time to a double"); case AMOUNT: return *((amount_t *) data); case BALANCE: @@ -1111,7 +1111,7 @@ void value_t::abs() } } -value_t value_t::value(const std::time_t moment) const +value_t value_t::value(const datetime_t& moment) const { switch (type) { case BOOLEAN: @@ -1225,18 +1225,18 @@ value_t value_t::date() const case BOOLEAN: throw new value_error("Cannot find the date of a boolean"); case INTEGER: - return 0L; + return datetime_t(); case DATETIME: return *this; case AMOUNT: - return ((amount_t *) data)->date(); + return datetime_t(((amount_t *) data)->date()); case BALANCE: - return (long)((balance_t *) data)->date(); + return datetime_t(((balance_t *) data)->date()); case BALANCE_PAIR: - return (long)((balance_pair_t *) data)->quantity.date(); + return datetime_t(((balance_pair_t *) data)->quantity.date()); default: assert(0); @@ -315,7 +315,7 @@ class value_t const bool keep_tag = amount_t::keep_tag) const; value_t& add(const amount_t& amount, const amount_t * cost = NULL); - value_t value(const std::time_t moment) const; + value_t value(const datetime_t& moment) const; void reduce(); value_t reduced() const { @@ -183,7 +183,7 @@ void handle_value(const value_t& value, unsigned int flags, std::list<transaction_t>& temps, item_handler<transaction_t>& handler, - const std::time_t date = 0, + const datetime_t& date = datetime_t(), transactions_list * component_xacts = NULL) { temps.push_back(transaction_t(account)); @@ -312,7 +312,7 @@ void related_transactions::flush() item_handler<transaction_t>::flush(); } -void changed_value_transactions::output_diff(const std::time_t current) +void changed_value_transactions::output_diff(const datetime_t& current) { value_t cur_bal; @@ -335,7 +335,7 @@ void changed_value_transactions::output_diff(const std::time_t current) void changed_value_transactions::operator()(transaction_t& xact) { if (last_xact) { - std::time_t moment = 0; + datetime_t moment; if (transaction_has_xdata(*last_xact)) moment = transaction_xdata_(*last_xact).date; else @@ -367,19 +367,18 @@ void component_transactions::operator()(transaction_t& xact) void subtotal_transactions::report_subtotal(const char * spec_fmt) { - char buf[256]; - + std::ostringstream out_date; if (! spec_fmt) { std::string fmt = "- "; - fmt += datetime_t::date_format; - std::strftime(buf, 255, fmt.c_str(), std::localtime(&finish)); + fmt += date_t::output_format; + finish.write(out_date, fmt); } else { - std::strftime(buf, 255, spec_fmt, std::localtime(&finish)); + finish.write(out_date, spec_fmt); } entry_temps.push_back(entry_t()); entry_t& entry = entry_temps.back(); - entry.payee = buf; + entry.payee = out_date.str(); entry._date = start; for (values_map::iterator i = values.begin(); @@ -393,9 +392,9 @@ void subtotal_transactions::report_subtotal(const char * spec_fmt) void subtotal_transactions::operator()(transaction_t& xact) { - if (! start || std::difftime(xact.date(), start) < 0) + if (! start || xact.date() < start) start = xact.date(); - if (! finish || std::difftime(xact.date(), finish) > 0) + if (! finish || xact.date() > finish) finish = xact.date(); account_t * acct = xact_account(xact); @@ -429,13 +428,13 @@ void subtotal_transactions::operator()(transaction_t& xact) account_xdata(*xact_account(xact)).dflags |= ACCOUNT_HAS_UNB_VIRTUALS; } -void interval_transactions::report_subtotal(const std::time_t moment) +void interval_transactions::report_subtotal(const datetime_t& moment) { assert(last_xact); start = interval.begin; if (moment) - finish = moment - 86400; + finish = moment - 86400L; else finish = last_xact->date(); @@ -446,10 +445,10 @@ void interval_transactions::report_subtotal(const std::time_t moment) void interval_transactions::operator()(transaction_t& xact) { - const std::time_t date = xact.date(); + const datetime_t date = xact.date(); - if ((interval.begin && std::difftime(date, interval.begin) < 0) || - (interval.end && std::difftime(date, interval.end) >= 0)) + if ((interval.begin && date < interval.begin) || + (interval.end && date >= interval.end)) return; if (interval) { @@ -460,13 +459,13 @@ void interval_transactions::operator()(transaction_t& xact) started = true; } - std::time_t quant = interval.increment(interval.begin); - if (std::difftime(date, quant) >= 0) { + datetime_t quant = interval.increment(interval.begin); + if (date >= quant) { if (last_xact) report_subtotal(quant); - std::time_t temp; - while (std::difftime(date, temp = interval.increment(quant)) >= 0) { + datetime_t temp; + while (date >= (temp = interval.increment(quant))) { if (quant == temp) break; quant = temp; @@ -518,7 +517,7 @@ void by_payee_transactions::operator()(transaction_t& xact) i = result.first; } - if (std::difftime(xact.date(), (*i).second->start) > 0) + if (xact.date() > (*i).second->start) (*i).second->start = xact.date(); (*(*i).second)(xact); @@ -602,7 +601,7 @@ void generate_transactions::add_transaction(const interval_t& period, pending_xacts.push_back(pending_xacts_pair(period, &xact)); } -void budget_transactions::report_budget_items(const std::time_t moment) +void budget_transactions::report_budget_items(const datetime_t& moment) { if (pending_xacts.size() == 0) return; @@ -613,14 +612,14 @@ void budget_transactions::report_budget_items(const std::time_t moment) for (pending_xacts_list::iterator i = pending_xacts.begin(); i != pending_xacts.end(); i++) { - std::time_t& begin = (*i).first.begin; + datetime_t& begin = (*i).first.begin; if (! begin) { (*i).first.start(moment); begin = (*i).first.begin; } - if (std::difftime(begin, moment) < 0 && - (! (*i).first.end || std::difftime(begin, (*i).first.end) < 0)) { + if (begin < moment && + (! (*i).first.end || begin < (*i).first.end)) { transaction_t& xact = *(*i).second; DEBUG_PRINT("ledger.walk.budget", "Reporting budget for " @@ -687,10 +686,10 @@ void forecast_transactions::add_transaction(const interval_t& period, interval_t& i = pending_xacts.back().first; if (! i.begin) { - i.start(now); + i.start(datetime_t::now); i.begin = i.increment(i.begin); } else { - while (std::difftime(i.begin, now) < 0) + while (i.begin < datetime_t::now) i.begin = i.increment(i.begin); } } @@ -698,20 +697,19 @@ void forecast_transactions::add_transaction(const interval_t& period, void forecast_transactions::flush() { transactions_list passed; - std::time_t last = 0; + datetime_t last; while (pending_xacts.size() > 0) { pending_xacts_list::iterator least = pending_xacts.begin(); for (pending_xacts_list::iterator i = ++pending_xacts.begin(); i != pending_xacts.end(); i++) - if (std::difftime((*i).first.begin, (*least).first.begin) < 0) + if ((*i).first.begin < (*least).first.begin) least = i; - std::time_t& begin = (*least).first.begin; + datetime_t& begin = (*least).first.begin; - if ((*least).first.end && - std::difftime(begin, (*least).first.end) >= 0) { + if ((*least).first.end && begin >= (*least).first.end) { pending_xacts.erase(least); passed.remove((*least).second); continue; @@ -731,9 +729,9 @@ void forecast_transactions::flush() temp.flags |= TRANSACTION_BULK_ALLOC; entry.add_transaction(&temp); - std::time_t next = (*least).first.increment(begin); - if (std::difftime(next, begin) < 0 || // wraparound - (last && std::difftime(next, last) > 5 * 365 * 24 * 60 * 60)) + datetime_t next = (*least).first.increment(begin); + if (next < begin || // wraparound + (last && (next - last) > 365 * 5 * 24 * 3600)) break; begin = next; @@ -90,15 +90,15 @@ struct transaction_xdata_t value_t value; unsigned int index; unsigned short dflags; - std::time_t date; + datetime_t date; account_t * account; void * ptr; transactions_list * component_xacts; transaction_xdata_t() - : index(0), dflags(0), date(0), account(NULL), ptr(NULL), - component_xacts(NULL) { + : index(0), dflags(0), + account(NULL), ptr(NULL), component_xacts(NULL) { DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_xdata_t " << this); } @@ -445,13 +445,13 @@ class changed_value_transactions : public item_handler<transaction_t> virtual void flush() { if (last_xact) { - output_diff(now); + output_diff(datetime_t::now); last_xact = NULL; } item_handler<transaction_t>::flush(); } - void output_diff(const std::time_t current); + void output_diff(const datetime_t& current); virtual void operator()(transaction_t& xact); }; @@ -481,13 +481,13 @@ class subtotal_transactions : public item_handler<transaction_t> std::list<transaction_t> xact_temps; public: - std::time_t start; - std::time_t finish; + datetime_t start; + datetime_t finish; subtotal_transactions(item_handler<transaction_t> * handler, bool _remember_components = false) : item_handler<transaction_t>(handler), - remember_components(_remember_components), start(0), finish(0) {} + remember_components(_remember_components) {} #ifdef DEBUG_ENABLED subtotal_transactions(const subtotal_transactions&) { assert(0); @@ -534,7 +534,7 @@ class interval_transactions : public subtotal_transactions : subtotal_transactions(_handler, remember_components), interval(_interval), last_xact(NULL), started(false) {} - void report_subtotal(const std::time_t moment = 0); + void report_subtotal(const datetime_t& moment = datetime_t()); virtual void flush() { if (last_xact) @@ -606,9 +606,7 @@ class dow_transactions : public subtotal_transactions virtual void flush(); virtual void operator()(transaction_t& xact) { - std::time_t when = xact.date(); - struct std::tm * desc = std::localtime(&when); - days_of_the_week[desc->tm_wday].push_back(&xact); + days_of_the_week[xact.date().wday()].push_back(&xact); } }; @@ -648,7 +646,7 @@ class budget_transactions : public generate_transactions unsigned long _flags = BUDGET_BUDGETED) : generate_transactions(handler), flags(_flags) {} - void report_budget_items(const std::time_t moment); + void report_budget_items(const datetime_t& moment); virtual void operator()(transaction_t& xact); }; @@ -12,13 +12,13 @@ extern "C" { #include <expat.h> // expat XML parser #elif defined(HAVE_XMLPARSE) #include <xmlparse.h> // expat XML parser -#else -#error "No XML parser library defined." #endif } namespace ledger { +#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE) + static XML_Parser current_parser; static unsigned int count; @@ -83,10 +83,10 @@ static void endElement(void *userData, const char *name) curr_entry = NULL; } else if (std::strcmp(name, "en:date") == 0) { - quick_parse_date(data.c_str(), &curr_entry->_date); + curr_entry->_date = data; } else if (std::strcmp(name, "en:date_eff") == 0) { - quick_parse_date(data.c_str(), &curr_entry->_date_eff); + curr_entry->_date_eff = data; } else if (std::strcmp(name, "en:code") == 0) { curr_entry->code = data; @@ -246,6 +246,8 @@ unsigned int xml_parser_t::parse(std::istream& in, return count; } +#endif // defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE) + void xml_write_amount(std::ostream& out, const amount_t& amount, const int depth = 0) { @@ -367,16 +369,14 @@ void output_xml_string(std::ostream& out, const std::string& str) void format_xml_entries::format_last_entry() { - char buf[32]; - std::strftime(buf, 31, "%Y/%m/%d", std::localtime(&last_entry->_date)); - output_stream << " <entry>\n" - << " <en:date>" << buf << "</en:date>\n"; + << " <en:date>" << last_entry->_date.to_string("%Y/%m/%d") + << "</en:date>\n"; - if (last_entry->_date_eff) { - std::strftime(buf, 31, "%Y/%m/%d", std::localtime(&last_entry->_date_eff)); - output_stream << " <en:date_eff>" << buf << "</en:date_eff>\n"; - } + if (last_entry->_date_eff) + output_stream << " <en:date_eff>" + << last_entry->_date_eff.to_string("%Y/%m/%d") + << "</en:date_eff>\n"; if (! last_entry->code.empty()) { output_stream << " <en:code>"; @@ -403,14 +403,15 @@ void format_xml_entries::format_last_entry() output_stream << " <transaction>\n"; - if ((*i)->_date) { - std::strftime(buf, 31, "%Y/%m/%d", std::localtime(&(*i)->_date)); - output_stream << " <tr:date>" << buf << "</tr:date>\n"; - } - if ((*i)->_date_eff) { - std::strftime(buf, 31, "%Y/%m/%d", std::localtime(&(*i)->_date_eff)); - output_stream << " <tr:date_eff>" << buf << "</tr:date_eff>\n"; - } + if ((*i)->_date) + output_stream << " <tr:date>" + << (*i)->_date.to_string("%Y/%m/%d") + << "</tr:date>\n"; + + if ((*i)->_date_eff) + output_stream << " <tr:date_eff>" + << (*i)->_date_eff.to_string("%Y/%m/%d") + << "</tr:date_eff>\n"; if ((*i)->state == transaction_t::CLEARED) output_stream << " <tr:cleared/>\n"; |