First iteration of the new date_interval_t rewrite
This commit is contained in:
parent
1889d449b6
commit
a05353e269
8 changed files with 390 additions and 274 deletions
|
|
@ -562,7 +562,7 @@ void interval_posts::report_subtotal(const date_t& finish)
|
|||
if (exact_periods)
|
||||
subtotal_posts::report_subtotal();
|
||||
else
|
||||
subtotal_posts::report_subtotal(NULL, interval.begin, finish);
|
||||
subtotal_posts::report_subtotal(NULL, *interval.start, finish);
|
||||
}
|
||||
|
||||
last_post = NULL;
|
||||
|
|
@ -572,49 +572,37 @@ void interval_posts::operator()(post_t& post)
|
|||
{
|
||||
date_t date = post.date();
|
||||
|
||||
if ((is_valid(interval.begin) && date < interval.begin) ||
|
||||
(is_valid(interval.end) && date >= interval.end))
|
||||
if (! interval.find_period(post.date(), &last_interval))
|
||||
return;
|
||||
|
||||
if (interval) {
|
||||
if (! is_valid(interval.begin))
|
||||
interval.set_start(date);
|
||||
start = interval.begin;
|
||||
|
||||
date_t quant = interval.increment(interval.begin);
|
||||
if (date >= quant) {
|
||||
if (last_post)
|
||||
report_subtotal(quant - gregorian::days(1));
|
||||
|
||||
date_t temp;
|
||||
while (date >= (temp = interval.increment(quant))) {
|
||||
if (quant == temp)
|
||||
break;
|
||||
interval.begin = quant;
|
||||
quant = temp;
|
||||
if (interval.duration) {
|
||||
if (last_interval) {
|
||||
if (interval != last_interval) {
|
||||
report_subtotal(last_interval.inclusive_end());
|
||||
|
||||
if (generate_empty_posts) {
|
||||
// Generate a null posting, so the intervening periods can be
|
||||
// seen when -E is used, or if the calculated amount ends up being
|
||||
// non-zero
|
||||
xact_temps.push_back(xact_t());
|
||||
xact_t& null_xact = xact_temps.back();
|
||||
null_xact.add_flags(ITEM_TEMP);
|
||||
null_xact._date = quant - gregorian::days(1);
|
||||
for (++last_interval; interval != last_interval; ++last_interval) {
|
||||
// Generate a null posting, so the intervening periods can be
|
||||
// seen when -E is used, or if the calculated amount ends up being
|
||||
// non-zero
|
||||
xact_temps.push_back(xact_t());
|
||||
xact_t& null_xact = xact_temps.back();
|
||||
null_xact.add_flags(ITEM_TEMP);
|
||||
null_xact._date = last_interval.inclusive_end();
|
||||
|
||||
post_temps.push_back(post_t(&empty_account));
|
||||
post_t& null_post = post_temps.back();
|
||||
null_post.add_flags(ITEM_TEMP | POST_CALCULATED);
|
||||
null_post.amount = 0L;
|
||||
null_xact.add_post(&null_post);
|
||||
post_temps.push_back(post_t(&empty_account));
|
||||
post_t& null_post = post_temps.back();
|
||||
null_post.add_flags(ITEM_TEMP | POST_CALCULATED);
|
||||
null_post.amount = 0L;
|
||||
null_xact.add_post(&null_post);
|
||||
|
||||
last_post = &null_post;
|
||||
subtotal_posts::operator()(null_post);
|
||||
last_post = &null_post;
|
||||
subtotal_posts::operator()(null_post);
|
||||
|
||||
report_subtotal(quant - gregorian::days(1));
|
||||
report_subtotal(last_interval.inclusive_end());
|
||||
}
|
||||
}
|
||||
}
|
||||
start = interval.begin = quant;
|
||||
}
|
||||
subtotal_posts::operator()(post);
|
||||
} else {
|
||||
|
|
@ -745,7 +733,7 @@ void generate_posts::add_period_xacts(period_xacts_list& period_xacts)
|
|||
add_post(xact->period, *post);
|
||||
}
|
||||
|
||||
void generate_posts::add_post(const interval_t& period, post_t& post)
|
||||
void generate_posts::add_post(const date_interval_t& period, post_t& post)
|
||||
{
|
||||
pending_posts.push_back(pending_posts_pair(period, &post));
|
||||
}
|
||||
|
|
@ -759,6 +747,7 @@ void budget_posts::report_budget_items(const date_t& date)
|
|||
do {
|
||||
reported = false;
|
||||
foreach (pending_posts_list::value_type& pair, pending_posts) {
|
||||
#if 0
|
||||
date_t& begin = pair.first.begin;
|
||||
if (! is_valid(begin)) {
|
||||
pair.first.set_start(date);
|
||||
|
|
@ -790,6 +779,7 @@ void budget_posts::report_budget_items(const date_t& date)
|
|||
|
||||
reported = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} while (reported);
|
||||
}
|
||||
|
|
@ -823,11 +813,12 @@ void budget_posts::operator()(post_t& post)
|
|||
}
|
||||
}
|
||||
|
||||
void forecast_posts::add_post(const interval_t& period, post_t& post)
|
||||
void forecast_posts::add_post(const date_interval_t& period, post_t& post)
|
||||
{
|
||||
generate_posts::add_post(period, post);
|
||||
|
||||
interval_t& i = pending_posts.back().first;
|
||||
date_interval_t& i = pending_posts.back().first;
|
||||
#if 0
|
||||
if (! is_valid(i.begin)) {
|
||||
i.set_start(CURRENT_DATE());
|
||||
i.begin = i.increment(i.begin);
|
||||
|
|
@ -835,6 +826,7 @@ void forecast_posts::add_post(const interval_t& period, post_t& post)
|
|||
while (i.begin < CURRENT_DATE())
|
||||
i.begin = i.increment(i.begin);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void forecast_posts::flush()
|
||||
|
|
@ -842,6 +834,7 @@ void forecast_posts::flush()
|
|||
posts_list passed;
|
||||
date_t last;
|
||||
|
||||
#if 0
|
||||
while (pending_posts.size() > 0) {
|
||||
pending_posts_list::iterator least = pending_posts.begin();
|
||||
for (pending_posts_list::iterator i = ++pending_posts.begin();
|
||||
|
|
@ -900,6 +893,7 @@ void forecast_posts::flush()
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
item_handler<post_t>::flush();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -558,22 +558,22 @@ public:
|
|||
*/
|
||||
class interval_posts : public subtotal_posts
|
||||
{
|
||||
interval_t interval;
|
||||
post_t * last_post;
|
||||
account_t empty_account;
|
||||
bool exact_periods;
|
||||
bool generate_empty_posts;
|
||||
date_t start;
|
||||
date_interval_t interval;
|
||||
date_interval_t last_interval;
|
||||
post_t * last_post;
|
||||
account_t empty_account;
|
||||
bool exact_periods;
|
||||
bool generate_empty_posts;
|
||||
|
||||
interval_posts();
|
||||
|
||||
public:
|
||||
|
||||
interval_posts(post_handler_ptr _handler,
|
||||
expr_t& amount_expr,
|
||||
const interval_t& _interval,
|
||||
bool _exact_periods = false,
|
||||
bool _generate_empty_posts = false)
|
||||
interval_posts(post_handler_ptr _handler,
|
||||
expr_t& amount_expr,
|
||||
const date_interval_t& _interval,
|
||||
bool _exact_periods = false,
|
||||
bool _generate_empty_posts = false)
|
||||
: subtotal_posts(_handler, amount_expr), interval(_interval),
|
||||
last_post(NULL), empty_account(NULL, _("<None>")),
|
||||
exact_periods(_exact_periods),
|
||||
|
|
@ -588,9 +588,11 @@ public:
|
|||
void report_subtotal(const date_t& finish);
|
||||
|
||||
virtual void flush() {
|
||||
if (last_post && interval)
|
||||
report_subtotal(interval.increment(interval.begin) - gregorian::days(1));
|
||||
subtotal_posts::flush();
|
||||
if (last_post && interval.duration) {
|
||||
if (interval.is_valid())
|
||||
report_subtotal(interval.inclusive_end());
|
||||
subtotal_posts::flush();
|
||||
}
|
||||
}
|
||||
virtual void operator()(post_t& post);
|
||||
};
|
||||
|
|
@ -725,11 +727,11 @@ class generate_posts : public item_handler<post_t>
|
|||
generate_posts();
|
||||
|
||||
protected:
|
||||
typedef std::pair<interval_t, post_t *> pending_posts_pair;
|
||||
typedef std::list<pending_posts_pair> pending_posts_list;
|
||||
typedef std::pair<date_interval_t, post_t *> pending_posts_pair;
|
||||
typedef std::list<pending_posts_pair> pending_posts_list;
|
||||
|
||||
pending_posts_list pending_posts;
|
||||
std::list<xact_t> xact_temps;
|
||||
std::list<xact_t> xact_temps;
|
||||
std::list<post_t> post_temps;
|
||||
|
||||
public:
|
||||
|
|
@ -745,7 +747,7 @@ public:
|
|||
|
||||
void add_period_xacts(period_xacts_list& period_xacts);
|
||||
|
||||
virtual void add_post(const interval_t& period, post_t& post);
|
||||
virtual void add_post(const date_interval_t& period, post_t& post);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -800,7 +802,7 @@ class forecast_posts : public generate_posts
|
|||
TRACE_DTOR(forecast_posts);
|
||||
}
|
||||
|
||||
virtual void add_post(const interval_t& period, post_t& post);
|
||||
virtual void add_post(const date_interval_t& period, post_t& post);
|
||||
virtual void flush();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -160,25 +160,54 @@ value_t period_command(call_scope_t& args)
|
|||
report_t& report(find_scope<report_t>(args));
|
||||
std::ostream& out(report.output_stream);
|
||||
|
||||
interval_t interval(arg);
|
||||
date_interval_t interval(arg);
|
||||
|
||||
out << _("global details => ") << std::endl << std::endl;
|
||||
|
||||
if (interval.start)
|
||||
out << _(" start: ") << format_date(*interval.start) << std::endl;
|
||||
else
|
||||
out << _(" start: TODAY: ") << format_date(CURRENT_DATE()) << std::endl;
|
||||
if (interval.end)
|
||||
out << _(" end: ") << format_date(*interval.end) << std::endl;
|
||||
|
||||
if (interval.skip_duration)
|
||||
out << _(" skip: ") << *interval.skip_duration << std::endl;
|
||||
if (interval.factor)
|
||||
out << _(" factor: ") << interval.factor << std::endl;
|
||||
if (interval.duration)
|
||||
out << _("duration: ") << *interval.duration << std::endl;
|
||||
|
||||
if (interval.find_period(interval.start ?
|
||||
*interval.start : CURRENT_DATE())) {
|
||||
out << std::endl
|
||||
<< _("after finding first period => ") << std::endl
|
||||
<< std::endl;
|
||||
|
||||
if (interval.start)
|
||||
out << _(" start: ") << format_date(*interval.start) << std::endl;
|
||||
if (interval.end)
|
||||
out << _(" end: ") << format_date(*interval.end) << std::endl;
|
||||
|
||||
if (interval.skip_duration)
|
||||
out << _(" skip: ") << *interval.skip_duration << std::endl;
|
||||
if (interval.factor)
|
||||
out << _(" factor: ") << interval.factor << std::endl;
|
||||
if (interval.duration)
|
||||
out << _("duration: ") << *interval.duration << std::endl;
|
||||
|
||||
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++) {
|
||||
for (int i = 0; i < 20 && interval; i++, ++interval) {
|
||||
out << std::right;
|
||||
out.width(2);
|
||||
|
||||
out << i << "): " << format_date(date) << std::endl;
|
||||
out << i << "): " << format_date(*interval.start);
|
||||
if (interval.end_of_duration)
|
||||
out << " -- " << format_date(interval.inclusive_end());
|
||||
out << std::endl;
|
||||
|
||||
date = interval.increment(date);
|
||||
if (is_valid(interval.end) && date >= interval.end)
|
||||
if (! interval.skip_duration)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
12
src/report.h
12
src/report.h
|
|
@ -240,14 +240,14 @@ public:
|
|||
});
|
||||
|
||||
OPTION_(report_t, begin_, DO_(args) { // -b
|
||||
interval_t interval(args[0].to_string());
|
||||
if (! is_valid(interval.begin))
|
||||
date_interval_t interval(args[0].to_string());
|
||||
if (! interval.start)
|
||||
throw_(std::invalid_argument,
|
||||
_("Could not determine beginning of period '%1'")
|
||||
<< args[0].to_string());
|
||||
|
||||
string predicate =
|
||||
"date>=[" + to_iso_extended_string(interval.begin) + "]";
|
||||
"date>=[" + to_iso_extended_string(*interval.start) + "]";
|
||||
parent->HANDLER(limit_).on(predicate);
|
||||
});
|
||||
|
||||
|
|
@ -355,14 +355,14 @@ public:
|
|||
OPTION(report_t, empty); // -E
|
||||
|
||||
OPTION_(report_t, end_, DO_(args) { // -e
|
||||
interval_t interval(args[0].to_string());
|
||||
if (! is_valid(interval.begin))
|
||||
date_interval_t interval(args[0].to_string());
|
||||
if (! interval.start)
|
||||
throw_(std::invalid_argument,
|
||||
_("Could not determine end of period '%1'")
|
||||
<< args[0].to_string());
|
||||
|
||||
string predicate =
|
||||
"date<[" + to_iso_extended_string(interval.begin) + "]";
|
||||
"date<[" + to_iso_extended_string(*interval.start) + "]";
|
||||
parent->HANDLER(limit_).on(predicate);
|
||||
#if 0
|
||||
terminus = interval.begin;
|
||||
|
|
|
|||
342
src/times.cc
342
src/times.cc
|
|
@ -107,25 +107,25 @@ namespace {
|
|||
}
|
||||
}
|
||||
|
||||
int string_to_day_of_week(const std::string& str)
|
||||
date_time::weekdays string_to_day_of_week(const std::string& str)
|
||||
{
|
||||
if (str == _("sun") || str == _("sunday") || str == "0")
|
||||
return 0;
|
||||
return gregorian::Sunday;
|
||||
else if (str == _("mon") || str == _("monday") || str == "1")
|
||||
return 1;
|
||||
return gregorian::Monday;
|
||||
else if (str == _("tue") || str == _("tuesday") || str == "2")
|
||||
return 2;
|
||||
return gregorian::Tuesday;
|
||||
else if (str == _("wed") || str == _("wednesday") || str == "3")
|
||||
return 3;
|
||||
return gregorian::Wednesday;
|
||||
else if (str == _("thu") || str == _("thursday") || str == "4")
|
||||
return 4;
|
||||
return gregorian::Thursday;
|
||||
else if (str == _("fri") || str == _("friday") || str == "5")
|
||||
return 5;
|
||||
return gregorian::Friday;
|
||||
else if (str == _("sat") || str == _("saturday") || str == "6")
|
||||
return 6;
|
||||
return gregorian::Saturday;
|
||||
|
||||
assert(false);
|
||||
return -1;
|
||||
return gregorian::Sunday;
|
||||
}
|
||||
|
||||
datetime_t parse_datetime(const char * str, int)
|
||||
|
|
@ -145,50 +145,156 @@ date_t parse_date(const char * str, int current_year)
|
|||
return gregorian::date_from_tm(when);
|
||||
}
|
||||
|
||||
date_t interval_t::first(const optional<date_t>& moment)
|
||||
date_t date_interval_t::add_duration(const date_t& date,
|
||||
const duration_t& duration)
|
||||
{
|
||||
if (! is_valid(begin)) {
|
||||
// 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.
|
||||
assert(moment);
|
||||
if (duration.type() == typeid(gregorian::days))
|
||||
return date + boost::get<gregorian::days>(duration);
|
||||
else if (duration.type() == typeid(gregorian::weeks))
|
||||
return date + boost::get<gregorian::weeks>(duration);
|
||||
else if (duration.type() == typeid(gregorian::months))
|
||||
return date + boost::get<gregorian::months>(duration);
|
||||
else
|
||||
assert(duration.type() == typeid(gregorian::years));
|
||||
return date + boost::get<gregorian::years>(duration);
|
||||
}
|
||||
|
||||
if (months > 0 || years > 0) {
|
||||
begin = date_t(moment->year(), gregorian::Jan, 1);
|
||||
} else {
|
||||
begin = date_t(*moment - gregorian::days(400));
|
||||
date_t date_interval_t::subtract_duration(const date_t& date,
|
||||
const duration_t& duration)
|
||||
{
|
||||
if (duration.type() == typeid(gregorian::days))
|
||||
return date - boost::get<gregorian::days>(duration);
|
||||
else if (duration.type() == typeid(gregorian::weeks))
|
||||
return date - boost::get<gregorian::weeks>(duration);
|
||||
else if (duration.type() == typeid(gregorian::months))
|
||||
return date - boost::get<gregorian::months>(duration);
|
||||
else
|
||||
assert(duration.type() == typeid(gregorian::years));
|
||||
return date - boost::get<gregorian::years>(duration);
|
||||
}
|
||||
|
||||
// jww (2009-02-21): Add support for starting a week on any day
|
||||
if (weekly) { // move it to a Sunday
|
||||
while (begin.day_of_week() != start_of_week)
|
||||
begin += gregorian::days(1);
|
||||
std::ostream& operator<<(std::ostream& out,
|
||||
const date_interval_t::duration_t& duration)
|
||||
{
|
||||
if (duration.type() == typeid(gregorian::days))
|
||||
out << boost::get<gregorian::days>(duration).days()
|
||||
<< " days";
|
||||
else if (duration.type() == typeid(gregorian::weeks))
|
||||
out << (boost::get<gregorian::weeks>(duration).days() / 7)
|
||||
<< " weeks";
|
||||
else if (duration.type() == typeid(gregorian::months))
|
||||
out << boost::get<gregorian::months>(duration).number_of_months()
|
||||
<< " months";
|
||||
else {
|
||||
assert(duration.type() == typeid(gregorian::years));
|
||||
out << boost::get<gregorian::years>(duration).number_of_years()
|
||||
<< " years";
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
bool date_interval_t::find_period(const date_t& date,
|
||||
date_interval_t * last_interval)
|
||||
{
|
||||
if (end && date > *end)
|
||||
return false;
|
||||
|
||||
if (! start) {
|
||||
if (duration) {
|
||||
// The interval object has not been seeded with a start date yet, so
|
||||
// find the nearest period before on on date which fits, if possible.
|
||||
//
|
||||
// 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.
|
||||
if (duration->type() == typeid(gregorian::months) ||
|
||||
duration->type() == typeid(gregorian::weeks)) {
|
||||
start = date_t(date.year(), gregorian::Jan, 1);
|
||||
} else {
|
||||
start = date_t(date - gregorian::days(400));
|
||||
|
||||
if (duration->type() == typeid(gregorian::weeks)) {
|
||||
// Move it to a Sunday
|
||||
while (start->day_of_week() != start_of_week)
|
||||
*start += gregorian::days(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
date_t quant(begin);
|
||||
if (date < *start)
|
||||
return false;
|
||||
|
||||
if (moment && *moment >= quant) {
|
||||
date_t temp;
|
||||
while (*moment >= (temp = increment(quant))) {
|
||||
if (quant == temp)
|
||||
break;
|
||||
quant = temp;
|
||||
}
|
||||
// If there is no duration, then if we've reached here the date falls
|
||||
// between begin and end.
|
||||
if (! duration) {
|
||||
if (! start && ! end)
|
||||
throw_(date_error,
|
||||
_("Invalid date interval: neither start, nor end, nor duration"));
|
||||
return true;
|
||||
}
|
||||
return quant;
|
||||
|
||||
if (! end_of_duration)
|
||||
end_of_duration = add_duration(*start, *duration);
|
||||
|
||||
if (! skip_duration)
|
||||
skip_duration = duration;
|
||||
|
||||
if (! next)
|
||||
next = add_duration(*start, *skip_duration);
|
||||
|
||||
if (date < *end_of_duration)
|
||||
return true;
|
||||
|
||||
// If we've reached here, it means the date does not fall into the current
|
||||
// interval, so we must seek another interval that does match -- unless we
|
||||
// pass by date in so doing, which means we shouldn't alter the current
|
||||
// period of the interval at all.
|
||||
|
||||
date_t scan = *next;
|
||||
date_t end_of_scan = add_duration(scan, *duration);
|
||||
|
||||
while (date >= scan && (! end || scan < *end)) {
|
||||
if (date < end_of_scan) {
|
||||
if (last_interval) {
|
||||
last_interval->start = start;
|
||||
last_interval->next = next;
|
||||
last_interval->end_of_duration = end_of_duration;
|
||||
}
|
||||
start = scan;
|
||||
end_of_duration = end_of_scan;
|
||||
next = none;
|
||||
return true;
|
||||
}
|
||||
scan = add_duration(scan, *skip_duration);
|
||||
end_of_scan = add_duration(scan, *duration);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
date_t interval_t::increment(const date_t& moment) const
|
||||
date_interval_t& date_interval_t::operator++()
|
||||
{
|
||||
date_t future(moment);
|
||||
if (! start)
|
||||
throw_(date_error, _("Cannot increment an unstarted date interval"));
|
||||
|
||||
if (years) future += gregorian::years(years);
|
||||
if (months) future += gregorian::months(months);
|
||||
if (days) future += gregorian::days(days);
|
||||
if (! skip_duration) {
|
||||
if (duration)
|
||||
skip_duration = duration;
|
||||
else
|
||||
throw_(date_error,
|
||||
_("Cannot increment a date interval without a duration"));
|
||||
}
|
||||
|
||||
return future;
|
||||
*start = add_duration(*start, *skip_duration);
|
||||
|
||||
if (end && *start >= *end)
|
||||
start = none;
|
||||
else
|
||||
end_of_duration = add_duration(*start, *duration);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
|
@ -230,9 +336,15 @@ namespace {
|
|||
|
||||
if (begin) {
|
||||
*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);
|
||||
|
||||
if (end) {
|
||||
if (saw_year)
|
||||
*end = *begin + gregorian::years(1);
|
||||
else if (saw_mon)
|
||||
*end = *begin + gregorian::months(1);
|
||||
else if (saw_day)
|
||||
*end = *begin + gregorian::days(1);
|
||||
}
|
||||
}
|
||||
else if (end) {
|
||||
*end = gregorian::date_from_tm(when);
|
||||
|
|
@ -245,14 +357,14 @@ namespace {
|
|||
word[i] = static_cast<char>(std::tolower(word[i]));
|
||||
}
|
||||
|
||||
void parse_date_words(std::istream& in, string& word,
|
||||
date_t * begin, date_t * end)
|
||||
void parse_date_words(std::istream& in,
|
||||
string& word,
|
||||
date_interval_t& interval,
|
||||
bool look_for_start = true,
|
||||
bool look_for_end = true)
|
||||
{
|
||||
string type;
|
||||
|
||||
bool mon_spec = false;
|
||||
char buf[32];
|
||||
|
||||
if (word == _("this") || word == _("last") || word == _("next")) {
|
||||
type = word;
|
||||
if (! in.eof())
|
||||
|
|
@ -263,59 +375,48 @@ namespace {
|
|||
type = _("this");
|
||||
}
|
||||
|
||||
if (word == _("month")) {
|
||||
time_t now = to_time_t(CURRENT_TIME());
|
||||
std::strftime(buf, 31, "%B", localtime(&now));
|
||||
word = buf;
|
||||
mon_spec = true;
|
||||
}
|
||||
else if (word == _("year")) {
|
||||
int year = CURRENT_DATE().year();
|
||||
std::sprintf(buf, "%04d", year);
|
||||
word = buf;
|
||||
}
|
||||
else if (word == _("today")) {
|
||||
if (begin)
|
||||
*begin = CURRENT_DATE();
|
||||
if (end) {
|
||||
*end = CURRENT_DATE();
|
||||
*end += gregorian::days(1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
date_t start = CURRENT_DATE();
|
||||
date_t end;
|
||||
bool parse_specifier = false;
|
||||
|
||||
parse_inclusion_specifier(word, begin, end);
|
||||
date_interval_t::duration_t duration;
|
||||
|
||||
assert(look_for_start || look_for_end);
|
||||
|
||||
if (word == _("year")) {
|
||||
duration = gregorian::years(1);
|
||||
start = gregorian::date(start.year(), 1, 1);
|
||||
}
|
||||
else if (word == _("month")) {
|
||||
duration = gregorian::months(1);
|
||||
start = gregorian::date(start.year(), start.month(), 1);
|
||||
}
|
||||
else if (word == _("today") || word == _("day")) {
|
||||
duration = gregorian::days(1);
|
||||
}
|
||||
else {
|
||||
parse_specifier = true;
|
||||
}
|
||||
end = date_interval_t::add_duration(start, duration);
|
||||
|
||||
if (parse_specifier)
|
||||
parse_inclusion_specifier(word, &start, &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);
|
||||
}
|
||||
start = date_interval_t::subtract_duration(start, duration);
|
||||
end = date_interval_t::subtract_duration(end, duration);
|
||||
}
|
||||
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);
|
||||
}
|
||||
start = date_interval_t::add_duration(start, duration);
|
||||
end = date_interval_t::add_duration(end, duration);
|
||||
}
|
||||
|
||||
if (look_for_start) interval.start = start;
|
||||
if (look_for_end) interval.end = end;
|
||||
}
|
||||
}
|
||||
|
||||
void interval_t::parse(std::istream& in)
|
||||
void date_interval_t::parse(std::istream& in)
|
||||
{
|
||||
string word;
|
||||
|
||||
|
|
@ -327,65 +428,62 @@ void interval_t::parse(std::istream& in)
|
|||
int quantity = lexical_cast<int>(word);
|
||||
read_lower_word(in, word);
|
||||
if (word == _("days"))
|
||||
days = quantity;
|
||||
else if (word == _("weeks")) {
|
||||
days = 7 * quantity;
|
||||
weekly = true;
|
||||
}
|
||||
duration = gregorian::days(quantity);
|
||||
else if (word == _("weeks"))
|
||||
duration = gregorian::weeks(quantity);
|
||||
else if (word == _("months"))
|
||||
months = quantity;
|
||||
duration = gregorian::months(quantity);
|
||||
else if (word == _("quarters"))
|
||||
months = 3 * quantity;
|
||||
duration = gregorian::months(3 * quantity);
|
||||
else if (word == _("years"))
|
||||
years = quantity;
|
||||
duration = gregorian::years(quantity);
|
||||
}
|
||||
else if (word == _("day"))
|
||||
days = 1;
|
||||
else if (word == _("week")) {
|
||||
days = 7;
|
||||
weekly = true;
|
||||
}
|
||||
duration = gregorian::days(1);
|
||||
else if (word == _("week"))
|
||||
duration = gregorian::weeks(1);
|
||||
else if (word == _("month"))
|
||||
months = 1;
|
||||
duration = gregorian::months(1);
|
||||
else if (word == _("quarter"))
|
||||
months = 3;
|
||||
duration = gregorian::months(3);
|
||||
else if (word == _("year"))
|
||||
years = 1;
|
||||
duration = gregorian::years(1);
|
||||
}
|
||||
else if (word == _("daily"))
|
||||
days = 1;
|
||||
else if (word == _("weekly")) {
|
||||
days = 7;
|
||||
weekly = true;
|
||||
}
|
||||
duration = gregorian::days(1);
|
||||
else if (word == _("weekly"))
|
||||
duration = gregorian::weeks(1);
|
||||
else if (word == _("biweekly"))
|
||||
days = 14;
|
||||
duration = gregorian::weeks(2);
|
||||
else if (word == _("monthly"))
|
||||
months = 1;
|
||||
duration = gregorian::months(1);
|
||||
else if (word == _("bimonthly"))
|
||||
months = 2;
|
||||
duration = gregorian::months(2);
|
||||
else if (word == _("quarterly"))
|
||||
months = 3;
|
||||
duration = gregorian::months(3);
|
||||
else if (word == _("yearly"))
|
||||
years = 1;
|
||||
duration = gregorian::years(1);
|
||||
else if (word == _("this") || word == _("last") || word == _("next") ||
|
||||
word == _("today")) {
|
||||
parse_date_words(in, word, &begin, &end);
|
||||
parse_date_words(in, word, *this);
|
||||
}
|
||||
else if (word == _("in")) {
|
||||
read_lower_word(in, word);
|
||||
parse_date_words(in, word, &begin, &end);
|
||||
parse_date_words(in, word, *this);
|
||||
}
|
||||
else if (word == _("from") || word == _("since")) {
|
||||
read_lower_word(in, word);
|
||||
parse_date_words(in, word, &begin, NULL);
|
||||
parse_date_words(in, word, *this, true, false);
|
||||
}
|
||||
else if (word == _("to") || word == _("until")) {
|
||||
read_lower_word(in, word);
|
||||
parse_date_words(in, word, NULL, &end);
|
||||
parse_date_words(in, word, *this, false, true);
|
||||
}
|
||||
else {
|
||||
parse_inclusion_specifier(word, &begin, &end);
|
||||
date_t b, e;
|
||||
parse_inclusion_specifier(word, &b, &e);
|
||||
start = b;
|
||||
end = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
134
src/times.h
134
src/times.h
|
|
@ -60,6 +60,7 @@ inline bool is_valid(const datetime_t& moment) {
|
|||
|
||||
typedef boost::gregorian::date date_t;
|
||||
typedef boost::gregorian::date_duration date_duration_t;
|
||||
typedef boost::gregorian::date_iterator date_iterator_t;
|
||||
|
||||
inline bool is_valid(const date_t& moment) {
|
||||
return ! moment.is_not_a_date();
|
||||
|
|
@ -75,7 +76,7 @@ inline bool is_valid(const date_t& moment) {
|
|||
extern int start_of_week;
|
||||
extern optional<std::string> input_date_format;
|
||||
|
||||
int string_to_day_of_week(const std::string& str);
|
||||
date_time::weekdays string_to_day_of_week(const std::string& str);
|
||||
|
||||
datetime_t parse_datetime(const char * str, int current_year = -1);
|
||||
|
||||
|
|
@ -123,94 +124,83 @@ inline std::string format_date(const date_t& when,
|
|||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Brief
|
||||
*
|
||||
* Long.
|
||||
*/
|
||||
struct range_t
|
||||
class date_interval_t : public equality_comparable<date_interval_t>
|
||||
{
|
||||
date_t begin;
|
||||
date_t end;
|
||||
public:
|
||||
typedef variant<gregorian::days,
|
||||
gregorian::weeks,
|
||||
gregorian::months,
|
||||
gregorian::years> duration_t;
|
||||
|
||||
date_range_t(const date_t& _begin = date_t(),
|
||||
const date_t& _end = date_t())
|
||||
: begin(_begin), end(_end) {
|
||||
TRACE_CTOR(date_range_t, "const date_t&, const date_t&");
|
||||
static date_t add_duration(const date_t& date,
|
||||
const duration_t& duration);
|
||||
static date_t subtract_duration(const date_t& date,
|
||||
const duration_t& duration);
|
||||
|
||||
optional<date_t> start;
|
||||
optional<duration_t> skip_duration;
|
||||
std::size_t factor;
|
||||
optional<date_t> next;
|
||||
optional<duration_t> duration;
|
||||
optional<date_t> end_of_duration;
|
||||
optional<date_t> end;
|
||||
|
||||
explicit date_interval_t() : factor(1) {
|
||||
TRACE_CTOR(date_interval_t, "");
|
||||
}
|
||||
date_range_t(const date_range_t& other)
|
||||
: begin(other.begin), end(other.end) {
|
||||
TRACE_CTOR(date_range_t, "copy");
|
||||
date_interval_t(const string& str) : factor(1) {
|
||||
TRACE_CTOR(date_interval_t, "const string&");
|
||||
parse(str);
|
||||
}
|
||||
date_range_t(const string& desc) : begin(), end() {
|
||||
TRACE_CTOR(date_range_t, "const string&");
|
||||
std::istringstream stream(desc);
|
||||
parse(stream);
|
||||
date_interval_t(const date_interval_t& other)
|
||||
: start(other.start),
|
||||
skip_duration(other.skip_duration),
|
||||
factor(other.factor),
|
||||
next(other.next),
|
||||
duration(other.duration),
|
||||
end_of_duration(other.end_of_duration),
|
||||
end(other.end) {
|
||||
TRACE_CTOR(date_interval_t, "copy");
|
||||
}
|
||||
~date_range_t() throw() {
|
||||
TRACE_DTOR(date_range_t);
|
||||
~date_interval_t() throw() {
|
||||
TRACE_DTOR(date_interval_t);
|
||||
}
|
||||
|
||||
bool date_in_range(const date_t& date) {
|
||||
return ((! is_valid(begin) || date >= begin) &&
|
||||
(! is_valid(end) || date < end));
|
||||
}
|
||||
|
||||
void parse(std::istream& in);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Brief
|
||||
*
|
||||
* Long.
|
||||
*/
|
||||
struct interval_t
|
||||
{
|
||||
int years;
|
||||
int months;
|
||||
int days;
|
||||
bool weekly;
|
||||
|
||||
interval_t(int _days = 0,
|
||||
int _months = 0,
|
||||
int _years = 0,
|
||||
bool _weekly = false)
|
||||
: years(_years), months(_months), days(_days), weekly(_weekly) {
|
||||
TRACE_CTOR(interval_t, "int, int, int, bool");
|
||||
}
|
||||
interval_t(const interval_t& other)
|
||||
: years(other.years),
|
||||
months(other.months),
|
||||
days(other.days),
|
||||
weekly(other.weekly) {
|
||||
TRACE_CTOR(interval_t, "copy");
|
||||
}
|
||||
interval_t(const string& desc)
|
||||
: years(0), months(0), days(0), weekly(false) {
|
||||
TRACE_CTOR(interval_t, "const string&");
|
||||
std::istringstream stream(desc);
|
||||
parse(stream);
|
||||
}
|
||||
~interval_t() throw() {
|
||||
TRACE_DTOR(interval_t);
|
||||
bool operator==(const date_interval_t& other) const {
|
||||
return (start == other.start &&
|
||||
(! start || *start == *other.start));
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return years != 0 || months != 0 || days != 0;
|
||||
return is_valid();
|
||||
}
|
||||
|
||||
#if 0
|
||||
void set_start(const date_t& moment) {
|
||||
begin = first(moment);
|
||||
void parse(std::istream& in);
|
||||
|
||||
void parse(const string& str) {
|
||||
std::istringstream in(str);
|
||||
parse(in);
|
||||
}
|
||||
#endif
|
||||
|
||||
date_t first(const optional<date_t>& moment = none);
|
||||
date_t increment(const date_t&) const;
|
||||
bool is_valid() const {
|
||||
return start;
|
||||
}
|
||||
|
||||
void parse(std::istream& in);
|
||||
/** Find the current or next period containing date. Returns true if the
|
||||
date_interval_t object has been altered to reflect the interval
|
||||
containing date, or false if no such period can be found. */
|
||||
bool find_period(const date_t& date, date_interval_t * last_interval = NULL);
|
||||
|
||||
date_t inclusive_end() const {
|
||||
return *end_of_duration - gregorian::days(1);
|
||||
}
|
||||
|
||||
date_interval_t& operator++();
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& out,
|
||||
const date_interval_t::duration_t& duration);
|
||||
|
||||
} // namespace ledger
|
||||
|
||||
#endif // _TIMES_H
|
||||
|
|
|
|||
|
|
@ -205,9 +205,12 @@ void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags)
|
|||
in.get(c);
|
||||
length++;
|
||||
|
||||
interval_t timespan(buf);
|
||||
date_interval_t timespan(buf);
|
||||
if (! timespan)
|
||||
throw_(parse_error,
|
||||
_("Date specifier does not refer to a starting date"));
|
||||
kind = VALUE;
|
||||
value = timespan.first();
|
||||
value = *timespan.start;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -186,8 +186,8 @@ struct auto_xact_finalizer_t : public xact_finalizer_t
|
|||
class period_xact_t : public xact_base_t
|
||||
{
|
||||
public:
|
||||
interval_t period;
|
||||
string period_string;
|
||||
date_interval_t period;
|
||||
string period_string;
|
||||
|
||||
period_xact_t() {
|
||||
TRACE_CTOR(period_xact_t, "");
|
||||
|
|
@ -206,7 +206,7 @@ class period_xact_t : public xact_base_t
|
|||
}
|
||||
|
||||
virtual bool valid() const {
|
||||
return period;
|
||||
return period.is_valid();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue