gnucash/src/libqof/qof/gnc-datetime.cpp

188 lines
6.0 KiB
C++
Raw Normal View History

/********************************************************************\
* gnc-datetime.cpp -- Date and Time classes for GnuCash *
* *
* Copyright 2015 John Ralls <jralls@ceridwen.us> *
* *
* 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 <boost/date_time/gregorian/gregorian.hpp>
#include <memory>
#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<PTime, boost::date_time::time_zone_base<PTime, char>>;
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>(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;
2015-04-26 20:01:23 -05:00
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();
}
2015-04-26 20:01:23 -05:00
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();
}
2015-04-26 20:01:23 -05:00
std::string
GncDateTime::format(const char* format) const
{
return m_impl->format(format);
}