昨日のDateTimeライブラリを少し拡張して、複数のprimitiveをoperator&で繋ぎ、まとめて代入できるようにしました。
可変引数にしなかったのは、Boost.PPを使いたくなかったからです。可変引数として扱いたい場合は簡単に後付けすることができます。
なお、つなげたprimitiveはFusion Sequenceとなるようにしたので、ユーザーコードがかなり柔軟になっています。
shand/date_time.hpp
#ifndef SHAND_DATE_TIME_INCLUDE #define SHAND_DATE_TIME_INCLUDE #include <cstddef> #include <ctime> #include <map> #include <boost/assert.hpp> #include <boost/xpressive/xpressive_static.hpp> #include <boost/xpressive/regex_actions.hpp> #include <boost/assign/list_of.hpp> #include <boost/format.hpp> #include <boost/lambda/lambda.hpp> #include <boost/fusion/include/make_vector.hpp> #include <boost/fusion/include/push_back.hpp> #include <boost/fusion/include/is_sequence.hpp> #include <boost/fusion/include/for_each.hpp> #include <boost/utility/enable_if.hpp> namespace shand { template <int> class primitive { std::size_t value_; public: primitive() : value_(0) {} explicit primitive(std::size_t value) : value_(value) {} std::size_t value() const { return value_; } }; typedef primitive<0> year; typedef primitive<1> month; typedef primitive<2> day; typedef primitive<3> hour; typedef primitive<4> minute; typedef primitive<5> second; #ifdef SHAND_DATE_TIME_CUSTOM_NOW_TIME std::time_t now_time_t(); #else inline std::time_t now_time_t() { std::time_t t; std::time(&t); return t; } #endif template <int I, int J> inline boost::fusion::vector<primitive<I>, primitive<J> > operator&(const primitive<I>& a, const primitive<J>& b) { return boost::fusion::make_vector(a, b); } template <class Seq, int I> inline typename boost::enable_if<boost::fusion::traits::is_sequence<Seq>, typename boost::fusion::result_of::push_back<const Seq, primitive<I> >::type >::type operator&(const Seq& a, const primitive<I>& b) { return boost::fusion::push_back(a, b); } namespace detail { class primitive_assigner { std::tm* st_; public: primitive_assigner(std::tm* st) : st_(st) {} void operator()(const year& x) const { st_->tm_year = x.value() - 1900; } void operator()(const month& x) const { st_->tm_mon = x.value() - 1; } void operator()(const day& x) const { st_->tm_mday = x.value(); } void operator()(const hour& x) const { st_->tm_hour = x.value(); } void operator()(const minute& x) const { st_->tm_min = x.value(); } void operator()(const second& x) const { st_->tm_sec = x.value(); } }; } // namespace detail class date_time { std::time_t time_; template <class F> date_time& assign_time(F f) { std::time_t tmp = time_; std::tm* st = std::localtime(&tmp); f(st); time_ = std::mktime(st); return *this; } template <class F> static date_time calc_time(const date_time& d, F f) { std::time_t tmp = d.time_; std::tm* st = std::localtime(&tmp); f(st); return date_time(std::mktime(st)); } public: date_time() {} explicit date_time(std::time_t t) : time_(t) {} template <class Seq> date_time(const Seq& seq, typename boost::enable_if<boost::fusion::traits::is_sequence<Seq> >::type* = 0) { std::time_t tmp = now_time_t(); std::tm* st = std::localtime(&tmp); boost::fusion::for_each(seq, detail::primitive_assigner(st)); time_ = std::mktime(st); } std::string format(const std::string& fmt) const { BOOST_ASSERT(time_ >= 0); std::tm* st = std::localtime(&time_); const std::map<std::string, std::string> rep = boost::assign::list_of (std::make_pair("%Y", (boost::format("%04d") % (1900 + st->tm_year)).str())) (std::make_pair("%m", (boost::format("%02d") % (1 + st->tm_mon)).str())) (std::make_pair("%d", (boost::format("%02d") % st->tm_mday).str())) (std::make_pair("%H", (boost::format("%02d") % st->tm_hour).str())) (std::make_pair("%M", (boost::format("%02d") % st->tm_min).str())) (std::make_pair("%S", (boost::format("%02d") % st->tm_sec).str())) ; using namespace boost::xpressive; local<std::string const *> pstr; const sregex rx = (a1 = rep)[pstr = &a1]; return regex_replace(fmt, rx, *pstr); } static date_time now() { return date_time(now_time_t()); } time_t to_time_t() const { return time_; } // assign template <class Seq> typename boost::enable_if<boost::fusion::traits::is_sequence<Seq>, date_time&>::type operator=(const Seq& seq) { std::time_t tmp = time_; std::tm* st = std::localtime(&tmp); boost::fusion::for_each(seq, detail::primitive_assigner(st)); time_ = std::mktime(st); return *this; } date_time& operator=(const year& x) { return assign_time(boost::lambda::_1 ->* &std::tm::tm_year = x.value() - 1900); } date_time& operator=(const month& x) { return assign_time(boost::lambda::_1 ->* &std::tm::tm_mon = x.value() - 1); } date_time& operator=(const day& x) { return assign_time(boost::lambda::_1 ->* &std::tm::tm_mday = x.value()); } date_time& operator=(const hour& x) { return assign_time(boost::lambda::_1 ->* &std::tm::tm_hour = x.value()); } date_time& operator=(const minute& x) { return assign_time(boost::lambda::_1 ->* &std::tm::tm_min = x.value()); } date_time& operator=(const second& x) { return assign_time(boost::lambda::_1 ->* &std::tm::tm_sec = x.value()); } // add friend date_time operator+(const date_time& d, const year& x) { return calc_time(d, boost::lambda::_1 ->* &std::tm::tm_year += x.value()); } friend date_time operator+(const date_time& d, const month& x) { return calc_time(d, boost::lambda::_1 ->* &std::tm::tm_mon += x.value()); } friend date_time operator+(const date_time& d, const day& x) { return calc_time(d, boost::lambda::_1 ->* &std::tm::tm_mday += x.value()); } friend date_time operator+(const date_time& d, const hour& x) { return calc_time(d, boost::lambda::_1 ->* &std::tm::tm_hour += x.value()); } friend date_time operator+(const date_time& d, const minute& x) { return calc_time(d, boost::lambda::_1 ->* &std::tm::tm_min += x.value()); } friend date_time operator+(const date_time& d, const second& x) { return calc_time(d, boost::lambda::_1 ->* &std::tm::tm_sec += x.value()); } // substract friend date_time operator-(const date_time& d, const year& x) { return date_time::calc_time(d, boost::lambda::_1 ->* &std::tm::tm_year -= x.value()); } friend date_time operator-(const date_time& d, const month& x) { return date_time::calc_time(d, boost::lambda::_1 ->* &std::tm::tm_mon -= x.value()); } friend date_time operator-(const date_time& d, const day& x) { return date_time::calc_time(d, boost::lambda::_1 ->* &std::tm::tm_mday -= x.value()); } friend date_time operator-(const date_time& d, const hour& x) { return date_time::calc_time(d, boost::lambda::_1 ->* &std::tm::tm_hour -= x.value()); } friend date_time operator-(const date_time& d, const minute& x) { return date_time::calc_time(d, boost::lambda::_1 ->* &std::tm::tm_min -= x.value()); } friend date_time operator-(const date_time& d, const second& x) { return date_time::calc_time(d, boost::lambda::_1 ->* &std::tm::tm_sec -= x.value()); } }; inline day diff_day(const date_time& a, const date_time& b) { const std::time_t& at = (std::max)(a.to_time_t(), b.to_time_t()); const std::time_t& bt = (std::min)(a.to_time_t(), b.to_time_t()); return day(static_cast<std::size_t>(std::difftime(at, bt) / (60 * 60 * 24))); } inline hour diff_hour(const date_time& a, const date_time& b) { const std::time_t& at = (std::max)(a.to_time_t(), b.to_time_t()); const std::time_t& bt = (std::min)(a.to_time_t(), b.to_time_t()); return hour(static_cast<std::size_t>(std::difftime(at, bt) / (60 * 60))); } inline minute diff_minute(const date_time& a, const date_time& b) { const std::time_t& at = (std::max)(a.to_time_t(), b.to_time_t()); const std::time_t& bt = (std::min)(a.to_time_t(), b.to_time_t()); return minute(static_cast<std::size_t>(std::difftime(at, bt) / 60)); } inline second diff_second(const date_time& a, const date_time& b) { const std::time_t& at = (std::max)(a.to_time_t(), b.to_time_t()); const std::time_t& bt = (std::min)(a.to_time_t(), b.to_time_t()); return second(static_cast<std::size_t>(std::difftime(at, bt))); } } // namespace shand #endif // SHAND_DATE_TIME_INCLUDE
テスト
#include <boost/detail/lightweight_test.hpp> #include <boost/fusion/include/adapt_struct.hpp> #define SHAND_DATE_TIME_CUSTOM_NOW_TIME // 現在時間を返す関数を書き換える #include <shand/date_time.hpp> namespace shand { std::time_t now_time_t() { std::tm t; t.tm_year = 2010 - 1900; t.tm_mon = 12 - 1; t.tm_yday = 353; t.tm_mday = 20; t.tm_wday = 1; t.tm_hour = 15; t.tm_min = 30; t.tm_sec = 45; t.tm_isdst = 0; return std::mktime(&t); } } namespace fusion = boost::fusion; struct UserDate { shand::year year; shand::month month; shand::day day; UserDate(const shand::year& year_, const shand::month& month_, const shand::day& day_) : year(year_), month(month_), day(day_) {} }; BOOST_FUSION_ADAPT_STRUCT( UserDate, (shand::year, year) (shand::month, month) (shand::day, day) ) int main() { using namespace shand; // 現在日時をフォーマット指定して出力 BOOST_TEST(date_time::now().format("%Y/%m/%d %H:%M:%S") == "2010/12/20 15:30:45"); // 1年加算 { const date_time tm = date_time::now(); BOOST_TEST((tm + year(1)).format("%Y/%m/%d %H:%M:%S") == "2011/12/20 15:30:45"); } // 月を書き換え { date_time tm = date_time::now(); tm = month(3); BOOST_TEST(tm.format("%Y/%m/%d %H:%M:%S") == "2010/03/20 15:30:45"); } // 今月の日数を求める(次の月の1日から1日引く) { date_time tm = date_time::now(); BOOST_TEST((((tm + month(1)) = day(1)) - day(1)).format("%d") == "31"); } // &で繋げてまとめて設定(mktimeが1回で済む) { const date_time tm = year(2010) & month(3) & day(1) & hour(15) & minute(30) & second(12); BOOST_TEST(tm.format("%Y/%m/%d %H:%M:%S") == "2010/03/01 15:30:12"); } { date_time tm = date_time::now(); tm = year(2010) & month(3) & day(1) & hour(15) & minute(30) & second(12); BOOST_TEST(tm.format("%Y/%m/%d %H:%M:%S") == "2010/03/01 15:30:12"); } // Fusion Sequenceにアダプトされたユーザー定義型を、shand::date_timeとして扱う { const date_time tm = UserDate(year(2010), month(3), day(1)); BOOST_TEST(tm.format("%Y/%m/%d") == "2010/03/01"); } // 日数の差を計算 { const date_time a = year(2010) & month(3) & day(1); const date_time b = year(2010) & month(3) & day(5); BOOST_TEST(diff_day(a, b).value() == 4); } return boost::report_errors(); }