summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--amount.cc51
-rw-r--r--amount.h51
-rw-r--r--balance.cc12
-rw-r--r--balance.h16
-rw-r--r--binary.cc319
-rw-r--r--config.cc19
-rw-r--r--config.h4
-rw-r--r--datetime.cc384
-rw-r--r--datetime.h317
-rw-r--r--debug.h9
-rw-r--r--derive.cc6
-rw-r--r--format.cc22
-rw-r--r--gnucash.cc11
-rw-r--r--journal.cc15
-rw-r--r--journal.h37
-rw-r--r--main.cc16
-rw-r--r--option.cc48
-rw-r--r--option.h4
-rw-r--r--qif.cc3
-rw-r--r--quotes.cc24
-rw-r--r--quotes.h6
-rw-r--r--reconcile.cc2
-rw-r--r--reconcile.h7
-rw-r--r--report.cc9
-rw-r--r--report.h3
-rw-r--r--textual.cc182
-rw-r--r--valexpr.cc43
-rw-r--r--valexpr.h6
-rw-r--r--value.cc12
-rw-r--r--value.h2
-rw-r--r--walk.cc68
-rw-r--r--walk.h24
-rw-r--r--xml.cc41
33 files changed, 1026 insertions, 747 deletions
diff --git a/amount.cc b/amount.cc
index fee533a3..841b83ab 100644
--- a/amount.cc
+++ b/amount.cc
@@ -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);
}
diff --git a/amount.h b/amount.h
index 70f86328..d40f8f3a 100644
--- a/amount.h
+++ b/amount.h
@@ -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;
}
diff --git a/balance.cc b/balance.cc
index 70f9bfce..9e516736 100644
--- a/balance.cc
+++ b/balance.cc
@@ -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;
diff --git a/balance.h b/balance.h
index 932ca916..ba76d459 100644
--- a/balance.h
+++ b/balance.h
@@ -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();
}
diff --git a/binary.cc b/binary.cc
index 787a7da0..9185f766 100644
--- a/binary.cc
+++ b/binary.cc
@@ -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);
}
diff --git a/config.cc b/config.cc
index 43ae61e3..84442df0 100644
--- a/config.cc
+++ b/config.cc
@@ -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;
}
diff --git a/config.h b/config.h
index 9ca9736a..005fba58 100644
--- a/config.h
+++ b/config.h
@@ -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);
+ }
}
diff --git a/datetime.h b/datetime.h
index 5aed93d7..45bd4921 100644
--- a/datetime.h
+++ b/datetime.h
@@ -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
diff --git a/debug.h b/debug.h
index 8fbff28f..81083ad3 100644
--- a/debug.h
+++ b/debug.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)
diff --git a/derive.cc b/derive.cc
index 0e6df28a..16452df2 100644
--- a/derive.cc
+++ b/derive.cc
@@ -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++);
diff --git a/format.cc b/format.cc
index 5a5184d4..378e13f6 100644
--- a/format.cc
+++ b/format.cc
@@ -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));
}
diff --git a/gnucash.cc b/gnucash.cc
index 48250fbf..970fba3b 100644
--- a/gnucash.cc
+++ b/gnucash.cc
@@ -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;
diff --git a/journal.cc b/journal.cc
index 05bc4b55..017f82c5 100644
--- a/journal.cc
+++ b/journal.cc
@@ -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;
diff --git a/journal.h b/journal.h
index 3d0927cf..3577ea06 100644
--- a/journal.h
+++ b/journal.h
@@ -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
diff --git a/main.cc b/main.cc
index 1f080ceb..78ae0f5d 100644
--- a/main.cc
+++ b/main.cc
@@ -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);
diff --git a/option.cc b/option.cc
index 923f318b..adbbdcc7 100644
--- a/option.cc
+++ b/option.cc
@@ -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 },
diff --git a/option.h b/option.h
index da1a6395..4bbafdfc 100644
--- a/option.h
+++ b/option.h
@@ -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);
diff --git a/qif.cc b/qif.cc
index 61512036..faad30ca 100644
--- a/qif.cc
+++ b/qif.cc
@@ -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':
diff --git a/quotes.cc b/quotes.cc
index 21612a0f..a8fbfbc5 100644
--- a/quotes.cc
+++ b/quotes.cc
@@ -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 '") +
diff --git a/quotes.h b/quotes.h
index 72d0589e..12164b14 100644
--- a/quotes.h
+++ b/quotes.h
@@ -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) {}
diff --git a/report.cc b/report.cc
index bd6338d2..b83547c3 100644
--- a/report.cc
+++ b/report.cc
@@ -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));
diff --git a/report.h b/report.h
index 14923ab5..377b9c57 100644
--- a/report.h
+++ b/report.h
@@ -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;
diff --git a/textual.cc b/textual.cc
index 3786e4ed..6fdde631 100644
--- a/textual.cc
+++ b/textual.cc
@@ -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)
diff --git a/valexpr.cc b/valexpr.cc
index c6327627..2be3204f 100644
--- a/valexpr.cc
+++ b/valexpr.cc
@@ -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;
}
diff --git a/valexpr.h b/valexpr.h
index e9bf4493..ff3d4d3c 100644
--- a/valexpr.h
+++ b/valexpr.h
@@ -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();
diff --git a/value.cc b/value.cc
index 8ee3b09b..6f1f3ad7 100644
--- a/value.cc
+++ b/value.cc
@@ -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);
diff --git a/value.h b/value.h
index bac12ec5..6c8bbe96 100644
--- a/value.h
+++ b/value.h
@@ -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 {
diff --git a/walk.cc b/walk.cc
index 7cba3bb5..35cd9839 100644
--- a/walk.cc
+++ b/walk.cc
@@ -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;
diff --git a/walk.h b/walk.h
index 25a36b4e..3eedbae9 100644
--- a/walk.h
+++ b/walk.h
@@ -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);
};
diff --git a/xml.cc b/xml.cc
index 23bb8d84..418c6bf7 100644
--- a/xml.cc
+++ b/xml.cc
@@ -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";