diff --git a/main.cc b/main.cc index da5ec84f..f58578fd 100644 --- a/main.cc +++ b/main.cc @@ -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. diff --git a/times.cc b/times.cc index ccec34fa..97c0c242 100644 --- a/times.cc +++ b/times.cc @@ -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 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& 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 diff --git a/times.h b/times.h index 4ae665be..306477f5 100644 --- a/times.h +++ b/times.h @@ -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 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& 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& 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 diff --git a/walk.cc b/walk.cc index 20fde04d..4d45dab3 100644 --- a/walk.cc +++ b/walk.cc @@ -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()); diff --git a/walk.h b/walk.h index fe08918b..9024a589 100644 --- a/walk.h +++ b/walk.h @@ -657,8 +657,8 @@ protected: values_map values; bool remember_components; - std::list entry_temps; - std::list xact_temps; + std::list entry_temps; + std::list xact_temps; public: date_t start;