summaryrefslogtreecommitdiff
path: root/src/times.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/times.h')
-rw-r--r--src/times.h632
1 files changed, 632 insertions, 0 deletions
diff --git a/src/times.h b/src/times.h
new file mode 100644
index 00000000..826937bb
--- /dev/null
+++ b/src/times.h
@@ -0,0 +1,632 @@
+/*
+ * Copyright (c) 2003-2009, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @addtogroup util
+ */
+
+/**
+ * @file times.h
+ * @author John Wiegley
+ *
+ * @ingroup util
+ *
+ * @brief datetime_t and date_t objects
+ */
+#ifndef _TIMES_H
+#define _TIMES_H
+
+#include "utils.h"
+
+namespace ledger {
+
+DECLARE_EXCEPTION(datetime_error, std::runtime_error);
+DECLARE_EXCEPTION(date_error, std::runtime_error);
+
+typedef boost::posix_time::ptime datetime_t;
+typedef datetime_t::time_duration_type time_duration_t;
+
+inline bool is_valid(const datetime_t& moment) {
+ return ! moment.is_not_a_date_time();
+}
+
+typedef boost::gregorian::date date_t;
+typedef boost::gregorian::date_iterator date_iterator_t;
+
+inline bool is_valid(const date_t& moment) {
+ return ! moment.is_not_a_date();
+}
+
+extern optional<datetime_t> epoch;
+
+#ifdef BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK
+#define TRUE_CURRENT_TIME() (boost::posix_time::microsec_clock::universal_time())
+#define CURRENT_TIME() (epoch ? *epoch : TRUE_CURRENT_TIME())
+#else
+#define TRUE_CURRENT_TIME() (boost::posix_time::second_clock::universal_time())
+#define CURRENT_TIME() (epoch ? *epoch : TRUE_CURRENT_TIME())
+#endif
+#define CURRENT_DATE() \
+ (epoch ? epoch->date() : boost::gregorian::day_clock::universal_day())
+
+extern date_time::weekdays start_of_week;
+
+optional<date_time::weekdays>
+string_to_day_of_week(const std::string& str);
+optional<date_time::months_of_year>
+string_to_month_of_year(const std::string& str);
+
+typedef optional<date_t::year_type> optional_year;
+
+datetime_t parse_datetime(const char * str, optional_year current_year = none);
+
+inline datetime_t parse_datetime(const std::string& str,
+ optional_year current_year = none) {
+ return parse_datetime(str.c_str(), current_year);
+}
+
+date_t parse_date(const char * str, optional_year current_year = none);
+
+inline date_t parse_date(const std::string& str,
+ optional_year current_year = none) {
+ return parse_date(str.c_str(), current_year);
+}
+
+enum format_type_t {
+ FMT_WRITTEN, FMT_PRINTED, FMT_CUSTOM
+};
+
+std::string format_datetime(const datetime_t& when,
+ const format_type_t format_type = FMT_PRINTED,
+ const optional<const char *>& format = none);
+void set_datetime_format(const char * format);
+
+std::string format_date(const date_t& when,
+ const format_type_t format_type = FMT_PRINTED,
+ const optional<const char *>& format = none);
+void set_date_format(const char * format);
+void set_input_date_format(const char * format);
+
+inline void to_xml(std::ostream& out, const datetime_t& when,
+ bool wrap = true)
+{
+ if (wrap) {
+ push_xml x(out, "datetime");
+ out << format_datetime(when, FMT_WRITTEN);
+ } else {
+ out << format_datetime(when, FMT_WRITTEN);
+ }
+}
+
+inline void to_xml(std::ostream& out, const date_t& when,
+ bool wrap = true)
+{
+ if (wrap) {
+ push_xml x(out, "date");
+ out << format_date(when, FMT_WRITTEN);
+ } else {
+ out << format_date(when, FMT_WRITTEN);
+ }
+}
+
+struct date_traits_t
+{
+ bool has_year;
+ bool has_month;
+ bool has_day;
+
+ date_traits_t(bool _has_year = false,
+ bool _has_month = false,
+ bool _has_day = false)
+ : has_year(_has_year), has_month(_has_month), has_day(_has_day) {
+ TRACE_CTOR(date_traits_t, "bool, bool, bool");
+ }
+ date_traits_t(const date_traits_t& traits)
+ : has_year(traits.has_year),
+ has_month(traits.has_month),
+ has_day(traits.has_day) {
+ TRACE_CTOR(date_traits_t, "copy");
+ }
+ ~date_traits_t() throw() {
+ TRACE_DTOR(date_traits_t);
+ }
+
+ date_traits_t& operator=(const date_traits_t& traits) {
+ has_year = traits.has_year;
+ has_month = traits.has_month;
+ has_day = traits.has_day;
+ return *this;
+ }
+
+ bool operator==(const date_traits_t& traits) const {
+ return (has_year == traits.has_year &&
+ has_month == traits.has_month &&
+ has_day == traits.has_day);
+ }
+
+#if defined(HAVE_BOOST_SERIALIZATION)
+private:
+ /** Serialization. */
+
+ friend class boost::serialization::access;
+
+ template<class Archive>
+ void serialize(Archive& ar, const unsigned int /* version */) {
+ ar & has_year;
+ ar & has_month;
+ ar & has_day;
+ }
+#endif // HAVE_BOOST_SERIALIZATION
+};
+
+struct date_duration_t
+{
+ enum skip_quantum_t {
+ DAYS, WEEKS, MONTHS, QUARTERS, YEARS
+ } quantum;
+ int length;
+
+ date_duration_t() : quantum(DAYS), length(0) {
+ TRACE_CTOR(date_duration_t, "");
+ }
+ date_duration_t(skip_quantum_t _quantum, int _length)
+ : quantum(_quantum), length(_length) {
+ TRACE_CTOR(date_duration_t, "skip_quantum_t, int");
+ }
+ date_duration_t(const date_duration_t& dur)
+ : quantum(dur.quantum), length(dur.length) {
+ TRACE_CTOR(date_duration_t, "copy");
+ }
+ ~date_duration_t() throw() {
+ TRACE_DTOR(date_duration_t);
+ }
+
+ date_t add(const date_t& date) const {
+ switch (quantum) {
+ case DAYS:
+ return date + gregorian::days(length);
+ case WEEKS:
+ return date + gregorian::weeks(length);
+ case MONTHS:
+ return date + gregorian::months(length);
+ case QUARTERS:
+ return date + gregorian::months(length * 3);
+ case YEARS:
+ return date + gregorian::years(length);
+ default:
+ assert(false); return date_t();
+ }
+ }
+
+ date_t subtract(const date_t& date) const {
+ switch (quantum) {
+ case DAYS:
+ return date - gregorian::days(length);
+ case WEEKS:
+ return date - gregorian::weeks(length);
+ case MONTHS:
+ return date - gregorian::months(length);
+ case QUARTERS:
+ return date - gregorian::months(length * 3);
+ case YEARS:
+ return date - gregorian::years(length);
+ default:
+ assert(false); return date_t();
+ }
+ }
+
+ string to_string() const {
+ std::ostringstream out;
+
+ out << length << ' ';
+
+ switch (quantum) {
+ case DAYS: out << "day"; break;
+ case WEEKS: out << "week"; break;
+ case MONTHS: out << "month"; break;
+ case QUARTERS: out << "quarter"; break;
+ case YEARS: out << "year"; break;
+ default:
+ assert(false);
+ break;
+ }
+
+ if (length > 1)
+ out << 's';
+
+ return out.str();
+ }
+
+ static date_t find_nearest(const date_t& date, skip_quantum_t skip);
+
+#if defined(HAVE_BOOST_SERIALIZATION)
+private:
+ /** Serialization. */
+
+ friend class boost::serialization::access;
+
+ template<class Archive>
+ void serialize(Archive& ar, const unsigned int /* version */) {
+ ar & quantum;
+ ar & length;
+ }
+#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(const optional<year_type>& _year = none,
+ const optional<month_type>& _month = none,
+ const optional<day_type>& _day = none,
+ const optional<day_of_week_type>& _wday = none)
+ : year(_year), month(_month), day(_day), wday(_wday) {
+ TRACE_CTOR(date_specifier_t,
+ "year_type, month_type, day_type, day_of_week_type");
+ }
+ date_specifier_t(const date_t& date,
+ const optional<date_traits_t>& traits = none) {
+ TRACE_CTOR(date_specifier_t, "date_t, date_traits_t");
+ if (! traits || traits->has_year)
+ year = date.year();
+ if (! traits || traits->has_month)
+ month = date.month();
+ if (! traits || traits->has_day)
+ day = date.day();
+ }
+ date_specifier_t(const date_specifier_t& other)
+ : year(other.year), month(other.month),
+ day(other.day), wday(other.wday) {
+ TRACE_CTOR(date_specifier_t, "copy");
+ }
+ ~date_specifier_t() throw() {
+ TRACE_DTOR(date_specifier_t);
+ }
+
+ date_t begin(const optional_year& current_year = none) const;
+ date_t end(const optional_year& current_year = none) const;
+
+ bool is_within(const date_t& date,
+ const optional_year& current_year = none) const {
+ return date >= begin(current_year) && date < end(current_year);
+ }
+
+ optional<date_duration_t> implied_duration() const {
+ if (day || wday)
+ return date_duration_t(date_duration_t::DAYS, 1);
+ else if (month)
+ return date_duration_t(date_duration_t::MONTHS, 1);
+ else if (year)
+ return date_duration_t(date_duration_t::YEARS, 1);
+ else
+ return none;
+ }
+
+ string to_string() const {
+ std::ostringstream out;
+
+ if (year)
+ out << " year " << *year;
+ if (month)
+ out << " month " << *month;
+ if (day)
+ out << " day " << *day;
+ if (wday)
+ out << " wday " << *wday;
+
+ return out.str();
+ }
+
+#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;
+
+ bool end_inclusive;
+
+public:
+ date_range_t(const optional<date_specifier_t>& _range_begin = none,
+ const optional<date_specifier_t>& _range_end = none)
+ : range_begin(_range_begin), range_end(_range_end),
+ end_inclusive(false) {
+ TRACE_CTOR(date_range_t, "date_specifier_t, date_specifier_t");
+ }
+ date_range_t(const date_range_t& other)
+ : range_begin(other.range_begin), range_end(other.range_end),
+ end_inclusive(other.end_inclusive) {
+ TRACE_CTOR(date_range_t, "date_range_t");
+ }
+ ~date_range_t() throw() {
+ TRACE_DTOR(date_range_t);
+ }
+
+ optional<date_t> begin(const optional_year& current_year = none) const {
+ if (range_begin)
+ return range_begin->begin(current_year);
+ else
+ return none;
+ }
+ optional<date_t> end(const optional_year& current_year = none) const {
+ if (range_end) {
+ if (end_inclusive)
+ return range_end->end(current_year);
+ else
+ return range_end->begin(current_year);
+ } else {
+ return none;
+ }
+ }
+
+ bool is_within(const date_t& date,
+ const optional_year& 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;
+ }
+
+ string to_string() const {
+ std::ostringstream out;
+
+ if (range_begin)
+ out << "from" << range_begin->to_string();
+ if (range_end)
+ out << " to" << range_end->to_string();
+
+ return out.str();
+ }
+
+#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;
+ ar & end_inclusive;
+ }
+#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:
+ date_specifier_or_range_t() {
+ TRACE_CTOR(date_specifier_or_range_t, "");
+ }
+ date_specifier_or_range_t(const date_specifier_or_range_t& other)
+ : specifier_or_range(other.specifier_or_range) {
+ TRACE_CTOR(date_specifier_or_range_t, "copy");
+ }
+ date_specifier_or_range_t(const date_specifier_t& specifier)
+ : specifier_or_range(specifier) {
+ TRACE_CTOR(date_specifier_or_range_t, "date_specifier_t");
+ }
+ date_specifier_or_range_t(const date_range_t& range)
+ : specifier_or_range(range) {
+ TRACE_CTOR(date_specifier_or_range_t, "date_range_t");
+ }
+ ~date_specifier_or_range_t() throw() {
+ TRACE_DTOR(date_specifier_or_range_t);
+ }
+
+ optional<date_t> begin(const optional_year& 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_year& 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;
+ }
+
+
+ string to_string() const {
+ std::ostringstream out;
+
+ if (specifier_or_range.type() == typeid(date_specifier_t))
+ out << "in" << boost::get<date_specifier_t>(specifier_or_range).to_string();
+ else if (specifier_or_range.type() == typeid(date_range_t))
+ out << boost::get<date_range_t>(specifier_or_range).to_string();
+
+ return out.str();
+ }
+
+#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:
+ static date_t add_duration(const date_t& date,
+ const date_duration_t& duration);
+ static date_t subtract_duration(const date_t& date,
+ const date_duration_t& duration);
+
+ optional<date_specifier_or_range_t> range;
+
+ optional<date_t> start; // the real start, after adjustment
+ optional<date_t> finish; // the real end, likewise
+ bool aligned;
+ optional<date_t> next;
+ optional<date_duration_t> duration;
+ optional<date_t> end_of_duration;
+
+ explicit date_interval_t() : aligned(false) {
+ TRACE_CTOR(date_interval_t, "");
+ }
+ date_interval_t(const string& str) : aligned(false) {
+ TRACE_CTOR(date_interval_t, "const string&");
+ parse(str);
+ }
+ date_interval_t(const date_interval_t& other)
+ : range(other.range),
+ start(other.start),
+ finish(other.finish),
+ aligned(other.aligned),
+ next(other.next),
+ duration(other.duration),
+ end_of_duration(other.end_of_duration) {
+ TRACE_CTOR(date_interval_t, "copy");
+ }
+ ~date_interval_t() throw() {
+ TRACE_DTOR(date_interval_t);
+ }
+
+ bool operator==(const date_interval_t& other) const {
+ return (start == other.start &&
+ (! start || *start == *other.start));
+ }
+
+ operator bool() const {
+ return is_valid();
+ }
+
+ optional<date_t> begin(const optional_year& current_year = none) const {
+ return start ? start : (range ? range->begin(current_year) : none);
+ }
+ optional<date_t> end(const optional_year& current_year = none) const {
+ return finish ? finish : (range ? range->end(current_year) : none);
+ }
+
+ void parse(const string& str);
+
+ void resolve_end();
+ void stabilize(const optional<date_t>& date = none);
+
+ bool is_valid() const {
+ return start;
+ }
+
+ /** 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);
+
+ optional<date_t> inclusive_end() const {
+ if (end_of_duration)
+ return *end_of_duration - gregorian::days(1);
+ else
+ return none;
+ }
+
+ date_interval_t& operator++();
+
+ void dump(std::ostream& out, optional_year current_year = 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 & range;
+ ar & start;
+ ar & finish;
+ ar & aligned;
+ ar & next;
+ ar & duration;
+ ar & end_of_duration;
+ }
+#endif // HAVE_BOOST_SERIALIZATION
+};
+
+void times_initialize();
+void times_shutdown();
+
+void show_period_tokens(std::ostream& out, const string& arg);
+
+std::ostream& operator<<(std::ostream& out, const date_duration_t& duration);
+
+} // namespace ledger
+
+#endif // _TIMES_H