转换绝对时间和相对时间——std::chrono
C++11之前,想要获取时间并对其打印是有些困难的,因为C++并没有标准时间库。想要对时间进行统计就需要调用C库,并且我们要考虑这样的调用是否能很好的封装到我们的类中。
C++11之后,STL提供了chrono库,其让对时间的操作更加简单。
本节,我们将会使用本地时间,并对本地时间进行打印,还会给时间加上不同的偏移,这些操作很容易使用std::chrono完成。
How to do it...
我们将会对当前时间进行保存,并对其进行打印。另外,我们的程序还会为已经保存的时间点添加不同的偏移,并且打印偏移之后的时间:
包含必要的头文件,并声明所使用的命名空间:
#include <iostream> #include <iomanip> #include <chrono> using namespace std;我们将打印绝对时间点。使用
chrono::time_point模板类型来获取,因此需要对输出流操作符进行重载。对时间点的打印方式有很多,我们将会使用%c来表示标准时间格式。当然,可以只打印时间、日期或是我们需要的信息。调用put_time之前对不同类型的变量进行转换的方式看起来有些笨拙,不过这里只这么做一次:ostream& operator<<(ostream &os, const chrono::time_point<chrono::system_clock> &t) { const auto tt (chrono::system_clock::to_time_t(t)); const auto loct (std::localtime(&tt)); return os << put_time(loct, "%c"); }STL已经定义了
seconds,minutes,hours等时间类型,所以我们只需要为其添加days类型就好。这很简单,只需要对chrono::duration模板类型进行特化,将hours类型乘以24,就表示一天具有24个小时:using days = chrono::duration< chrono::hours::rep, ratio_multiply<chrono::hours::period, ratio<24>>>;为了用很有优雅的方式表示很多天,我们定义属于
days类型的字面值操作符。现在我们程序中写3_days就代表着3天:constexpr days operator ""_days(unsigned long long d) { return days{d}; }实际程序中,我们将会对时间点进行记录,然后就会对这个时间点进行打印。因为已经对操作符进行了重载,所以完成这样的事情就变得很简单:
int main() { auto now (chrono::system_clock::now()); cout << "The current date and time is " << now << '\n';我们使用
now函数来获得当前的时间点,并可以为这个时间添加一个偏移,然后再对其进行打印。为当前的时间添加12个小时,其表示的为12个小时之后的时间:chrono::hours chrono_12h {12}; cout << "In 12 hours, it will be " << (now + chrono_12h)<< '\n';这里将使用
chrono_literals命名空间中的函数,声明使用这个命名空间会解锁小时、秒等等时间类型的间隔字面值类型。这样我们就能很优雅的对12个小时15分之前的时间或7天之前的时间进行打印:using namespace chrono_literals; cout << "12 hours and 15 minutes ago, it was " << (now - 12h - 15min) << '\n' << "1 week ago, it was " << (now - 7_days) << '\n'; }编译并运行程序,我们将会获得如下的输出。因为使用
%c格式对时间进行打印,所以得到还不错的时间输出格式。通过对不同格式的的字符串进行操作,我们可以获得想要的格式。要注意的是,这里的时间格式并不是以12小时AM/PM方式表示,因为程序运行在欧洲操作系统上,所以使用24小时表示的方式:$ ./relative_absolute_times The current date and time is Fri May 5 13:20:38 2017 In 12 hours, it will be Sat May6 01:20:38 2017 12 hours and 15 minutes ago, it was Fri May5 01:05:38 2017 1 week ago, it was Fri Apr 28 13:20:38 2017
How it works...
我们可以通过std::chrono::system_clock来获取当前时间点。这个STL时钟类是唯一一个能将时间点的值转换成一个时间结构体的类型,其能将时间点以能够看懂的方式进行输出。
为了打印这样的时间点,我们可以对operator<<操作符进行重载:
ostream& operator<<(ostream &os,
const chrono::time_point<chrono::system_clock> &t)
{
const auto tt (chrono::system_clock::to_time_t(t));
const auto loct (std::localtime(&tt));
return os << put_time(loct, "%c");
}
首先,将chrono::time_point<chrono::system_clock>转换为std::time_t。然后,使用std::localtime将这个时间值进行转换,这样就能获取到一个本地时钟的相对时间值。这个函数会给我们返回一个转换后的指针(对于这个指针背后的内存不用我们多操心,因为其是一个静态对象,并不是从堆上分配的内存),这样我们就能完成最终的打印。
std::put_time函数接受一个流对象和一个时间格式字符串。%c表示标准时间格式字符串,例如Sun Mar 12 11:33:40 2017。我们也可以写成%m/%d/%y;之后,时间就会按照这个格式进行打印,比如03/12/17。时间格式符的描述很长,想要了解其具体描述的最好方式就是去查看C++参考手册。
除了打印,我们也会为我们的时间点添加偏移。这也很简单,比如:12小时15分钟就可以表示为12h+15min。chrono_literals命名空间为我们提供了字面类型:hours(h), minutes(min), seconds(s), milliseconds(ms), microseconds(us), nanoseconds(ns)。
通过对两个时间间隔的相加,我们会得到一个新的时间点。要实现这样的操作就需要对左加operator+和左减operator-操作符进行重载,这样对时间偏移的操作就会变得非常简单。