The new period parser is passing all tests
This commit is contained in:
parent
7fe369eb49
commit
e4b3f0bb3a
10 changed files with 283 additions and 160 deletions
|
|
@ -43,7 +43,7 @@
|
||||||
#include "xact.h"
|
#include "xact.h"
|
||||||
|
|
||||||
#define LEDGER_MAGIC 0x4c454447
|
#define LEDGER_MAGIC 0x4c454447
|
||||||
#define ARCHIVE_VERSION 0x03000005
|
#define ARCHIVE_VERSION 0x03000006
|
||||||
|
|
||||||
//BOOST_IS_ABSTRACT(ledger::scope_t)
|
//BOOST_IS_ABSTRACT(ledger::scope_t)
|
||||||
BOOST_CLASS_EXPORT(ledger::scope_t)
|
BOOST_CLASS_EXPORT(ledger::scope_t)
|
||||||
|
|
|
||||||
|
|
@ -515,6 +515,7 @@ void subtotal_posts::report_subtotal(const char * spec_fmt,
|
||||||
optional<date_t> range_start = interval ? interval->start : none;
|
optional<date_t> range_start = interval ? interval->start : none;
|
||||||
optional<date_t> range_finish = interval ? interval->inclusive_end() : none;
|
optional<date_t> range_finish = interval ? interval->inclusive_end() : none;
|
||||||
|
|
||||||
|
if (! range_start || ! range_finish) {
|
||||||
foreach (post_t * post, component_posts) {
|
foreach (post_t * post, component_posts) {
|
||||||
date_t date = post->date();
|
date_t date = post->date();
|
||||||
if (! range_start || date < *range_start)
|
if (! range_start || date < *range_start)
|
||||||
|
|
@ -522,6 +523,7 @@ void subtotal_posts::report_subtotal(const char * spec_fmt,
|
||||||
if (! range_finish || date > *range_finish)
|
if (! range_finish || date > *range_finish)
|
||||||
range_finish = date;
|
range_finish = date;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
component_posts.clear();
|
component_posts.clear();
|
||||||
|
|
||||||
std::ostringstream out_date;
|
std::ostringstream out_date;
|
||||||
|
|
|
||||||
|
|
@ -169,7 +169,7 @@ value_t period_command(call_scope_t& args)
|
||||||
out << std::endl;
|
out << std::endl;
|
||||||
|
|
||||||
date_interval_t interval(arg);
|
date_interval_t interval(arg);
|
||||||
interval.dump(out);
|
interval.dump(out, report.session.current_year);
|
||||||
|
|
||||||
return NULL_VALUE;
|
return NULL_VALUE;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -120,14 +120,15 @@ void report_t::normalize_options(const string& verb)
|
||||||
|
|
||||||
date_interval_t interval(HANDLER(period_).str());
|
date_interval_t interval(HANDLER(period_).str());
|
||||||
|
|
||||||
if (! HANDLED(begin_) && interval.start) {
|
optional<date_t> begin = interval.begin(session.current_year);
|
||||||
string predicate =
|
optional<date_t> end = interval.end(session.current_year);
|
||||||
"date>=[" + to_iso_extended_string(*interval.start) + "]";
|
|
||||||
|
if (! HANDLED(begin_) && begin) {
|
||||||
|
string predicate = "date>=[" + to_iso_extended_string(*begin) + "]";
|
||||||
HANDLER(limit_).on(string("?normalize"), predicate);
|
HANDLER(limit_).on(string("?normalize"), predicate);
|
||||||
}
|
}
|
||||||
if (! HANDLED(end_) && interval.finish) {
|
if (! HANDLED(end_) && end) {
|
||||||
string predicate =
|
string predicate = "date<[" + to_iso_extended_string(*end) + "]";
|
||||||
"date<[" + to_iso_extended_string(*interval.finish) + "]";
|
|
||||||
HANDLER(limit_).on(string("?normalize"), predicate);
|
HANDLER(limit_).on(string("?normalize"), predicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
27
src/report.h
27
src/report.h
|
|
@ -50,6 +50,7 @@
|
||||||
#include "option.h"
|
#include "option.h"
|
||||||
#include "commodity.h"
|
#include "commodity.h"
|
||||||
#include "annotate.h"
|
#include "annotate.h"
|
||||||
|
#include "session.h"
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
@ -352,7 +353,7 @@ public:
|
||||||
set_expr(args[0].to_string(), args[1].to_string());
|
set_expr(args[0].to_string(), args[1].to_string());
|
||||||
});
|
});
|
||||||
|
|
||||||
OPTION(report_t, amount_data);
|
OPTION(report_t, amount_data); // -j
|
||||||
OPTION(report_t, anon);
|
OPTION(report_t, anon);
|
||||||
|
|
||||||
OPTION_(report_t, average, DO() { // -A
|
OPTION_(report_t, average, DO() { // -A
|
||||||
|
|
@ -378,13 +379,13 @@ public:
|
||||||
|
|
||||||
OPTION_(report_t, begin_, DO_(args) { // -b
|
OPTION_(report_t, begin_, DO_(args) { // -b
|
||||||
date_interval_t interval(args[1].to_string());
|
date_interval_t interval(args[1].to_string());
|
||||||
if (! interval.start)
|
optional<date_t> begin = interval.begin(parent->session.current_year);
|
||||||
|
if (! begin)
|
||||||
throw_(std::invalid_argument,
|
throw_(std::invalid_argument,
|
||||||
_("Could not determine beginning of period '%1'")
|
_("Could not determine beginning of period '%1'")
|
||||||
<< args[1].to_string());
|
<< args[1].to_string());
|
||||||
|
|
||||||
string predicate =
|
string predicate = "date>=[" + to_iso_extended_string(*begin) + "]";
|
||||||
"date>=[" + to_iso_extended_string(*interval.start) + "]";
|
|
||||||
parent->HANDLER(limit_).on(string("--begin"), predicate);
|
parent->HANDLER(limit_).on(string("--begin"), predicate);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -525,16 +526,18 @@ public:
|
||||||
|
|
||||||
OPTION_(report_t, end_, DO_(args) { // -e
|
OPTION_(report_t, end_, DO_(args) { // -e
|
||||||
date_interval_t interval(args[1].to_string());
|
date_interval_t interval(args[1].to_string());
|
||||||
if (! interval.start)
|
// Use begin() here so that if the user says --end=2008, we end on
|
||||||
|
// 2008/01/01 instead of 2009/01/01 (which is what end() would return).
|
||||||
|
optional<date_t> end = interval.begin(parent->session.current_year);
|
||||||
|
if (! end)
|
||||||
throw_(std::invalid_argument,
|
throw_(std::invalid_argument,
|
||||||
_("Could not determine end of period '%1'")
|
_("Could not determine end of period '%1'")
|
||||||
<< args[1].to_string());
|
<< args[1].to_string());
|
||||||
|
|
||||||
string predicate =
|
string predicate = "date<[" + to_iso_extended_string(*end) + "]";
|
||||||
"date<[" + to_iso_extended_string(*interval.start) + "]";
|
|
||||||
parent->HANDLER(limit_).on(string("--end"), predicate);
|
parent->HANDLER(limit_).on(string("--end"), predicate);
|
||||||
|
|
||||||
parent->terminus = datetime_t(*interval.start);
|
parent->terminus = datetime_t(*end);
|
||||||
});
|
});
|
||||||
|
|
||||||
OPTION(report_t, equity);
|
OPTION(report_t, equity);
|
||||||
|
|
@ -622,11 +625,13 @@ public:
|
||||||
|
|
||||||
OPTION_(report_t, now_, DO_(args) {
|
OPTION_(report_t, now_, DO_(args) {
|
||||||
date_interval_t interval(args[1].to_string());
|
date_interval_t interval(args[1].to_string());
|
||||||
if (! interval.start)
|
optional<date_t> begin = interval.begin(parent->session.current_year);
|
||||||
|
if (! begin)
|
||||||
throw_(std::invalid_argument,
|
throw_(std::invalid_argument,
|
||||||
_("Could not determine beginning of period '%1'")
|
_("Could not determine beginning of period '%1'")
|
||||||
<< args[1].to_string());
|
<< args[1].to_string());
|
||||||
ledger::epoch = datetime_t(*interval.start);
|
ledger::epoch = parent->terminus = datetime_t(*begin);
|
||||||
|
parent->session.current_year = ledger::epoch->date().year();
|
||||||
});
|
});
|
||||||
|
|
||||||
OPTION__
|
OPTION__
|
||||||
|
|
@ -844,7 +849,7 @@ public:
|
||||||
set_expr(args[0].to_string(), args[1].to_string());
|
set_expr(args[0].to_string(), args[1].to_string());
|
||||||
});
|
});
|
||||||
|
|
||||||
OPTION(report_t, total_data);
|
OPTION(report_t, total_data); // -J
|
||||||
|
|
||||||
OPTION_(report_t, truncate_, DO_(args) {
|
OPTION_(report_t, truncate_, DO_(args) {
|
||||||
string style(args[1].to_string());
|
string style(args[1].to_string());
|
||||||
|
|
|
||||||
265
src/times.cc
265
src/times.cc
|
|
@ -434,6 +434,7 @@ class date_parser_t
|
||||||
} kind;
|
} kind;
|
||||||
|
|
||||||
typedef variant<unsigned short,
|
typedef variant<unsigned short,
|
||||||
|
string,
|
||||||
date_specifier_t::year_type,
|
date_specifier_t::year_type,
|
||||||
date_time::months_of_year,
|
date_time::months_of_year,
|
||||||
date_time::weekdays,
|
date_time::weekdays,
|
||||||
|
|
@ -442,7 +443,8 @@ class date_parser_t
|
||||||
optional<content_t> value;
|
optional<content_t> value;
|
||||||
|
|
||||||
explicit token_t(kind_t _kind = UNKNOWN,
|
explicit token_t(kind_t _kind = UNKNOWN,
|
||||||
const optional<content_t>& _value = none)
|
const optional<content_t>& _value =
|
||||||
|
content_t(empty_string))
|
||||||
: kind(_kind), value(_value) {
|
: kind(_kind), value(_value) {
|
||||||
TRACE_CTOR(date_parser_t::lexer_t::token_t, "");
|
TRACE_CTOR(date_parser_t::lexer_t::token_t, "");
|
||||||
}
|
}
|
||||||
|
|
@ -467,49 +469,112 @@ class date_parser_t
|
||||||
}
|
}
|
||||||
|
|
||||||
string to_string() const {
|
string to_string() const {
|
||||||
|
std::ostringstream out;
|
||||||
|
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case UNKNOWN: return "UNKNOWN";
|
case UNKNOWN:
|
||||||
case TOK_DATE: return "TOK_DATE";
|
out << boost::get<string>(*value);
|
||||||
case TOK_INT: return "TOK_INT";
|
break;
|
||||||
case TOK_SLASH: return "TOK_SLASH";
|
case TOK_DATE:
|
||||||
case TOK_DASH: return "TOK_DASH";
|
return boost::get<date_specifier_t>(*value).to_string();
|
||||||
case TOK_DOT: return "TOK_DOT";
|
case TOK_INT:
|
||||||
case TOK_A_YEAR: return "TOK_A_YEAR";
|
out << boost::get<unsigned short>(*value);
|
||||||
case TOK_A_MONTH: return "TOK_A_MONTH";
|
break;
|
||||||
case TOK_A_WDAY: return "TOK_A_WDAY";
|
case TOK_SLASH: return "/";
|
||||||
case TOK_SINCE: return "TOK_SINCE";
|
case TOK_DASH: return "-";
|
||||||
case TOK_UNTIL: return "TOK_UNTIL";
|
case TOK_DOT: return ".";
|
||||||
case TOK_IN: return "TOK_IN";
|
case TOK_A_YEAR:
|
||||||
case TOK_THIS: return "TOK_THIS";
|
out << boost::get<date_specifier_t::year_type>(*value);
|
||||||
case TOK_NEXT: return "TOK_NEXT";
|
break;
|
||||||
case TOK_LAST: return "TOK_LAST";
|
case TOK_A_MONTH:
|
||||||
case TOK_EVERY: return "TOK_EVERY";
|
out << date_specifier_t::month_type
|
||||||
case TOK_TODAY: return "TOK_EVERY";
|
(boost::get<date_time::months_of_year>(*value));
|
||||||
case TOK_TOMORROW: return "TOK_TOMORROW";
|
break;
|
||||||
case TOK_YESTERDAY: return "TOK_YESTERDAY";
|
case TOK_A_WDAY:
|
||||||
case TOK_YEAR: return "TOK_YEAR";
|
out << date_specifier_t::day_of_week_type
|
||||||
case TOK_QUARTER: return "TOK_QUARTER";
|
(boost::get<date_time::weekdays>(*value));
|
||||||
case TOK_MONTH: return "TOK_MONTH";
|
break;
|
||||||
case TOK_WEEK: return "TOK_WEEK";
|
case TOK_SINCE: return "since";
|
||||||
case TOK_DAY: return "TOK_DAY";
|
case TOK_UNTIL: return "until";
|
||||||
case TOK_YEARLY: return "TOK_YEARLY";
|
case TOK_IN: return "in";
|
||||||
case TOK_QUARTERLY: return "TOK_QUARTERLY";
|
case TOK_THIS: return "this";
|
||||||
case TOK_BIMONTHLY: return "TOK_BIMONTHLY";
|
case TOK_NEXT: return "next";
|
||||||
case TOK_MONTHLY: return "TOK_MONTHLY";
|
case TOK_LAST: return "last";
|
||||||
case TOK_BIWEEKLY: return "TOK_BIWEEKLY";
|
case TOK_EVERY: return "every";
|
||||||
case TOK_WEEKLY: return "TOK_WEEKLY";
|
case TOK_TODAY: return "today";
|
||||||
case TOK_DAILY: return "TOK_DAILY";
|
case TOK_TOMORROW: return "tomorrow";
|
||||||
case TOK_YEARS: return "TOK_YEARS";
|
case TOK_YESTERDAY: return "yesterday";
|
||||||
case TOK_QUARTERS: return "TOK_QUARTERS";
|
case TOK_YEAR: return "year";
|
||||||
case TOK_MONTHS: return "TOK_MONTHS";
|
case TOK_QUARTER: return "quarter";
|
||||||
case TOK_WEEKS: return "TOK_WEEKS";
|
case TOK_MONTH: return "month";
|
||||||
case TOK_DAYS: return "TOK_DAYS";
|
case TOK_WEEK: return "week";
|
||||||
case END_REACHED: return "END_REACHED";
|
case TOK_DAY: return "day";
|
||||||
}
|
case TOK_YEARLY: return "yearly";
|
||||||
|
case TOK_QUARTERLY: return "quarterly";
|
||||||
|
case TOK_BIMONTHLY: return "bimonthly";
|
||||||
|
case TOK_MONTHLY: return "monthly";
|
||||||
|
case TOK_BIWEEKLY: return "biweekly";
|
||||||
|
case TOK_WEEKLY: return "weekly";
|
||||||
|
case TOK_DAILY: return "daily";
|
||||||
|
case TOK_YEARS: return "years";
|
||||||
|
case TOK_QUARTERS: return "quarters";
|
||||||
|
case TOK_MONTHS: return "months";
|
||||||
|
case TOK_WEEKS: return "weeks";
|
||||||
|
case TOK_DAYS: return "days";
|
||||||
|
case END_REACHED: return "<EOF>";
|
||||||
|
default:
|
||||||
assert(false);
|
assert(false);
|
||||||
return empty_string;
|
return empty_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return out.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump(std::ostream& out) const {
|
||||||
|
switch (kind) {
|
||||||
|
case UNKNOWN: out << "UNKNOWN"; break;
|
||||||
|
case TOK_DATE: out << "TOK_DATE"; break;
|
||||||
|
case TOK_INT: out << "TOK_INT"; break;
|
||||||
|
case TOK_SLASH: out << "TOK_SLASH"; break;
|
||||||
|
case TOK_DASH: out << "TOK_DASH"; break;
|
||||||
|
case TOK_DOT: out << "TOK_DOT"; break;
|
||||||
|
case TOK_A_YEAR: out << "TOK_A_YEAR"; break;
|
||||||
|
case TOK_A_MONTH: out << "TOK_A_MONTH"; break;
|
||||||
|
case TOK_A_WDAY: out << "TOK_A_WDAY"; break;
|
||||||
|
case TOK_SINCE: out << "TOK_SINCE"; break;
|
||||||
|
case TOK_UNTIL: out << "TOK_UNTIL"; break;
|
||||||
|
case TOK_IN: out << "TOK_IN"; break;
|
||||||
|
case TOK_THIS: out << "TOK_THIS"; break;
|
||||||
|
case TOK_NEXT: out << "TOK_NEXT"; break;
|
||||||
|
case TOK_LAST: out << "TOK_LAST"; break;
|
||||||
|
case TOK_EVERY: out << "TOK_EVERY"; break;
|
||||||
|
case TOK_TODAY: out << "TOK_TODAY"; break;
|
||||||
|
case TOK_TOMORROW: out << "TOK_TOMORROW"; break;
|
||||||
|
case TOK_YESTERDAY: out << "TOK_YESTERDAY"; break;
|
||||||
|
case TOK_YEAR: out << "TOK_YEAR"; break;
|
||||||
|
case TOK_QUARTER: out << "TOK_QUARTER"; break;
|
||||||
|
case TOK_MONTH: out << "TOK_MONTH"; break;
|
||||||
|
case TOK_WEEK: out << "TOK_WEEK"; break;
|
||||||
|
case TOK_DAY: out << "TOK_DAY"; break;
|
||||||
|
case TOK_YEARLY: out << "TOK_YEARLY"; break;
|
||||||
|
case TOK_QUARTERLY: out << "TOK_QUARTERLY"; break;
|
||||||
|
case TOK_BIMONTHLY: out << "TOK_BIMONTHLY"; break;
|
||||||
|
case TOK_MONTHLY: out << "TOK_MONTHLY"; break;
|
||||||
|
case TOK_BIWEEKLY: out << "TOK_BIWEEKLY"; break;
|
||||||
|
case TOK_WEEKLY: out << "TOK_WEEKLY"; break;
|
||||||
|
case TOK_DAILY: out << "TOK_DAILY"; break;
|
||||||
|
case TOK_YEARS: out << "TOK_YEARS"; break;
|
||||||
|
case TOK_QUARTERS: out << "TOK_QUARTERS"; break;
|
||||||
|
case TOK_MONTHS: out << "TOK_MONTHS"; break;
|
||||||
|
case TOK_WEEKS: out << "TOK_WEEKS"; break;
|
||||||
|
case TOK_DAYS: out << "TOK_DAYS"; break;
|
||||||
|
case END_REACHED: out << "END_REACHED"; break;
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void unexpected();
|
void unexpected();
|
||||||
static void expected(char wanted, char c = '\0');
|
static void expected(char wanted, char c = '\0');
|
||||||
};
|
};
|
||||||
|
|
@ -753,7 +818,9 @@ date_interval_t date_parser_t::parse()
|
||||||
inclusion_specifier =
|
inclusion_specifier =
|
||||||
date_specifier_t(static_cast<date_specifier_t::year_type>(temp.year()),
|
date_specifier_t(static_cast<date_specifier_t::year_type>(temp.year()),
|
||||||
temp.month());
|
temp.month());
|
||||||
|
#if 0
|
||||||
period.duration = date_duration_t(date_duration_t::QUARTERS, 1);
|
period.duration = date_duration_t(date_duration_t::QUARTERS, 1);
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -771,7 +838,9 @@ date_interval_t date_parser_t::parse()
|
||||||
date_duration_t::find_nearest(today, date_duration_t::WEEKS);
|
date_duration_t::find_nearest(today, date_duration_t::WEEKS);
|
||||||
temp += gregorian::days(7 * adjust);
|
temp += gregorian::days(7 * adjust);
|
||||||
inclusion_specifier = date_specifier_t(today);
|
inclusion_specifier = date_specifier_t(today);
|
||||||
|
#if 0
|
||||||
period.duration = date_duration_t(date_duration_t::WEEKS, 1);
|
period.duration = date_duration_t(date_duration_t::WEEKS, 1);
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -806,19 +875,19 @@ date_interval_t date_parser_t::parse()
|
||||||
tok = lexer.next_token();
|
tok = lexer.next_token();
|
||||||
switch (tok.kind) {
|
switch (tok.kind) {
|
||||||
case lexer_t::token_t::TOK_YEARS:
|
case lexer_t::token_t::TOK_YEARS:
|
||||||
period.skip_duration = date_duration_t(date_duration_t::YEARS, quantity);
|
period.duration = date_duration_t(date_duration_t::YEARS, quantity);
|
||||||
break;
|
break;
|
||||||
case lexer_t::token_t::TOK_QUARTERS:
|
case lexer_t::token_t::TOK_QUARTERS:
|
||||||
period.skip_duration = date_duration_t(date_duration_t::QUARTERS, quantity);
|
period.duration = date_duration_t(date_duration_t::QUARTERS, quantity);
|
||||||
break;
|
break;
|
||||||
case lexer_t::token_t::TOK_MONTHS:
|
case lexer_t::token_t::TOK_MONTHS:
|
||||||
period.skip_duration = date_duration_t(date_duration_t::MONTHS, quantity);
|
period.duration = date_duration_t(date_duration_t::MONTHS, quantity);
|
||||||
break;
|
break;
|
||||||
case lexer_t::token_t::TOK_WEEKS:
|
case lexer_t::token_t::TOK_WEEKS:
|
||||||
period.skip_duration = date_duration_t(date_duration_t::WEEKS, quantity);
|
period.duration = date_duration_t(date_duration_t::WEEKS, quantity);
|
||||||
break;
|
break;
|
||||||
case lexer_t::token_t::TOK_DAYS:
|
case lexer_t::token_t::TOK_DAYS:
|
||||||
period.skip_duration = date_duration_t(date_duration_t::DAYS, quantity);
|
period.duration = date_duration_t(date_duration_t::DAYS, quantity);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
tok.unexpected();
|
tok.unexpected();
|
||||||
|
|
@ -827,19 +896,19 @@ date_interval_t date_parser_t::parse()
|
||||||
} else {
|
} else {
|
||||||
switch (tok.kind) {
|
switch (tok.kind) {
|
||||||
case lexer_t::token_t::TOK_YEAR:
|
case lexer_t::token_t::TOK_YEAR:
|
||||||
period.skip_duration = date_duration_t(date_duration_t::YEARS, 1);
|
period.duration = date_duration_t(date_duration_t::YEARS, 1);
|
||||||
break;
|
break;
|
||||||
case lexer_t::token_t::TOK_QUARTER:
|
case lexer_t::token_t::TOK_QUARTER:
|
||||||
period.skip_duration = date_duration_t(date_duration_t::QUARTERS, 1);
|
period.duration = date_duration_t(date_duration_t::QUARTERS, 1);
|
||||||
break;
|
break;
|
||||||
case lexer_t::token_t::TOK_MONTH:
|
case lexer_t::token_t::TOK_MONTH:
|
||||||
period.skip_duration = date_duration_t(date_duration_t::MONTHS, 1);
|
period.duration = date_duration_t(date_duration_t::MONTHS, 1);
|
||||||
break;
|
break;
|
||||||
case lexer_t::token_t::TOK_WEEK:
|
case lexer_t::token_t::TOK_WEEK:
|
||||||
period.skip_duration = date_duration_t(date_duration_t::WEEKS, 1);
|
period.duration = date_duration_t(date_duration_t::WEEKS, 1);
|
||||||
break;
|
break;
|
||||||
case lexer_t::token_t::TOK_DAY:
|
case lexer_t::token_t::TOK_DAY:
|
||||||
period.skip_duration = date_duration_t(date_duration_t::DAYS, 1);
|
period.duration = date_duration_t(date_duration_t::DAYS, 1);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
tok.unexpected();
|
tok.unexpected();
|
||||||
|
|
@ -849,25 +918,25 @@ date_interval_t date_parser_t::parse()
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case lexer_t::token_t::TOK_YEARLY:
|
case lexer_t::token_t::TOK_YEARLY:
|
||||||
period.skip_duration = date_duration_t(date_duration_t::YEARS, 1);
|
period.duration = date_duration_t(date_duration_t::YEARS, 1);
|
||||||
break;
|
break;
|
||||||
case lexer_t::token_t::TOK_QUARTERLY:
|
case lexer_t::token_t::TOK_QUARTERLY:
|
||||||
period.skip_duration = date_duration_t(date_duration_t::QUARTERS, 1);
|
period.duration = date_duration_t(date_duration_t::QUARTERS, 1);
|
||||||
break;
|
break;
|
||||||
case lexer_t::token_t::TOK_BIMONTHLY:
|
case lexer_t::token_t::TOK_BIMONTHLY:
|
||||||
period.skip_duration = date_duration_t(date_duration_t::MONTHS, 2);
|
period.duration = date_duration_t(date_duration_t::MONTHS, 2);
|
||||||
break;
|
break;
|
||||||
case lexer_t::token_t::TOK_MONTHLY:
|
case lexer_t::token_t::TOK_MONTHLY:
|
||||||
period.skip_duration = date_duration_t(date_duration_t::MONTHS, 1);
|
period.duration = date_duration_t(date_duration_t::MONTHS, 1);
|
||||||
break;
|
break;
|
||||||
case lexer_t::token_t::TOK_BIWEEKLY:
|
case lexer_t::token_t::TOK_BIWEEKLY:
|
||||||
period.skip_duration = date_duration_t(date_duration_t::WEEKS, 2);
|
period.duration = date_duration_t(date_duration_t::WEEKS, 2);
|
||||||
break;
|
break;
|
||||||
case lexer_t::token_t::TOK_WEEKLY:
|
case lexer_t::token_t::TOK_WEEKLY:
|
||||||
period.skip_duration = date_duration_t(date_duration_t::WEEKS, 1);
|
period.duration = date_duration_t(date_duration_t::WEEKS, 1);
|
||||||
break;
|
break;
|
||||||
case lexer_t::token_t::TOK_DAILY:
|
case lexer_t::token_t::TOK_DAILY:
|
||||||
period.skip_duration = date_duration_t(date_duration_t::DAYS, 1);
|
period.duration = date_duration_t(date_duration_t::DAYS, 1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
@ -876,8 +945,10 @@ date_interval_t date_parser_t::parse()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
if (! period.duration && inclusion_specifier)
|
if (! period.duration && inclusion_specifier)
|
||||||
period.duration = inclusion_specifier->implied_duration();
|
period.duration = inclusion_specifier->implied_duration();
|
||||||
|
#endif
|
||||||
|
|
||||||
if (since_specifier || until_specifier) {
|
if (since_specifier || until_specifier) {
|
||||||
date_range_t range(since_specifier, until_specifier);
|
date_range_t range(since_specifier, until_specifier);
|
||||||
|
|
@ -915,16 +986,9 @@ void date_interval_t::resolve_end()
|
||||||
"stabilize: end_of_duration reset to end: " << *end_of_duration);
|
"stabilize: end_of_duration reset to end: " << *end_of_duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! skip_duration) {
|
|
||||||
skip_duration = duration;
|
|
||||||
DEBUG("times.interval",
|
|
||||||
"stabilize: skip_duration set to: " << *skip_duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start && ! next) {
|
if (start && ! next) {
|
||||||
next = skip_duration->add(*start);
|
next = end_of_duration;
|
||||||
DEBUG("times.interval",
|
DEBUG("times.interval", "stabilize: next set to: " << *next);
|
||||||
"stabilize: next set to: " << *next);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1024,9 +1088,10 @@ void date_interval_t::stabilize(const optional<date_t>& date)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG("times.interval", "stabilize: final start date = " << *start);
|
DEBUG("times.interval", "stabilize: proposed start date = " << *start);
|
||||||
|
|
||||||
if (initial_start && (! start || *start < *initial_start)) {
|
if (initial_start && (! start || *start < *initial_start)) {
|
||||||
|
// Using the discovered start, find the end of the period
|
||||||
resolve_end();
|
resolve_end();
|
||||||
|
|
||||||
start = initial_start;
|
start = initial_start;
|
||||||
|
|
@ -1036,14 +1101,20 @@ void date_interval_t::stabilize(const optional<date_t>& date)
|
||||||
finish = initial_finish;
|
finish = initial_finish;
|
||||||
DEBUG("times.interval", "stabilize: finish reset to initial finish");
|
DEBUG("times.interval", "stabilize: finish reset to initial finish");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (start)
|
||||||
|
DEBUG("times.interval", "stabilize: final start = " << *start);
|
||||||
|
if (finish)
|
||||||
|
DEBUG("times.interval", "stabilize: final finish = " << *finish);
|
||||||
}
|
}
|
||||||
else if (range) {
|
else if (range) {
|
||||||
|
if (date) {
|
||||||
|
start = range->begin(date->year());
|
||||||
|
finish = range->end(date->year());
|
||||||
|
} else {
|
||||||
start = range->begin();
|
start = range->begin();
|
||||||
finish = range->end();
|
finish = range->end();
|
||||||
|
}
|
||||||
if (start && finish)
|
|
||||||
duration = date_duration_t(date_duration_t::DAYS,
|
|
||||||
static_cast<int>((*finish - *start).days()));
|
|
||||||
}
|
}
|
||||||
aligned = true;
|
aligned = true;
|
||||||
}
|
}
|
||||||
|
|
@ -1116,7 +1187,7 @@ bool date_interval_t::find_period(const date_t& date)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
scan = skip_duration->add(scan);
|
scan = duration->add(scan);
|
||||||
end_of_scan = duration->add(scan);
|
end_of_scan = duration->add(scan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1130,7 +1201,7 @@ date_interval_t& date_interval_t::operator++()
|
||||||
|
|
||||||
stabilize();
|
stabilize();
|
||||||
|
|
||||||
if (! skip_duration || ! duration)
|
if (! duration)
|
||||||
throw_(date_error,
|
throw_(date_error,
|
||||||
_("Cannot increment a date interval without a duration"));
|
_("Cannot increment a date interval without a duration"));
|
||||||
|
|
||||||
|
|
@ -1140,10 +1211,8 @@ date_interval_t& date_interval_t::operator++()
|
||||||
start = none;
|
start = none;
|
||||||
} else {
|
} else {
|
||||||
start = *next;
|
start = *next;
|
||||||
|
|
||||||
end_of_duration = duration->add(*start);
|
end_of_duration = duration->add(*start);
|
||||||
}
|
}
|
||||||
|
|
||||||
next = none;
|
next = none;
|
||||||
|
|
||||||
resolve_end();
|
resolve_end();
|
||||||
|
|
@ -1151,7 +1220,7 @@ date_interval_t& date_interval_t::operator++()
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void date_interval_t::dump(std::ostream& out)
|
void date_interval_t::dump(std::ostream& out, optional_year current_year)
|
||||||
{
|
{
|
||||||
out << _("--- Before stabilization ---") << std::endl;
|
out << _("--- Before stabilization ---") << std::endl;
|
||||||
|
|
||||||
|
|
@ -1162,14 +1231,10 @@ void date_interval_t::dump(std::ostream& out)
|
||||||
if (finish)
|
if (finish)
|
||||||
out << _(" finish: ") << format_date(*finish) << std::endl;
|
out << _(" finish: ") << format_date(*finish) << std::endl;
|
||||||
|
|
||||||
if (skip_duration)
|
|
||||||
out << _(" skip: ") << skip_duration->to_string() << std::endl;
|
|
||||||
if (factor)
|
|
||||||
out << _(" factor: ") << factor << std::endl;
|
|
||||||
if (duration)
|
if (duration)
|
||||||
out << _("duration: ") << duration->to_string() << std::endl;
|
out << _("duration: ") << duration->to_string() << std::endl;
|
||||||
|
|
||||||
stabilize(begin());
|
stabilize(begin(current_year));
|
||||||
|
|
||||||
out << std::endl
|
out << std::endl
|
||||||
<< _("--- After stabilization ---") << std::endl;
|
<< _("--- After stabilization ---") << std::endl;
|
||||||
|
|
@ -1181,27 +1246,30 @@ void date_interval_t::dump(std::ostream& out)
|
||||||
if (finish)
|
if (finish)
|
||||||
out << _(" finish: ") << format_date(*finish) << std::endl;
|
out << _(" finish: ") << format_date(*finish) << std::endl;
|
||||||
|
|
||||||
if (skip_duration)
|
|
||||||
out << _(" skip: ") << skip_duration->to_string() << std::endl;
|
|
||||||
if (factor)
|
|
||||||
out << _(" factor: ") << factor << std::endl;
|
|
||||||
if (duration)
|
if (duration)
|
||||||
out << _("duration: ") << duration->to_string() << std::endl;
|
out << _("duration: ") << duration->to_string() << std::endl;
|
||||||
|
|
||||||
out << std::endl
|
out << std::endl
|
||||||
<< _("--- Sample dates in range (max. 20) ---") << std::endl;
|
<< _("--- Sample dates in range (max. 20) ---") << std::endl;
|
||||||
|
|
||||||
for (int i = 0; i < 20 && *this; i++, ++*this) {
|
date_t last_date;
|
||||||
|
|
||||||
|
for (int i = 0; i < 20 && *this; ++i, ++*this) {
|
||||||
out << std::right;
|
out << std::right;
|
||||||
out.width(2);
|
out.width(2);
|
||||||
|
|
||||||
|
if (! last_date.is_not_a_date() && last_date == *start)
|
||||||
|
break;
|
||||||
|
|
||||||
out << (i + 1) << ": " << format_date(*start);
|
out << (i + 1) << ": " << format_date(*start);
|
||||||
if (skip_duration)
|
if (duration)
|
||||||
out << " -- " << format_date(*inclusive_skip_end());
|
out << " -- " << format_date(*inclusive_end());
|
||||||
out << std::endl;
|
out << std::endl;
|
||||||
|
|
||||||
if (! skip_duration)
|
if (! duration)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
last_date = *start;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1268,6 +1336,8 @@ date_parser_t::lexer_t::token_t date_parser_t::lexer_t::next_token()
|
||||||
token_t::content_t(lexical_cast<unsigned short>(term)));
|
token_t::content_t(lexical_cast<unsigned short>(term)));
|
||||||
}
|
}
|
||||||
else if (std::isalpha(term[0])) {
|
else if (std::isalpha(term[0])) {
|
||||||
|
to_lower(term);
|
||||||
|
|
||||||
if (optional<date_time::months_of_year> month =
|
if (optional<date_time::months_of_year> month =
|
||||||
string_to_month_of_year(term)) {
|
string_to_month_of_year(term)) {
|
||||||
return token_t(token_t::TOK_A_MONTH, token_t::content_t(*month));
|
return token_t(token_t::TOK_A_MONTH, token_t::content_t(*month));
|
||||||
|
|
@ -1339,20 +1409,20 @@ date_parser_t::lexer_t::token_t date_parser_t::lexer_t::next_token()
|
||||||
token_t::expected('\0', *begin);
|
token_t::expected('\0', *begin);
|
||||||
}
|
}
|
||||||
|
|
||||||
return token_t(token_t::UNKNOWN);
|
return token_t(token_t::UNKNOWN, token_t::content_t(term));
|
||||||
}
|
}
|
||||||
|
|
||||||
void date_parser_t::lexer_t::token_t::unexpected()
|
void date_parser_t::lexer_t::token_t::unexpected()
|
||||||
{
|
{
|
||||||
kind_t prev_kind = kind;
|
switch (kind) {
|
||||||
|
|
||||||
kind = UNKNOWN;
|
|
||||||
|
|
||||||
switch (prev_kind) {
|
|
||||||
case END_REACHED:
|
case END_REACHED:
|
||||||
|
kind = UNKNOWN;
|
||||||
throw_(date_error, _("Unexpected end of expression"));
|
throw_(date_error, _("Unexpected end of expression"));
|
||||||
default:
|
default: {
|
||||||
throw_(date_error, _("Unexpected token '%1'") << to_string());
|
string desc = to_string();
|
||||||
|
kind = UNKNOWN;
|
||||||
|
throw_(date_error, _("Unexpected date period token '%1'") << desc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1503,7 +1573,8 @@ void show_period_tokens(std::ostream& out, const string& arg)
|
||||||
date_parser_t::lexer_t::token_t token;
|
date_parser_t::lexer_t::token_t token;
|
||||||
do {
|
do {
|
||||||
token = lexer.next_token();
|
token = lexer.next_token();
|
||||||
out << token.to_string() << std::endl;
|
token.dump(out);
|
||||||
|
out << ": " << token.to_string() << std::endl;
|
||||||
}
|
}
|
||||||
while (token.kind != date_parser_t::lexer_t::token_t::END_REACHED);
|
while (token.kind != date_parser_t::lexer_t::token_t::END_REACHED);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
18
src/times.h
18
src/times.h
|
|
@ -534,16 +534,14 @@ public:
|
||||||
optional<date_t> start; // the real start, after adjustment
|
optional<date_t> start; // the real start, after adjustment
|
||||||
optional<date_t> finish; // the real end, likewise
|
optional<date_t> finish; // the real end, likewise
|
||||||
bool aligned;
|
bool aligned;
|
||||||
optional<date_duration_t> skip_duration;
|
|
||||||
std::size_t factor;
|
|
||||||
optional<date_t> next;
|
optional<date_t> next;
|
||||||
optional<date_duration_t> duration;
|
optional<date_duration_t> duration;
|
||||||
optional<date_t> end_of_duration;
|
optional<date_t> end_of_duration;
|
||||||
|
|
||||||
explicit date_interval_t() : aligned(false), factor(1) {
|
explicit date_interval_t() : aligned(false) {
|
||||||
TRACE_CTOR(date_interval_t, "");
|
TRACE_CTOR(date_interval_t, "");
|
||||||
}
|
}
|
||||||
date_interval_t(const string& str) : aligned(false), factor(1) {
|
date_interval_t(const string& str) : aligned(false) {
|
||||||
TRACE_CTOR(date_interval_t, "const string&");
|
TRACE_CTOR(date_interval_t, "const string&");
|
||||||
parse(str);
|
parse(str);
|
||||||
}
|
}
|
||||||
|
|
@ -552,8 +550,6 @@ public:
|
||||||
start(other.start),
|
start(other.start),
|
||||||
finish(other.finish),
|
finish(other.finish),
|
||||||
aligned(other.aligned),
|
aligned(other.aligned),
|
||||||
skip_duration(other.skip_duration),
|
|
||||||
factor(other.factor),
|
|
||||||
next(other.next),
|
next(other.next),
|
||||||
duration(other.duration),
|
duration(other.duration),
|
||||||
end_of_duration(other.end_of_duration) {
|
end_of_duration(other.end_of_duration) {
|
||||||
|
|
@ -593,12 +589,6 @@ public:
|
||||||
containing date, or false if no such period can be found. */
|
containing date, or false if no such period can be found. */
|
||||||
bool find_period(const date_t& date);
|
bool find_period(const date_t& date);
|
||||||
|
|
||||||
optional<date_t> inclusive_skip_end() const {
|
|
||||||
if (skip_duration)
|
|
||||||
return skip_duration->add(*start) - gregorian::days(1);
|
|
||||||
else
|
|
||||||
return none;
|
|
||||||
}
|
|
||||||
optional<date_t> inclusive_end() const {
|
optional<date_t> inclusive_end() const {
|
||||||
if (end_of_duration)
|
if (end_of_duration)
|
||||||
return *end_of_duration - gregorian::days(1);
|
return *end_of_duration - gregorian::days(1);
|
||||||
|
|
@ -608,7 +598,7 @@ public:
|
||||||
|
|
||||||
date_interval_t& operator++();
|
date_interval_t& operator++();
|
||||||
|
|
||||||
void dump(std::ostream& out);
|
void dump(std::ostream& out, optional_year current_year = none);
|
||||||
|
|
||||||
#if defined(HAVE_BOOST_SERIALIZATION)
|
#if defined(HAVE_BOOST_SERIALIZATION)
|
||||||
private:
|
private:
|
||||||
|
|
@ -622,8 +612,6 @@ private:
|
||||||
ar & start;
|
ar & start;
|
||||||
ar & finish;
|
ar & finish;
|
||||||
ar & aligned;
|
ar & aligned;
|
||||||
ar & skip_duration;
|
|
||||||
ar & factor;
|
|
||||||
ar & next;
|
ar & next;
|
||||||
ar & duration;
|
ar & duration;
|
||||||
ar & end_of_duration;
|
ar & end_of_duration;
|
||||||
|
|
|
||||||
|
|
@ -206,11 +206,12 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags)
|
||||||
length++;
|
length++;
|
||||||
|
|
||||||
date_interval_t timespan(buf);
|
date_interval_t timespan(buf);
|
||||||
if (! timespan)
|
optional<date_t> begin = timespan.begin();
|
||||||
|
if (! begin)
|
||||||
throw_(parse_error,
|
throw_(parse_error,
|
||||||
_("Date specifier does not refer to a starting date"));
|
_("Date specifier does not refer to a starting date"));
|
||||||
kind = VALUE;
|
kind = VALUE;
|
||||||
value = *timespan.start;
|
value = *begin;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,50 +1,95 @@
|
||||||
period --now=2010/11/01 12/01
|
period --now=2010/11/01 12/01
|
||||||
<<<
|
<<<
|
||||||
>>>1
|
>>>1
|
||||||
global details =>
|
--- Period expression tokens ---
|
||||||
|
TOK_DATE: month Dec day 1
|
||||||
|
END_REACHED: <EOF>
|
||||||
|
|
||||||
start: 09-Dec-01
|
--- Before stabilization ---
|
||||||
finish: 09-Dec-02
|
range: in month Dec day 1
|
||||||
factor: 1
|
|
||||||
|
--- After stabilization ---
|
||||||
|
range: in month Dec day 1
|
||||||
|
start: 10-Dec-01
|
||||||
|
finish: 10-Dec-02
|
||||||
|
|
||||||
|
--- Sample dates in range (max. 20) ---
|
||||||
|
1: 10-Dec-01
|
||||||
>>>2
|
>>>2
|
||||||
=== 0
|
=== 0
|
||||||
period --now=2010/11/01 10/01
|
period --now=2010/11/01 10/01
|
||||||
<<<
|
<<<
|
||||||
>>>1
|
>>>1
|
||||||
global details =>
|
--- Period expression tokens ---
|
||||||
|
TOK_DATE: month Oct day 1
|
||||||
|
END_REACHED: <EOF>
|
||||||
|
|
||||||
|
--- Before stabilization ---
|
||||||
|
range: in month Oct day 1
|
||||||
|
|
||||||
|
--- After stabilization ---
|
||||||
|
range: in month Oct day 1
|
||||||
start: 10-Oct-01
|
start: 10-Oct-01
|
||||||
finish: 10-Oct-02
|
finish: 10-Oct-02
|
||||||
factor: 1
|
|
||||||
|
--- Sample dates in range (max. 20) ---
|
||||||
|
1: 10-Oct-01
|
||||||
>>>2
|
>>>2
|
||||||
=== 0
|
=== 0
|
||||||
period --now=2010/11/01 2009/10
|
period --now=2010/11/01 2009/10
|
||||||
<<<
|
<<<
|
||||||
>>>1
|
>>>1
|
||||||
global details =>
|
--- Period expression tokens ---
|
||||||
|
TOK_DATE: year 2009 month Oct
|
||||||
|
END_REACHED: <EOF>
|
||||||
|
|
||||||
|
--- Before stabilization ---
|
||||||
|
range: in year 2009 month Oct
|
||||||
|
|
||||||
|
--- After stabilization ---
|
||||||
|
range: in year 2009 month Oct
|
||||||
start: 09-Oct-01
|
start: 09-Oct-01
|
||||||
finish: 09-Nov-01
|
finish: 09-Nov-01
|
||||||
factor: 1
|
|
||||||
|
--- Sample dates in range (max. 20) ---
|
||||||
|
1: 09-Oct-01
|
||||||
>>>2
|
>>>2
|
||||||
=== 0
|
=== 0
|
||||||
period --now=2010/11/01 2009/10/01
|
period --now=2010/11/01 2009/10/01
|
||||||
<<<
|
<<<
|
||||||
>>>1
|
>>>1
|
||||||
global details =>
|
--- Period expression tokens ---
|
||||||
|
TOK_DATE: year 2009 month Oct day 1
|
||||||
|
END_REACHED: <EOF>
|
||||||
|
|
||||||
|
--- Before stabilization ---
|
||||||
|
range: in year 2009 month Oct day 1
|
||||||
|
|
||||||
|
--- After stabilization ---
|
||||||
|
range: in year 2009 month Oct day 1
|
||||||
start: 09-Oct-01
|
start: 09-Oct-01
|
||||||
finish: 09-Oct-02
|
finish: 09-Oct-02
|
||||||
factor: 1
|
|
||||||
|
--- Sample dates in range (max. 20) ---
|
||||||
|
1: 09-Oct-01
|
||||||
>>>2
|
>>>2
|
||||||
=== 0
|
=== 0
|
||||||
period --now=2010/11/01 2009
|
period --now=2010/11/01 2009
|
||||||
<<<
|
<<<
|
||||||
>>>1
|
>>>1
|
||||||
global details =>
|
--- Period expression tokens ---
|
||||||
|
TOK_A_YEAR: 2009
|
||||||
|
END_REACHED: <EOF>
|
||||||
|
|
||||||
|
--- Before stabilization ---
|
||||||
|
range: in year 2009
|
||||||
|
|
||||||
|
--- After stabilization ---
|
||||||
|
range: in year 2009
|
||||||
start: 09-Jan-01
|
start: 09-Jan-01
|
||||||
finish: 10-Jan-01
|
finish: 10-Jan-01
|
||||||
factor: 1
|
|
||||||
|
--- Sample dates in range (max. 20) ---
|
||||||
|
1: 09-Jan-01
|
||||||
>>>2
|
>>>2
|
||||||
=== 0
|
=== 0
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,20 @@
|
||||||
period june 2008
|
period june 2008
|
||||||
<<<
|
<<<
|
||||||
>>>1
|
>>>1
|
||||||
global details =>
|
--- Period expression tokens ---
|
||||||
|
TOK_A_MONTH: Jun
|
||||||
|
TOK_A_YEAR: 2008
|
||||||
|
END_REACHED: <EOF>
|
||||||
|
|
||||||
|
--- Before stabilization ---
|
||||||
|
range: in year 2008 month Jun
|
||||||
|
|
||||||
|
--- After stabilization ---
|
||||||
|
range: in year 2008 month Jun
|
||||||
start: 08-Jun-01
|
start: 08-Jun-01
|
||||||
finish: 08-Jul-01
|
finish: 08-Jul-01
|
||||||
factor: 1
|
|
||||||
|
--- Sample dates in range (max. 20) ---
|
||||||
|
1: 08-Jun-01
|
||||||
>>>2
|
>>>2
|
||||||
=== 0
|
=== 0
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue