summaryrefslogtreecommitdiff
path: root/src/times.h
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2009-11-17 22:23:46 -0500
committerJohn Wiegley <johnw@newartisans.com>2009-11-17 22:23:46 -0500
commit3e91c3bf2c3662c40f0fe7c9cf197f6b0c725269 (patch)
tree15015ec3af9a8533592515f01fcb33f740146a4a /src/times.h
parentd6cb382b20a1c65fde043af0ef41f21570027b27 (diff)
downloadfork-ledger-3e91c3bf2c3662c40f0fe7c9cf197f6b0c725269.tar.gz
fork-ledger-3e91c3bf2c3662c40f0fe7c9cf197f6b0c725269.tar.bz2
fork-ledger-3e91c3bf2c3662c40f0fe7c9cf197f6b0c725269.zip
Added several new types for working with dates and ranges
date_specifier_t :: This is like a plain date_t, except it knows what wasn't specified. For example, if 2008/06 is parsed, it becomes date_specifier_t which knows that no day was given. If you ask for the begin() date of the specifier, it will be 2008/06/01; the end() date (which is exclusive) will be 2008/07/01. date_range_t :: A date range is a range of two specifiers, either of which (but not both) may be omitted. This makes it possible to represent expressions like "from june to july", where no day or year is given. The exact dates will be inferred by using the current year, and fixing the range from YEAR/06/01 to YEAR/07/01. That is, the range goes from the begin() of one date specifier to the begin() of the other. date_specifier_or_range_t :: A variadic type that can be either a date_specifier_t or a date_range_t. It's just a wrapper to represent the fact that ranges can be implicit via specifiers (such as, "in june"), or explicit via ranges ("since 2008").
Diffstat (limited to 'src/times.h')
-rw-r--r--src/times.h327
1 files changed, 327 insertions, 0 deletions
diff --git a/src/times.h b/src/times.h
index c50b0366..b13f48b1 100644
--- a/src/times.h
+++ b/src/times.h
@@ -181,6 +181,105 @@ private:
#endif // HAVE_BOOST_SERIALIZATION
};
+class date_specifier_t
+{
+ friend class date_parser_t;
+
+#if 0
+ typedef date_t::year_type year_type;
+#else
+ typedef unsigned short year_type;
+#endif
+ typedef date_t::month_type month_type;
+ typedef date_t::day_type day_type;
+ typedef date_t::day_of_week_type day_of_week_type;
+
+ optional<year_type> year;
+ optional<month_type> month;
+ optional<day_type> day;
+ optional<day_of_week_type> wday;
+
+public:
+ date_specifier_t() {}
+ date_specifier_t(const date_t& date, const date_traits_t& traits) {
+ if (traits.has_year)
+ year = date.year();
+ if (traits.has_month)
+ month = date.month();
+ if (traits.has_day)
+ day = date.day();
+ }
+
+ date_t begin(const optional<date_t::year_type>& current_year = none) const;
+ date_t end(const optional<date_t::year_type>& current_year = none) const;
+
+ bool is_within(const date_t& date,
+ const optional<date_t::year_type>& current_year = none) const {
+ return date >= begin(current_year) && date < end(current_year);
+ }
+
+#if defined(HAVE_BOOST_SERIALIZATION)
+private:
+ /** Serialization. */
+
+ friend class boost::serialization::access;
+
+ template<class Archive>
+ void serialize(Archive& ar, const unsigned int /* version */) {
+ ar & year;
+ ar & month;
+ ar & day;
+ ar & wday;
+ }
+#endif // HAVE_BOOST_SERIALIZATION
+};
+
+class date_range_t
+{
+ friend class date_parser_t;
+
+ optional<date_specifier_t> range_begin;
+ optional<date_specifier_t> range_end;
+
+public:
+ optional<date_t>
+ begin(const optional<date_t::year_type>& current_year = none) const {
+ if (range_begin)
+ return range_begin->begin(current_year);
+ else
+ return none;
+ }
+ optional<date_t>
+ end(const optional<date_t::year_type>& current_year = none) const {
+ if (range_end)
+ return range_end->end(current_year);
+ else
+ return none;
+ }
+
+ bool is_within(const date_t& date,
+ const optional<date_t::year_type>& current_year = none) const {
+ optional<date_t> b = begin(current_year);
+ optional<date_t> e = end(current_year);
+ bool after_begin = b ? date >= *b : true;
+ bool before_end = e ? date < *e : true;
+ return after_begin && before_end;
+ }
+
+#if defined(HAVE_BOOST_SERIALIZATION)
+private:
+ /** Serialization. */
+
+ friend class boost::serialization::access;
+
+ template<class Archive>
+ void serialize(Archive& ar, const unsigned int /* version */) {
+ ar & range_begin;
+ ar & range_end;
+ }
+#endif // HAVE_BOOST_SERIALIZATION
+};
+
struct date_duration_t
{
enum skip_quantum_t {
@@ -247,6 +346,45 @@ private:
#endif // HAVE_BOOST_SERIALIZATION
};
+class date_specifier_or_range_t
+{
+ typedef variant<int, date_specifier_t, date_range_t> value_type;
+
+ value_type specifier_or_range;
+
+public:
+ optional<date_t>
+ begin(const optional<date_t::year_type>& current_year = none) const {
+ if (specifier_or_range.type() == typeid(date_specifier_t))
+ return boost::get<date_specifier_t>(specifier_or_range).begin(current_year);
+ else if (specifier_or_range.type() == typeid(date_range_t))
+ return boost::get<date_range_t>(specifier_or_range).begin(current_year);
+ else
+ return none;
+ }
+ optional<date_t>
+ end(const optional<date_t::year_type>& current_year = none) const {
+ if (specifier_or_range.type() == typeid(date_specifier_t))
+ return boost::get<date_specifier_t>(specifier_or_range).end(current_year);
+ else if (specifier_or_range.type() == typeid(date_range_t))
+ return boost::get<date_range_t>(specifier_or_range).end(current_year);
+ else
+ return none;
+ }
+
+#if defined(HAVE_BOOST_SERIALIZATION)
+private:
+ /** Serialization. */
+
+ friend class boost::serialization::access;
+
+ template<class Archive>
+ void serialize(Archive& ar, const unsigned int /* version */) {
+ ar & specifier_or_range;
+ }
+#endif // HAVE_BOOST_SERIALIZATION
+};
+
class date_interval_t : public equality_comparable<date_interval_t>
{
public:
@@ -343,9 +481,198 @@ private:
#endif // HAVE_BOOST_SERIALIZATION
};
+class date_parser_t
+{
+ friend void show_period_tokens(std::ostream& out, const string& arg);
+
+ class lexer_t
+ {
+ friend class date_parser_t;
+
+ string::const_iterator begin;
+ string::const_iterator end;
+
+ public:
+ struct token_t
+ {
+ enum kind_t {
+ UNKNOWN,
+
+ TOK_DATE,
+ TOK_INT,
+ TOK_SLASH,
+ TOK_DASH,
+ TOK_DOT,
+
+ TOK_A_YEAR,
+ TOK_A_MONTH,
+ TOK_A_DAY,
+ TOK_A_WDAY,
+
+ TOK_SINCE,
+ TOK_UNTIL,
+ TOK_IN,
+ TOK_THIS,
+ TOK_NEXT,
+ TOK_LAST,
+
+ TOK_YEAR,
+ TOK_QUARTER,
+ TOK_MONTH,
+ TOK_WEEK,
+ TOK_DAY,
+
+ TOK_YEARLY,
+ TOK_QUARTERLY,
+ TOK_BIMONTHLY,
+ TOK_MONTHLY,
+ TOK_BIWEEKLY,
+ TOK_WEEKLY,
+ TOK_DAILY,
+
+ TOK_YEARS,
+ TOK_QUARTERS,
+ TOK_MONTHS,
+ TOK_WEEKS,
+ TOK_DAYS,
+
+ END_REACHED
+
+ } kind;
+
+ typedef variant<string, int, date_specifier_t> content_t;
+
+ optional<content_t> value;
+
+ explicit token_t(kind_t _kind = UNKNOWN,
+ const optional<content_t>& _value = none)
+ : kind(_kind), value(_value) {
+ TRACE_CTOR(date_parser_t::lexer_t::token_t, "");
+ }
+ token_t(const token_t& tok)
+ : kind(tok.kind), value(tok.value) {
+ TRACE_CTOR(date_parser_t::lexer_t::token_t, "copy");
+ }
+ ~token_t() throw() {
+ TRACE_DTOR(date_parser_t::lexer_t::token_t);
+ }
+
+ token_t& operator=(const token_t& tok) {
+ if (this != &tok) {
+ kind = tok.kind;
+ value = tok.value;
+ }
+ return *this;
+ }
+
+ operator bool() const {
+ return kind != END_REACHED;
+ }
+
+ string to_string() const {
+ switch (kind) {
+ case UNKNOWN: return "UNKNOWN";
+ case TOK_DATE: return "TOK_DATE";
+ case TOK_INT: return "TOK_INT";
+ case TOK_SLASH: return "TOK_SLASH";
+ case TOK_DASH: return "TOK_DASH";
+ case TOK_DOT: return "TOK_DOT";
+ case TOK_A_YEAR: return "TOK_A_YEAR";
+ case TOK_A_MONTH: return "TOK_A_MONTH";
+ case TOK_A_DAY: return "TOK_A_DAY";
+ case TOK_A_WDAY: return "TOK_A_WDAY";
+ case TOK_SINCE: return "TOK_SINCE";
+ case TOK_UNTIL: return "TOK_UNTIL";
+ case TOK_IN: return "TOK_IN";
+ case TOK_THIS: return "TOK_THIS";
+ case TOK_NEXT: return "TOK_NEXT";
+ case TOK_LAST: return "TOK_LAST";
+ case TOK_YEAR: return "TOK_YEAR";
+ case TOK_QUARTER: return "TOK_QUARTER";
+ case TOK_MONTH: return "TOK_MONTH";
+ case TOK_WEEK: return "TOK_WEEK";
+ case TOK_DAY: return "TOK_DAY";
+ case TOK_YEARLY: return "TOK_YEARLY";
+ case TOK_QUARTERLY: return "TOK_QUARTERLY";
+ case TOK_BIMONTHLY: return "TOK_BIMONTHLY";
+ case TOK_MONTHLY: return "TOK_MONTHLY";
+ case TOK_BIWEEKLY: return "TOK_BIWEEKLY";
+ case TOK_WEEKLY: return "TOK_WEEKLY";
+ case TOK_DAILY: return "TOK_DAILY";
+ case TOK_YEARS: return "TOK_YEARS";
+ case TOK_QUARTERS: return "TOK_QUARTERS";
+ case TOK_MONTHS: return "TOK_MONTHS";
+ case TOK_WEEKS: return "TOK_WEEKS";
+ case TOK_DAYS: return "TOK_DAYS";
+ case END_REACHED: return "END_REACHED";
+ }
+ assert(false);
+ return empty_string;
+ }
+
+ void unexpected();
+ static void expected(char wanted, char c = '\0');
+ };
+
+ token_t token_cache;
+
+ lexer_t(string::const_iterator _begin,
+ string::const_iterator _end)
+ : begin(_begin), end(_end)
+ {
+ TRACE_CTOR(date_parser_t::lexer_t, "");
+ }
+ lexer_t(const lexer_t& lexer)
+ : begin(lexer.begin), end(lexer.end),
+ token_cache(lexer.token_cache)
+ {
+ TRACE_CTOR(date_parser_t::lexer_t, "copy");
+ }
+ ~lexer_t() throw() {
+ TRACE_DTOR(date_parser_t::lexer_t);
+ }
+
+ token_t next_token();
+ void push_token(token_t tok) {
+ assert(token_cache.kind == token_t::UNKNOWN);
+ token_cache = tok;
+ }
+ token_t peek_token() {
+ if (token_cache.kind == token_t::UNKNOWN)
+ token_cache = next_token();
+ return token_cache;
+ }
+ };
+
+ string arg;
+ lexer_t lexer;
+
+ date_interval_t parse_date_expr();
+
+public:
+ date_parser_t(const string& _arg)
+ : arg(_arg), lexer(arg.begin(), arg.end()) {
+ TRACE_CTOR(date_parser_t, "");
+ }
+ date_parser_t(const date_parser_t& parser)
+ : arg(parser.arg), lexer(parser.lexer) {
+ TRACE_CTOR(date_parser_t, "copy");
+ }
+ ~date_parser_t() throw() {
+ TRACE_DTOR(date_parser_t);
+ }
+
+ date_interval_t parse() {
+ return date_interval_t();
+ }
+};
+
void times_initialize();
void times_shutdown();
+void show_period_tokens(std::ostream& out, const string& arg);
+void analyze_period(std::ostream& out, const string& arg);
+
std::ostream& operator<<(std::ostream& out, const date_duration_t& duration);
} // namespace ledger