C++ boost——时间与日期
文章目录
- timer
- progress_timer
- date_time
- 创建日期
- 访问日期
- 日期输出
- 转换C结构
- 日期长度
- 日期运算
- 日期区间
- 日期区间运算
- 日期迭代器
- 其他功能
- 综合运用
timer
是一个小型计时器,提供毫秒级的精度
适用于要求不高的程序计时任务
class timer
{
public :timer()(_start_time = std::clock();}void restart(){_start_time = std::clock();}double elapsed() const { return double(std::clock() - _start_time)/ CLOCKS _PER_SEC; }double elapsed_min() const{return double(1)/double(CLOCKS_PER_SEC);)double elapsed_max() const{return (double((std::numeric_limits<std::clock_t>::max)())- double(_start_time))/double(CLOCKS _PER_SEC);}private :std::clockt _start_time;
};
#include <boost/timer.hpp>
using namespace boost; int main()
{timer t;cout<<"max timespan:"<<t.elapsed_max() /3600 << "h" <<endl;cout<<"min timespan:"<<t.elapsed_min()<< "s" << endl;cout << "now time elapsed:"<< t.elapsed()<<"s"<< endl;
}
progress_timer
派生自timer,自动计时的小工具
class progress_timer : public timer, noncopyable
{
public:explicit progress_timer();progress_timer(std::ostream& os);~progress_timer();
};
#include<boost/progress.hpp>int main()
{// 第一个计时器{boost::progress_timer t;// do sth}std::stringstream ss;{boost::progress_timer t(ss);// do sth}std::cout<<ss.str();
}
date_time
date_time库基于格里高利历,支持从“1400-01-01”到“9999-12-31”
处理日期的组件
#include<boost/date_time/gregorian/gregorian.hpp>
处理时间组件
#include <boost/date_time/posix_time/posix_time.hpp>
template<typename T, typename calendar, typename duration_type>
class date
{
public:date(year_type, month_type, day_type);date(const ymd type &);year_type year() const;month_type month() const;day_type day() const;day_of_week_type day_of_week() const;ymd_type year_month_day() const;bool operator<(const date type &) const;bool operator==(const date type &) const;// 有效验证bool is_special() const;bool is_not_a_date() const;bool is_infinity() const;bool is_pos_infinity() const;bool is_neg_infinity() const;special_values as_special() const;duration_type operator-(const date type &)const;...
};
创建日期
date d1; // 无效日期
date d2(2010, 1, 1);
date d3(2010, Jan, 1);
date d4(d2);assert(d1 == date(not_a_date_time));
assert(d2 == d4);
assert(d3 < d4);date d1 = from_string("2010-12-20");
date d2(from_string("2010-12-20"));
date d3 = from_undelimited_string("20100502");
访问日期
date d(2017, 6, 1);
date::ymd_type ymd = d.year_month_day();
assert(ymd.year == 2013);
assert(ymd.month == 2);
assert(ymd.day == 2);cout << d.day_of_week()<< endl; // Thu 周四
cout << d.day of year()<< endl; // 152 当年第152天// week_number()返回 date所在的周是当年的第几周
cout << date(2015,1,10).week_number()<< endl; // 2
日期输出
date d(2017,1,23);
cout<< to_simple_string(d)<< endl; // 2017-Jan-23
cout <<to_iso_string(d)<< endl; // 20170123
cout << to_iso_extended_string(d)<< endl; // 2017-01-23
cout << d<< endl; // 2017-Jan-23
转换C结构
date d(2017,5,20);
tmt = to_tm(d);
assert(t.tmyear == 117 && t.tmmday == 20);date d2 = date_from_tm(t);
assert(d == d2);
日期长度
日期长度是以天为单位的时长
是任意整数,可正可负
基本的日期长度类是date_duration
class date_duration
{
public:date_duration(long);date_duration(special_values);long day() const;bool is_special() const;bool operator==(const date duration &) const;...static date duration unit(); // 时长单位
};
date_time 库为 date_duration 定义了一个常用的typedef: days,这个新名字更好地说明了 date_duration的含义–它可以用来计量天数。
days dd1(10), dd2(-100), dd3(255);
assert( ddl > dd2 && ddl < dd3);
assert(ddl + dd2 == days(-90));
assert((dd1 + dd3).days() == 265);
assert(dd3/5 == days(51));
为了方便计算时间长度,date_time库还提供了months、years、weeks3 个时长类,分别用来表示月、年和星期,它们的含义与days 类似,但其行为不太相同。
weeks w(3); //3个星期
assert(w.days() == 21);months m(5);
years y(2);months m2 = y + m; //2年零5个月
assert(m2.number_of_months() == 29);
assert((y*2).number_of_years()== 4);
日期运算
date dl(2000, 1, 1), d2(2017, 11, 18);
cout<<d2 - d1<< endl; // 6531天
assert(d1 + (d2 - d1) == d2);d1 += days(10); //2000-1-11
assert(d1.day() == 11);
dl += months(2); //2000-3-11
assert(dl.month() == 3 && d1.day() == 11);
d1 -= weeks(l); //2000-3-04
assert(dl.day() == 4);d2 -= years(10); //2007-11-18
assert(d2.year() == d1.year() + 7);
日期与特殊日期长度、特殊日期与日期长度进行运算的结果也是特殊日期:
date d1(2017,1,1);date d2 = d1 + days(pos_infin);
assert(d2.is_pos_infinity());d2 = d1 + days(not_a_date_time);
assert(d2.is_not_a_date());
d2 = date(neg_infin);
days dd = d1 - d2;
assert(dd.is_special() && !dd.is_negative());
如果日期是月末的最后一天,那么加减月或年会得到同样的月末时间
但当天数是月末的28或29时,如果加减月份到2月份,那么随后的运算就总是月末操作,原来的天数信息就会丢失
date d(2017, 3, 30);
d -= months(1); //2017-2-28,变为月末,原30的日期信息丢失
d -= months(l); //2017-1-31
d += months(2); //2017-3-31
assert(d.day() == 31); //与原来日期不相等
使用 days 则不会出现这样的问题
如果担心 weeks、months、years 这些时长类的使用进而扰乱了代码
可以 undef 宏 BOOST_DATE_TIME_OPTIONAL_GREGORIAN_TYPES
这将使date_time库不包含它们的定义头文件<boost/date time/gregorian/greg_duration types .hpp>
日期区间
- date_time 库使用 date_period 来表示日期区间的概念
- 它是时间轴上的一个左闭右开的区间,其端点是两个date对象
- 日期区间的左边界必须小于右边界,否则date period将表示一个无效的日期区间。
date_period 的类摘要如下:
class date period
{
public:period(date, date); period(date, days); date begin() const;date end() const;date last() const;date length() const;boo1 is_null()const;bool operator==(const period &) const;bool operator<(const period &) const;void shift(const days&); //平移void expand(const days &); //扩展boo1 contains(const date &) const; //区间运算boo1 contains(const period &)const;boo1 intersects(const period &)const;boo1 is_adjacent(const period &)const;bool is_before(const date &)const;boo1 is_after(const date &)const;period intersection(const period &)const;period merge(const period &)const;period span(const period &)const;
}
date_period可以指定区间的两个端点构造区间,也可以指定左端点再加上时长构造区间,通常后一种方法比较常用,这相当于在生活中从某天开始的一个周期。例如:
date_period dpl(date(2017, 1, 1), days(20));
date period dp2(date(2017, 1, 1), date(2016, 1, 1)); // 无效
date_period dpl(date(2017, 5, 1), days(-20)); // 无效
- begin()和last()返回日期区间的两个端点,
- end()返回last()后面第一天,与标准容器中end()含义相同,这是一个“逾尾位置”。
- length()返回日期区间的长度,以天为单位。如果日期区间左大右小或长度为0,那么is_null()将返回true
date period 还支持输入输出操作符,默认的输入输出格式是一个[YYYY-mmm-DD/YYYY-mmm-DD]
date_period dpl(date(2017, 1, 1), days(20));
date_period dp2(date(2017, 2, 19), days(10));
cout << dpl; //[2017-Jan-01/2017-Jan-20]
日期区间运算
shift()和expand()可以变动区间:shift()将日期区间平移n天而长度不变
expand()将日期区间向两端延伸n天,相当于区间长度增加2n天
date_period dp(date(2017, 1, 1), days(20));dp.shift(days(3));
assert(dp.begin().day() == 4);
assert(dp.length().days() == 20);dp.expand(days(3));
assert(dp.begin().day() == 1);
assert(dp.length().days() == 26);
date_period可以使用成员函数判断某个日期是否在区间内,还可以计算日期区间的交集。
- is_before()/is_after(): 日期区间是否在日期前或后。
- contains(): 日期区间是否包含另一个区间或日期。
- intersects(): 两个日期区间是否存在交集。
- intersection(): 返回两个区间的交集,如果无交集,则返回一个无效区间。
- is_adjacent(): 两个日期区间是否相邻。
date_period dp(date(2010,1,1),days(20)); //1-1至1-20assert(dp.is_after(date(2009, 12, 1)));
assert(dp.is_before(date(2010, 2, 1)));
assert(dp.contains(date(2010, 1, 10)));date_period dp2(date(2010, 1, 5), days(10)); //1-5至1-15
assert(dp.contains(dp2));assert(dp.intersects(dp2));
assert(dp.intersection(dp2) == dp2);date_period dp3(date(2010, 1, 21), days(5)); //1-21至1-26
assert(!dp3.intersects(dp2));
assert(dp3.intersection(dp2).is_null());assert(dp.is adjacent(dp3));
assert(!dp.intersects(dp3));
- merge(): 返回两个日期区间的并集,如果日期区间无交集或不相邻,则返回无效区间。
- span(): 合并两个日期区间及两者间的间隔,相当于广义的merge()。
date_period dpl(date(2010, 1, 1), days(20));
date_period dp2(date(2010, 1, 5), days(10));
date_period dp3(date(2010, 2, 1), days(5));
date_period dp4(date(2010, 1, 15), days(10));assert(dpl.contains(dp2) && dpl.merge(dp2) == dpl);
assert(!dpl.intersects(dp3) && dpl.merge(dp3).is_null());
assert(dp1.intersects(dp2) && dp1.merge(dp4).end() == dp4.end());
assert( dpl.span(dp3).end() == dp3.end());
日期迭代器
日期迷代器的用法基本类似,都需要在构造时传入一个起始日期和增减步长(可以是天、两周或N个月等,默认是1个单位)
date d(2007, 9, 28);
day_iterator d_iter(d); //增减步长默认为1天assert(d_iter == d);
++ d_iter; //递增1天
assert(d_iter == date(2007, 9, 28));year_iterator y_iter(*d_iter, 10); //增减步长为10年
assert(y_iter == d + days(l));
++y_iter; //递增10年
assert(y_iter->year() == 2017);
day_iterator、week iterator并不符合标准迭代器的定义,如果没有aifferenceype、pointer、reference, 等内部类型的定义,就不能使用std::advance()或 operator+=来前进或后退。
day_iterator iter(day_clock::local_day()); //声明一个日期迭代器
++iter; //使用operator++,正确iter += 5; // 错误,编译失败
std::advance(iter,5); // 错误,编译失败
其他功能
成员函数is_leap_year()可以判断年份是否是闰年
end_of_month_day()可以给定年份和月份,并返回该月的最后一天
typedef gregorian_calendar gre_cal;cout << "Y2017 is "<<(gre_cal::is_leap_year(2017)? "" : "not")<< " a leap year."<< endl;assert(gre_cal::end_of_month_day(2017, 2) == 28);
综合运用
计算信用卡的免息期
使用信用卡的当天称为消费日,信用卡每月有一个记账日,在记账日之后有一个固定的免息还款期限,通常为20天,因此每笔信用卡交易的免息期就是消费日到下一个记账日的时间再加上还款期限,最长可以达到50天。
class credit_card
{
public:credit card(const char*bname,int no) : bank name(bname),bill day no(no){}string bank_name; //银行名int bill_day_no; //记账日int calc_free_days(date consume_day = day_clock::local_day()) const{date bill_day(consume_day.year(), consume day.month(), bill_day_no); //得到记账日if(consume_day > bill_day)bill_day += months(l); //如果过了记账日,则是下个月的记账日return (bill_day - consume_day).days() + 20;}// 为了支持比较操作,我们还需要为creditcard增加小于比较操作符重载friend bool operator < (const credit_card& l, const credit_card& r){return (l.calc_free days() < r.calc free days());//比较免息期}
};credit_card a("A bank", 25);
credit_card b("B bank", 12);credit_card tmp = std::max(a, b);