C++ 时间操作chrono库(实现系统时间与字符串时间相互转换)
目录
C++11提供了chrono模版库,实现了一系列时间相关的操作(时间长度、系统时间和计时器)
头文件:#include <chrono>
命名空间:std::chrono
一、时间长度
duration模板类用于表示一段时间(时间长度、时钟周期),如:1小时、8分钟、5秒。
duration的定义如下:
template<class Rep, class Period = std::ratio<1, 1>>
class duration
{
……
};
为了方便使用,定义了一些常用的时间长度,比如:时、分、秒、毫秒、微秒、纳秒,它们都位于std::chrono命名空间下,定义如下:
using hours = duration<Rep, std::ratio<3600>> // 小时
using minutes = duration<Rep, std::ratio<60>> // 分钟
using seconds = duration<Rep> // 秒
using milliseconds = duration<Rep, std::milli> // 毫秒
using microseconds = duration<Rep, std::micro> // 微秒
using nanoseconds = duration<Rep, std::nano> // 纳秒
注意:
- duration模板类重载了各种算术运算符,用于操作duration对象。
- duration模板类提供了count()方法,获取duration对象的值。
代码示例(注意溢出问题):
#include <iostream>
#include <chrono> // chrono库的头文件。
using namespace std;
int main()
{
chrono::hours t1(1); // 1小时
chrono::minutes t2(60); // 60分钟
chrono::seconds t3(60 * 60); // 60*60秒
chrono::milliseconds t4(60 * 60 * 1000); // 60*60*1000毫秒
chrono::microseconds t5(60 * 60 * 1000 * 1000); // 警告:整数溢出。
chrono::nanoseconds t6(60 * 60 * 1000 * 1000*1000); // 警告:整数溢出。
if (t1 == t2) cout << "t1==t2\n";
if (t1 == t3) cout << "t1==t3\n";
if (t1 == t4) cout << "t1==t4\n";
// 获取时间对象的值,返回的是int整数。
cout << "t1=" << t1.count() << endl;
cout << "t2=" << t2.count() << endl;
cout << "t3=" << t3.count() << endl;
cout << "t4=" << t4.count() << endl;
chrono::seconds t7(1); // 1秒
chrono::milliseconds t8(1000); // 1000毫秒
chrono::microseconds t9(1000 * 1000); // 1000*1000微秒
chrono::nanoseconds t10(1000 * 1000 * 1000); // 1000*1000*1000纳秒
if (t7 == t8) cout << "t7==t8\n";
if (t7 == t9) cout << "t7==t9\n";
if (t7 == t10) cout << "t7==t10\n";
// 获取时间对象的值。
cout << "t7=" << t7.count() << endl;
cout << "t8=" << t8.count() << endl;
cout << "t9=" << t9.count() << endl;
cout << "t10=" << t10.count() << endl;
}
二、系统时间
system_clock类支持了对系统时钟的访问,提供了三个静态成员函数:
// 返回当前时间的时间点。
static std::chrono::time_point<std::chrono::system_clock> now() noexcept;
// 将时间点time_point类型转换为std::time_t 类型。
static std::time_t to_time_t( const time_point& t ) noexcept;
// 将std::time_t类型转换为时间点time_point类型。
static std::chrono::system_clock::time_point from_time_t( std::time_t t ) noexcept;
tm结构体与常用时间函数:
struct tm {
int tm_sec; /* seconds after the minute - [0,59] */
int tm_min; /* minutes after the hour - [0,59] */
int tm_hour; /* hours since midnight - [0,23] */
int tm_mday; /* day of the month - [1,31] */
int tm_mon; /* months since January - [0,11] */
int tm_year; /* years since 1900 */
int tm_wday; /* days since Sunday - [0,6] */
int tm_yday; /* days since January 1 - [0,365] */
int tm_isdst; /* daylight savings time flag */
};
time_t time(time_t *t); //取得从1970年1月1日至今的秒数
char *asctime(const struct tm *tm); //将结构中的信息转换为真实世界的时间,以字符串的形式显示
char *ctime(const time_t *timep); //将timep转换为真是世界的时间,以字符串显示,它和asctime不同就在于传入的参数形式不一样
struct tm *gmtime(const time_t *timep); //将time_t表示的时间转换为没有经过时区转换的UTC时间,是一个struct tm结构指针
struct tm *localtime(const time_t *timep); //和gmtime类似,但是它是经过时区转换的时间。
time_t mktime(struct tm *tm); //将struct tm 结构的时间转换为从1970年至今的秒数
int gettimeofday(struct timeval *tv, struct timezone *tz); //返回当前距离1970年的秒数和微妙数,后面的tz是时区,一般不用
double difftime(time_t time1, time_t time2); //返回两个时间相差的秒数
代码示例1(获取系统时间,并将其转换为自定义的时间格式输出):
#define _CRT_SECURE_NO_WARNINGS // 使用localtime()需要这个宏。
#include <iostream>
#include <chrono>
#include <iomanip> // put_time()函数需要包含的头文件。
#include <sstream>
using namespace std;
int main()
{
// 1)静态成员函数chrono::system_clock::now()用于获取系统时间。(C++时间)
auto now = chrono::system_clock::now();
// 2)静态成员函数chrono::system_clock::to_time_t()把系统时间转换为time_t。
//(UTC时间,即世界时间,中国位于东八区使用的北京时间,UTC时间==北京时间-0800,即北京时间回退八小时)
auto t_now = chrono::system_clock::to_time_t(now);
// t_now = t_now + 24*60*60; // 把当前时间加1天。
// t_now = t_now + -1*60*60; // 把当前时间减1小时。
// t_now = t_now + 120; // 把当前时间加120秒。
// 3)std::localtime()函数把time_t转换成本地时间。(北京时)
//localtime获取的时间以秒为基本单位,为从1970年1月1日0时0分0秒(格林尼治时间)开始到现在(本地时间)经过的秒数.
// localtime()不是线程安全的,VS用localtime_s()代替,Linux用localtime_r()代替。
auto tm_now = std::localtime(&t_now);
// 4)格式化输出tm结构体中的成员。
//Y代表年,m代表月份,d代表天数,H代表小时,M代表分钟,S代表秒数
std::cout << std::put_time(tm_now, "%Y-%m-%d %H:%M:%S") << std::endl;
std::cout << std::put_time(tm_now, "%Y-%m-%d") << std::endl;
std::cout << std::put_time(tm_now, "%H:%M:%S") << std::endl;
std::cout << std::put_time(tm_now, "%Y%m%d%H%M%S") << std::endl;
//输出时间方法2,可用于将时间输出到文件中保存时间点.
stringstream ss; // 创建stringstream对象ss,需要包含<sstream>头文件。
ss << std::put_time(tm_now, "%Y-%m-%d %H:%M:%S");// 把时间输出到对象ss中。
string timestr = ss.str(); // 把ss转换成string的对象。
cout << timestr << endl;
}
代码示例2:(将人熟知的本地时间转换为UTC时间及系统时间,将代码1的四步倒着走,略有不同)
参考文章:c++的时间点time_point基本用法 - 炖丸子的猫 - 博客园
#define _CRT_SECURE_NO_WARNINGS // 使用localtime()需要这个宏。
#include <iostream>
#include <chrono>
#include <iomanip> // put_time()函数需要包含的头文件。
#include <sstream>
using namespace std;
int main()
{
// 1)静态成员函数chrono::system_clock::now()用于获取系统时间。(C++时间)
auto now = chrono::system_clock::now();
// 2)静态成员函数chrono::system_clock::to_time_t()把系统时间转换为time_t(UTC时间)。
//(UTC时间,即世界时间,中国位于东八区使用的北京时间,UTC时间==北京时间-0800,即北京时间回退八小时)
auto t_now = chrono::system_clock::to_time_t(now);
// localtime()不是线程安全的,VS用localtime_s()代替,Linux用localtime_r()代替。
auto tm_now = std::localtime(&t_now);
// 4)格式化输出tm结构体中的成员。
//Y代表年,m代表月份,d代表天数,H代表小时,M代表分钟,S代表秒数
std::cout << std::put_time(tm_now, "%Y-%m-%d %H:%M:%S") << std::endl;
//std::cout << std::put_time(tm_now, "%Y-%m-%d") << std::endl;
//std::cout << std::put_time(tm_now, "%H:%M:%S") << std::endl;
//std::cout << std::put_time(tm_now, "%Y%m%d%H%M%S") << std::endl;
//stringstream ss; // 创建stringstream对象ss,需要包含<sstream>头文件。
//ss << std::put_time(tm_now, "%Y-%m-%d %H:%M:%S"); // 把时间输出到对象ss中。
//string timestr = ss.str(); // 把ss转换成string的对象。
//cout << timestr << endl;
//方法1:
tm tm_now1;//可自定义输入时间点
struct tm* tm1=&tm_now1;
tm1->tm_year =125 ; tm1->tm_mon = 0; tm1->tm_mday = 19; //tm_year从1900开始,tm_mon从一月开始,设置的值为原值的加数
tm1->tm_hour = 18; tm1->tm_min = 2; tm1->tm_sec = 30,tm1->tm_isdst=-1;
auto t_now1 = mktime(tm1);//转换为time_t对象(UTC时间)
std::cout << std::put_time(localtime(&t_now1), "%Y-%m-%d %H:%M:%S") << std::endl;//确认这一步转换无误
std::cout << std::put_time(gmtime(&t_now1), "%Y-%m-%d %H:%M:%S") << std::endl;
auto now1 = chrono::system_clock::from_time_t(t_now1);//将time_t类型转换为time_point类型,即转换为系统时间(C++时间)
auto t_now2 = chrono::system_clock::to_time_t(now1);//测试转换是否正确
auto tm_now2 = std::localtime(&t_now2);
std::cout << std::put_time(tm_now2, "%Y-%m-%d %H:%M:%S") << std::endl;
std::cout << "compare: now >= now1 ?\t" << (now >= now1 ? "true" : "false") << std::endl;//结果应为ture
//方法2:
/*std::tm aTime;
std::string strTime("2008-08-08 10:0:0");
std::stringstream ssTime(strTime);
ssTime.imbue(std::locale("de_DE.utf-8"))//utf-8指定字符串格式;
ssTime >> std::get_time(&aTime, "%Y-%m-%d %H:%M:%S");
std::chrono::time_point<std::chrono::system_clock> tp = std::chrono::system_clock::from_time_t(std::mktime(&aTime));
std::time_t aTestTime = std::chrono::system_clock::to_time_t(tp);
std::cout << "aTime\t" << std::put_time(std::localtime(&aTestTime), "%F %T") << std::endl;
std::cout << "compare: nowTime >= aTime ?\t" << (nowTime >= tp ? "true" : "false") << std::endl;*/
}
tips: 在计算机应用上,2038年问题可能会导致某些软件在2038年无法正常工作。所有使用POSIX时间表示时间的程序都将受其影响,因为它们的时间起点是格林尼治时间1970年1月1日0时0分0秒(这个时间名叫 the Unix Epoch),它们用the Unix Epoch经过的秒数(忽略闰秒)来表示时间。这种时间表示法在类Unix(Unix-like)操作系统上是一个标准,并会影响以其C编程语言开发给其他大部份操作系统使用的软件。在大部分的32位操作系统上,此“time_t”数据模式使用一个有符号32位整数(signed int32)存储计算的秒数。依照此“time_t”标准,在此格式能被表示的最后时间是第2147483647秒(代表格林尼治时间2038年1月19日凌晨03:14:07)。下一秒,即格林尼治时间2038年1月19日凌晨03:14:08,由于32位整型溢出,时间将会被“绕回”(wrap around)成一个负数,变成了第 -2147483648 秒(代表格林尼治时间1901年12月13日20:45:52),造成应用程序发生严重的时间错误,而无法运行。
也许大家都已经知道计算机的2000年问题(the Millennium bug)是什么概念,但是什么时候又冒出来一个2038年问题。
用C语言编制的程序不会碰到2000年问题,但是会有2038年问题。这是因为,大多数C语言程序都使用到一个叫做“标准时间库”的程序库,这个时间库用一个标准的4字节也就是32位的形式来储存时间信息。
当初设计的时候,这个4字节的时间格式把1970年1月1日凌晨0时0分0秒(这个时间名叫 the Unix Epoch)作为时间起点,这时的时间值为0。以后所有的时间都是从这个时间开始一秒一秒累积得来的。
比方说如果时间已经累积到了919642718这个数值,就是说这时距离 the Unix Epoch已经过去了919642718秒,换算一下就应该是1999年2月21日16时18分38秒(代表北京时间1999年2月22日0时18分38秒)。
这样计算时间的好处在于,把任意两个时间值相减之后,就可以很迅速地得到这两个时间之间相差的秒数,然后你可以利用别的程序把它换算成明白易懂的年月日时分秒的形式。
要是你曾经读过一点儿关于计算机方面的书,你就会知道一个4字节也就是32位的存储空间的最大值是2147483647,请注意,2038年问题的关键也就在这里———当时间一秒一秒地跳完2147483647那惊心动魄的最后一秒后,我们的世界会发生什么。
答案是,它就会转为负数也就是说时间无效。那一刻的准确的时间为2038年1月19日星期二凌晨03:14:07,之后所有用到这种“标准时间库”的C语言程序都会碰到时间计算上的麻烦。
这就是2038年问题。
但是也不用太过紧张。2038年问题比千年虫(the Millennium bug)问题解决起来相对要容易一些,只要给那些程序换一个新版本的“标准时间库”就可以了,比如说,改用8字节64位的形式来存储时间。这样做并不怎么费事,因为在C程序中“标准时间库”是相对独立的一个部分,里面的时间表达都有自己的一套时间类型和参数(而在碰到Y2K的那些大型主机中,时间格式大都没有一)。
使用32位的数据类型来计算从新纪元开始的秒数,到2038年1月19日左右内核就会溢出。使用64位内核时这个问题就消失了。因为使用64位的数据类型来计算从新纪元开始的秒数直到100亿年后才能溢出。 [1]
而在一些用64位来表示时间的平台上,例如DigitalAlpha、SGI、Sparc等等,想要看到它们的时间出错你得等到天荒地老———那大概是2920亿年(大道都磨灭了)。到那时,位于猎户座旋臂的太阳,已经是黑矮星或暗黑物质,猎户座旋臂已经被重力波震断,银河系大概则已经变成小型似星体了。
所以如果时间将近2038年时,还存在32位机器在世界中运行,那将会受到2038年问题的影响。从这也可以看出为什么很多厂商都不再提供32位支持了。
总之,32位的最后时间是2038年1月19日03:14:07,星期二。
64位的最后时间约2900亿年后的292,277,026,596年12月4日15:30:08,星期日。
三、计时器
steady_clock类相当于秒表,操作系统只要启动就会进行时间的累加,常用于耗时的统计(精确到纳秒)。
#include <iostream>
#include <chrono>
using namespace std;
int main()
{
// 静态成员函数chrono::steady_clock::now()获取开始的时间点。
auto start = chrono::steady_clock::now();
// 执行一些代码,让它消耗一些时间。
cout << "计时开始 ...... \n";
for (int ii = 0; ii < 1000000; ii++) {
// cout << "我是一只傻傻鸟。\n";
}
cout << "计时完成 ...... \n";
// 静态成员函数chrono::steady_clock::now()获取结束的时间点。
auto end = chrono::steady_clock::now();
// 计算消耗的时间,单位是纳秒。
auto dt = end - start;
cout << "耗时: " << dt.count() << "纳秒("<<(double)dt.count()/(1000*1000*1000)<<"秒)";
}
原文地址:https://blog.csdn.net/qq_74224788/article/details/145242134
免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!