Restored the interval_t time and added a new "period" debugging command.

You can use 'ledger period "daily in june"' to find out how Ledger will parse
that date string, plus up to the first 20 dates it encounters in the range.
Note that the 'end' displayed is currently exclusive.
This commit is contained in:
John Wiegley 2008-08-02 00:37:36 -04:00
parent 858978de89
commit 5bf3f536b3
5 changed files with 166 additions and 232 deletions

25
main.cc
View file

@ -222,6 +222,31 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[],
fmt.dump(*out);
return 0;
}
else if (verb == "period") {
interval_t interval(*arg);
if (! is_valid(interval.begin)) {
*out << "Time period has no beginning." << std::endl;
} else {
*out << "begin: " << format_date(interval.begin) << std::endl;
*out << " end: " << format_date(interval.end) << std::endl;
*out << std::endl;
date_t date = interval.first();
for (int i = 0; i < 20; i++) {
*out << std::right;
out->width(2);
*out << i << ": " << format_date(date) << std::endl;
date = interval.increment(date);
if (is_valid(interval.end) && date >= interval.end)
break;
}
}
return 0;
}
// Parse the initialization file, which can only be textual; then
// parse the journal data.

200
times.cc
View file

@ -29,7 +29,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "utils.h"
#include "utils.h" // this brings in times.h
namespace ledger {
@ -66,82 +66,92 @@ namespace {
};
}
string output_time_format = "%Y/%m/%d";
#if 0
datetime_t datetime_t::now(std::time(NULL));
optional<string> input_date_format;
string output_date_format = "%Y/%m/%d";
namespace {
static std::time_t base = -1;
static int base_year = -1;
bool parse_date_mask(const char * date_str, std::tm& result)
{
if (input_date_format) {
std::memset(&result, -1, sizeof(std::tm));
if (strptime(date_str, input_date_format->c_str(), &result))
return true;
}
for (const char ** f = formats; *f; f++) {
std::memset(&result, -1, sizeof(std::tm));
if (strptime(date_str, *f, &result))
return true;
}
return false;
}
static const int month_days[12] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
bool parse_date(const char * date_str, std::tm& result, const int year)
{
if (! parse_date_mask(date_str, result))
return false;
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);
result.tm_hour = 0;
result.tm_min = 0;
result.tm_sec = 0;
if (result.tm_year == -1)
result.tm_year = ((year == -1) ? current_year : year) - 1900;
if (result.tm_mon == -1)
result.tm_mon = 0;
if (result.tm_mday == -1)
result.tm_mday = 1;
return true;
}
bool quick_parse_date(const char * date_str, std::tm& result)
{
return parse_date(date_str, result, current_year);
}
}
#endif
datetime_t parse_datetime(const char * str)
{
#if 0
return parse_abs_datetime(in);
#else
int year = ((str[0] - '0') * 1000 +
(str[1] - '0') * 100 +
(str[2] - '0') * 10 +
(str[3] - '0'));
int mon = ((str[5] - '0') * 10 +
(str[6] - '0'));
int day = ((str[8] - '0') * 10 +
(str[9] - '0'));
return datetime_t(boost::gregorian::date(year, mon, day));
#endif
std::tm when;
// jww (2008-08-01): This needs to look for HH:MM:SS as well.
quick_parse_date(str, when);
return posix_time::ptime_from_tm(when);
}
date_t interval_t::first(const date_t& moment) const
date_t parse_date(const char * str)
{
std::tm when;
quick_parse_date(str, when);
return gregorian::date_from_tm(when);
}
date_t interval_t::first(const optional<date_t>& moment) const
{
if (! is_valid(begin))
throw_(date_error,
"Use of interval_t::first() with specifying a range start");
date_t quant(begin);
if (! advanced)
advanced = true;
#if 0
if (is_valid(moment) && moment > quant) {
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.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_isdst = -1;
quant = std::mktime(desc);
date_t quant(moment->year(), gregorian::Jan, 1);
date_t temp;
while (moment >= (temp = increment(quant))) {
while (*moment >= (temp = increment(quant))) {
if (quant == temp)
break;
quant = temp;
}
}
#endif
return quant;
}
@ -150,8 +160,8 @@ date_t interval_t::increment(const date_t& moment) const
date_t future(moment);
if (years) future += gregorian::years(years);
if (months) future += gregorian::years(months);
if (days) future += gregorian::years(days);
if (months) future += gregorian::months(months);
if (days) future += gregorian::days(days);
return future;
}
@ -160,11 +170,9 @@ namespace {
void parse_inclusion_specifier(const string& word,
date_t * begin, date_t * end)
{
#if 0
// jww (2008-05-08): Implement!
struct std::tm when;
if (! parse_date_mask(word.c_str(), &when))
if (! parse_date_mask(word.c_str(), when))
throw_(date_error, "Could not parse date mask: " << word);
when.tm_hour = 0;
@ -177,7 +185,7 @@ namespace {
bool saw_day = true;
if (when.tm_year == -1) {
when.tm_year = date_t::current_year - 1900;
when.tm_year = current_year - 1900;
saw_year = false;
}
if (when.tm_mon == -1) {
@ -195,19 +203,14 @@ namespace {
}
if (begin) {
*begin = std::mktime(&when);
assert(int(*begin) != -1);
if (end) {
*begin = gregorian::date_from_tm(when);
if (end)
*end = interval_t(saw_day ? 1 : 0, saw_mon ? 1 : 0,
saw_year ? 1 : 0).increment(*begin);
assert(int(*end) != -1);
}
}
else if (end) {
*end = std::mktime(&when);
assert(int(*end) != -1);
*end = gregorian::date_from_tm(when);
}
#endif
}
inline void read_lower_word(std::istream& in, string& word) {
@ -235,18 +238,13 @@ namespace {
}
if (word == "month") {
#if 0
// jww (2008-05-08):
std::strftime(buf, 31, "%B", date_t::now.localtime());
#endif
time_t now = to_time_t(current_time);
std::strftime(buf, 31, "%B", localtime(&now));
word = buf;
mon_spec = true;
}
else if (word == "year") {
#if 0
// jww (2008-05-08):
std::strftime(buf, 31, "%Y", date_t::now.localtime());
#endif
std::sprintf(buf, "%04d", current_year);
word = buf;
}
@ -349,62 +347,4 @@ void interval_t::parse(std::istream& in)
}
}
namespace {
bool parse_date_mask(const char * date_str, struct std::tm * result)
{
#if 0
// jww (2008-05-08):
if (! date_t::input_format.empty()) {
std::memset(result, -1, sizeof(struct std::tm));
if (strptime(date_str, date_t::input_format.c_str(), result))
return true;
}
for (const char ** f = formats; *f; f++) {
std::memset(result, INT_MAX, sizeof(struct std::tm));
if (strptime(date_str, *f, result))
return true;
}
#endif
return false;
}
bool parse_date(const char * date_str, std::time_t * result, const int year)
{
#if 0
// jww (2008-05-08):
struct std::tm when;
if (! parse_date_mask(date_str, &when))
return false;
when.tm_hour = 0;
when.tm_min = 0;
when.tm_sec = 0;
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_mday == -1)
when.tm_mday = 1;
*result = std::mktime(&when);
#endif
return true;
}
bool quick_parse_date(const char * date_str, std::time_t * result)
{
#if 0
// jww (2008-05-08):
return parse_date(date_str, result, date_t::current_year);
#else
return false;
#endif
}
}
} // namespace ledger

