/********************************************************************\ * gnc-datetime.cpp -- Date and Time classes for GnuCash * * * * Copyright 2015 John Ralls * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License* * along with this program; if not, contact: * * * * Free Software Foundation Voice: +1-617-542-5942 * * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * * Boston, MA 02110-1301, USA gnu@gnu.org * * * \********************************************************************/ extern "C" { #include "config.h" #include "platform.h" } #include #include #include "gnc-timezone.hpp" #include "gnc-datetime.hpp" using Date = boost::gregorian::date; using Month = boost::gregorian::greg_month; using PTime = boost::posix_time::ptime; using LDT = boost::local_time::local_date_time; using Duration = boost::posix_time::time_duration; using LDTBase = boost::local_time::local_date_time_base>; using time64 = int64_t; static const TimeZoneProvider tzp; // For converting to/from POSIX time. static const PTime unix_epoch (Date(1970, boost::gregorian::Jan, 1), boost::posix_time::seconds(0)); /* To ensure things aren't overly screwed up by setting the nanosecond clock for boost::date_time. Don't do it, though, it doesn't get us anything and slows down the date/time library. */ #ifndef BOOST_DATE_TIME_HAS_NANOSECONDS static constexpr auto ticks_per_second = INT64_C(1000000); #else static constexpr auto ticks_per_second = INT64_C(1000000000); #endif /** Private implementation of GncDate. See the documentation for that class. */ class GncDateImpl { public: GncDateImpl(): m_greg(unix_epoch.date()) {} GncDateImpl(const int year, const int month, const int day) : m_greg(year, static_cast(month), day) {} GncDateImpl(Date d) : m_greg(d) {} void today() { m_greg = boost::gregorian::day_clock::local_day(); } private: Date m_greg; }; /** Private implementation of GncDateTime. See the documentation for that class. */ static LDT LDT_from_unix_local(const time64 time) { PTime temp(unix_epoch.date(), boost::posix_time::hours(time / 3600) + boost::posix_time::seconds(time % 3600)); auto tz = tzp.get(temp.date().year()); return LDT(temp, tz); } static LDT LDT_from_struct_tm(const struct tm tm) { auto tdate = boost::gregorian::date_from_tm(tm); auto tdur = boost::posix_time::time_duration(tm.tm_hour, tm.tm_min, tm.tm_sec, 0); auto tz = tzp.get(tdate.year()); return LDT(PTime(tdate, tdur), tz); } class GncDateTimeImpl { public: GncDateTimeImpl() : m_time(unix_epoch, tzp.get(unix_epoch.date().year())) {} GncDateTimeImpl(const time64 time) : m_time(LDT_from_unix_local(time)) {} GncDateTimeImpl(const struct tm tm) : m_time(LDT_from_struct_tm(tm)) {} GncDateTimeImpl(PTime&& pt) : m_time(pt, tzp.get(pt.date().year())) {} GncDateTimeImpl(LDT&& ldt) : m_time(ldt) {} operator time64() const; operator struct tm() const; void now() { m_time = boost::local_time::local_sec_clock::local_time(tzp.get(boost::gregorian::day_clock::local_day().year())); } long offset() const; std::string format(const char* format) const; private: LDT m_time; }; GncDateTimeImpl::operator time64() const { auto duration = m_time.utc_time() - unix_epoch; auto secs = duration.ticks(); secs /= ticks_per_second; return secs; } GncDateTimeImpl::operator struct tm() const { return to_tm(m_time); } long GncDateTimeImpl::offset() const { auto offset = m_time.local_time() - m_time.utc_time(); return offset.total_seconds(); } std::string GncDateTimeImpl::format(const char* format) const { using Facet = boost::local_time::local_time_facet; std::stringstream ss; //The stream destructor frees the facet, so it must be heap-allocated. auto output_facet(new Facet(format)); ss.imbue(std::locale(std::locale(), output_facet)); ss << m_time; return ss.str(); } /* =================== Presentation-class Implementations ====================*/ GncDate::GncDate() : m_impl{new GncDateImpl} {} GncDate::GncDate(int year, int month, int day) : m_impl(new GncDateImpl(year, month, day)) {} GncDate::~GncDate() = default; void GncDate::today() { m_impl->today(); } GncDateTime::GncDateTime() : m_impl(new GncDateTimeImpl) {} GncDateTime::GncDateTime(const time64 time) : m_impl(new GncDateTimeImpl(time)) {} GncDateTime::GncDateTime(const struct tm tm) : m_impl(new GncDateTimeImpl(tm)) {} GncDateTime::~GncDateTime() = default; void GncDateTime::now() { m_impl->now(); } GncDateTime::operator time64() const { return m_impl->operator time64(); } GncDateTime::operator struct tm() const { return m_impl->operator struct tm(); } long GncDateTime::offset() const { return m_impl->offset(); } std::string GncDateTime::format(const char* format) const { return m_impl->format(format); }