158
times.h
View file

@ -54,80 +54,8 @@ inline bool is_valid(const date_t& moment) {
extern const datetime_t& current_time;
extern const date_t& current_date;
extern int current_year;
extern string input_time_format;
extern string output_time_format;
struct interval_t
{
unsigned short years;
unsigned short months;
unsigned short days;
date_t begin;
date_t end;
mutable bool advanced;
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),
begin(_begin), end(_end), advanced(false) {
TRACE_CTOR(interval_t,
"int, int, int, const date_t&, const date_t&");
}
interval_t(const interval_t& other)
: years(other.years),
months(other.months),
days(other.days),
begin(other.begin),
end(other.end),
advanced(other.advanced) {
TRACE_CTOR(interval_t, "copy");
}
interval_t(const string& desc)
: years(0), months(0), days(0),
begin(), end(), advanced(false) {
TRACE_CTOR(interval_t, "const string&");
std::istringstream stream(desc);
parse(stream);
}
~interval_t() throw() {
TRACE_DTOR(interval_t);
}
operator bool() const {
return years > 0 || months > 0 || days > 0;
}
void start(const date_t& moment) {
begin = first(moment);
}
date_t first(const date_t& moment = date_t()) const;
date_t increment(const date_t&) const;
void parse(std::istream& in);
};
#if 0
inline datetime_t ptime_local_to_utc(const datetime_t& when) {
struct std::tm tm_gmt = to_tm(when);
return boost::posix_time::from_time_t(std::mktime(&tm_gmt));
}
// jww (2007-04-18): I need to make a general parsing function
// instead, and then make these into private methods.
inline datetime_t ptime_from_local_date_string(const string& date_string) {
return ptime_local_to_utc(datetime_t(boost::gregorian::from_string(date_string),
time_duration()));
}
inline datetime_t ptime_from_local_time_string(const string& time_string) {
return ptime_local_to_utc(boost::posix_time::time_from_string(time_string));
}
#endif
extern optional<string> input_date_format;
extern string output_date_format;
inline datetime_t parse_datetime(const string& str) {
return parse_datetime(str.c_str());
@ -135,8 +63,9 @@ inline datetime_t parse_datetime(const string& str) {
datetime_t parse_datetime(const char * str);
inline date_t parse_date(const string& str) {
return gregorian::from_string(str);
return parse_date(str.c_str());
}
date_t parse_date(const char * str);
inline std::time_t to_time_t(const ptime& t)
{
@ -148,33 +77,78 @@ inline std::time_t to_time_t(const ptime& t)
return (t-start).total_seconds();
}
inline string format_datetime(const datetime_t& when) {
char buf[64];
inline string format_datetime(const datetime_t& when)
{
char buf[256];
time_t moment = to_time_t(when);
// jww (2008-07-29): Need to make the output format configurable
std::strftime(buf, 63, "%Y/%m/%d", std::localtime(&moment));
std::strftime(buf, 255, (output_date_format + " %H:%M:%S").c_str(),
std::localtime(&moment));
return buf;
}
inline string format_date(const date_t& when) {
return to_iso_extended_string(when);
inline string format_date(const date_t& when,
const optional<string>& format = none)
{
if (format) {
char buf[256];
std::tm moment = gregorian::to_tm(when);
std::strftime(buf, 255, format->c_str(), &moment);
return buf;
} else {
return to_iso_extended_string(when);
}
}
#if 0
struct intorchar
struct interval_t
{
int ival;
string sval;
int years;
int months;
int days;
date_t begin;
date_t end;
intorchar() : ival(-1) {}
intorchar(int val) : ival(val) {}
intorchar(const string& val) : ival(-1), sval(val) {}
intorchar(const intorchar& o) : ival(o.ival), sval(o.sval) {}
mutable bool advanced;
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),
begin(_begin), end(_end), advanced(false) {
TRACE_CTOR(interval_t, "int, int, int, const date_t&, const date_t&");
}
interval_t(const interval_t& other)
: years(other.years),
months(other.months),
days(other.days),
begin(other.begin),
end(other.end),
advanced(other.advanced) {
TRACE_CTOR(interval_t, "copy");
}
interval_t(const string& desc)
: years(0), months(0), days(0), begin(), end(), advanced(false) {
TRACE_CTOR(interval_t, "const string&");
std::istringstream stream(desc);
parse(stream);
}
~interval_t() throw() {
TRACE_DTOR(interval_t);
}
operator bool() const {
return years != 0 || months != 0 || days != 0;
}
void start(const date_t& moment) {
begin = first(moment);
}
date_t first(const optional<date_t>& moment = none) const;
date_t increment(const date_t&) const;
void parse(std::istream& in);
};
ledger::datetime_t parse_abs_datetime(std::istream& input);
#endif
} // namespace ledger
#endif // _TIMES_H

11
walk.cc
View file

@ -438,15 +438,10 @@ void subtotal_xacts::report_subtotal(const char * spec_fmt)
std::ostringstream out_date;
if (! spec_fmt) {
string fmt = "- ";
fmt += output_time_format; // jww (2008-04-24): output_date_format?
// jww (2008-04-24): There is no date output function?
#if 0
finish.write(out_date, fmt);
#endif
fmt += output_date_format;
out_date << format_date(finish, string(fmt));
} else {
#if 0
finish.write(out_date, spec_fmt);
#endif
out_date << format_date(finish, string(spec_fmt));
}
entry_temps.push_back(entry_t());

4
walk.h
View file

@ -657,8 +657,8 @@ protected:
values_map values;
bool remember_components;
std::list<entry_t> entry_temps;
std::list<xact_t> xact_temps;
std::list<entry_t> entry_temps;
std::list<xact_t> xact_temps;
public:
date_t start;