在平时的开发过程中,难免会遇到时间相关的处理,比如心跳、定时任务、超时处理等等,总是很难在众多时间处理函数中选择一个。在假期这几天把经常用到的一些时间处理函数整理了一下,算是个梳理也是备忘吧。
在VS2015中,time_t
的定义为:
#ifndef _CRT_NO_TIME_T
#ifdef _USE_32BIT_TIME_T
typedef __time32_t time_t;
#else
typedef __time64_t time_t;
#endif
#endif
time_t的默认类型是 __time64_t
,而__time64_t
的定义为 typedef __int64 __time64_t;
可以看到time_t就是一个64位的整型类型。
time_t 保存的是自1970年1月1日00:00:00以来的秒数。这个时间点就是常见的 Epoch 时间
struct tm {
int tm_sec; // 秒,正常范围从 0 到 59,但允许至 61
int tm_min; // 分,范围从 0 到 59
int tm_hour; // 小时,范围从 0 到 23
int tm_mday; // 一月中的第几天,范围从 1 到 31
int tm_mon; // 月,范围从 0 到 11
int tm_year; // 自 1900 年起的年数
int tm_wday; // 一周中的第几天,范围从 0 到 6,从星期日算起
int tm_yday; // 一年中的第几天,范围从 0 到 365,从 1 月 1 日算起
int tm_isdst; // 夏令时
}
在VS2015中,clock_t
的定义为:
typedef long clock_t;
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
通过 clock_gettime()
函数获取系统的当前时间。
参数 clk_id : 检索和设置的clk_id指定的时钟时间。可以设置如下:
CLOCK_REALTIME:系统实时时间,随系统实时时间改变而改变,即从UTC1970-1-1 0:0:0开始计时,如果系统时间被用户改成其他,则对应的时间相应改变
CLOCK_REALTIME_COARSE:和CLOCK_REALTIME类似,但是执行速度快,精度低
CLOCK_MONOTONIC:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响
CLOCK_MONOTONIC_COARSE :和CLOCK_MONOTONIC类似,但是执行速度快,精度低
CLOCK_BOOTTIME:和 CLOCK_MONOTONIC 类似,但是包括了系统休眠的时间。
CLOCK_PROCESS_CPUTIME_ID:本进程到当前代码系统CPU花费的时间
CLOCK_THREAD_CPUTIME_ID:本线程到当前代码系统CPU花费的时间
参数:tp 返回时间值,返回值 0成功, 1失败。
struct timeval {
long tv_sec; /* seconds */
long tv_nsec; /* microseconds */
};
通过 gettimeofday()
函数获取系统的当前时间:
参数:
struct timeval *tv 将带回当前的系统时间,从UTC1970-1-1 0:0:0开始计时
struct timezone *tz 带回当前的时区信息,如果不需要刻意设置为0
timezone结构描述如下:
struct timezone {
int tz_minuteswest; /* minutes west of Greenwich 和格林威治 时间差了多少分钟 */
int tz_dsttime; /* type of dst correction 日光节约时间的状态(夏时制)*/
};
功能:获取系统当前时间。
头文件:
#include
返回值:该函数返回系统当前的日历时间,自1970年1月1日00:00 以来经过的秒数。如果系统没有时间,返回-1
参数:如果time不为nullptr,则返回值也存储在time指向的对象中
#include
#include
int main()
{
time_t result = time(nullptr);
std::cout << "自Epoch以来的秒数:" << result << std::endl;
system("pause");
}
自Epoch以来的秒数:1522943543
请按任意键继续…
功能:获取一个指示本地时间的
struct tm
结构头文件:
#include
返回值:返回指向
struct tm
结构的指针,该结构带有被填充的时间信息,使用本地时区表示。(struct tm
结构定义见上文)参数:当前时间
#include <iostream>
#include <time.h>
int main()
{
time_t result = time(nullptr);
struct tm* formatTime = localtime(&result);
std::cout
<< "tm_sec: " << formatTime->tm_sec << std::endl
<< "tm_min: " << formatTime->tm_min << std::endl
<< "tm_hour: " << formatTime->tm_hour << std::endl
<< "tm_mday: " << formatTime->tm_mday << std::endl
<< "tm_mon: " << formatTime->tm_mon << std::endl
<< "tm_year: " << formatTime->tm_year << std::endl
<< "tm_wday: " << formatTime->tm_wday << std::endl
<< "tm_yday: " << formatTime->tm_yday << std::endl
<< "tm_isdst: " << formatTime->tm_isdst << std::endl;
system("pause");
}
tm_sec: 11
tm_min: 24
tm_hour: 0
tm_mday: 6
tm_mon: 3
tm_year: 118
tm_wday: 5
tm_yday: 95
tm_isdst: 0
请按任意键继续…
功能:获取表示当前时间的字符串指针
头文件:
#include
返回值:返回表示当前时间的字符串指针,字符串形式
Www Mmm dd hh:mm:ss yyyy\n\0
,其中,Www 表示星期几,Mmm 是以字母表示的月份,dd 表示一月中的第几天,hh:mm:ss 表示时间,yyyy 表示年份。参数:当前时间
#include
#include
int main()
{
time_t result = time(nullptr);
std::cout << "现在时间是:" << ctime(&result);
system("pause");
}
现在时间是:Fri Apr 6 00:05:27 2018
请按任意键继续…
功能:获取表示当前时间的字符串指针 (ps: 功能及返回值同
ctime
,但是参数不同)头文件:
#include
返回值:返回一个指向字符串的指针,字符串包含了 time 所指向结构中存储的信息,字符串形式
Www Mmm dd hh:mm:ss yyyy\n\0
,其中,Www 表示星期几,Mmm 是以字母表示的月份,dd 表示一月中的第几天,hh:mm:ss 表示时间,yyyy 表示年份参数:指向
struct tm
结构的当前时间的指针
#include
#include
int main()
{
time_t result = time(nullptr);
struct tm* formatTime = localtime(&result);
std::cout << "现在时间是:" << asctime(formatTime);
system("pause");
}
现在时间是:Fri Apr 6 00:41:41 2018
请按任意键继续…
作用:自定义日期时间输出格式
头文件:
#include
返回值:
参数:
strDest
用来存放格式化后的字符串缓存,maxsize
指定最多可以输出的字符数,format
格式化字符串,time
要转换的时间
格式化字符串 | 含义 | 格式化字符串 | 含义 | 格式化字符串 | 含义 | 格式化字符串 | 含义 |
---|---|---|---|---|---|---|---|
%a | 星期几的简写 | %F | 年 - 月 - 日 | %n | 新行符 | %V | 每年的第几周,使用基于周的年 |
%A | 星期几的全称 | %g | 年份的后两位数字,使用基于周的年 | %p | 本地的AM或PM的等价显示 | %w | 十进制表示的星期几(值从0到6,星期天为0) |
%b | 月分的简写 | %G | 年分,使用基于周的年 | %r | 12小时的时间 | %W | 每年的第几周,把星期一做为第一天(值从0到53) |
%B | 月份的全称 | %h | 简写的月份名 | %R | 显示小时和分钟:hh : mm | %x | 标准的日期串 |
%c | 标准的日期的时间串 | %H | 24小时制的小时 | %S | 十进制的秒数 | %X | 标准的时间串 |
%C | 年份的后两位数字 | %I | 12小时制的小时 | %t | 水平制表符 | %y | 不带世纪的十进制年份(值从0到99) |
%d | 十进制表示的每月的第几天 | %j | 十进制表示的每年的第几天 | %T | 显示时分秒:hh : mm : ss | %Y | 带世纪部分的十进制年份 |
%D | 月 / 天 / 年 | %m | 十进制表示的月份 | %u | 每周的第几天,星期一为第一天 (值从0到6,星期一为0) | %z %Z | 时区名称,如果不能得到时区名称则返回空字符。 |
%e | 在两字符域中,十进制表示的每月的第几天 | %M | 十进制表示的分钟数 | %U | 第年的第几周,把星期日做为第一天(值从0到53) | %% | 百分号 |
#include
#include
int main(void)
{
time_t result = time(nullptr);
struct tm *localTime = localtime(&result);
char outBuffer[40] = { 0 };
strftime(outBuffer, 40, "%Y-%m-%d %H:%M:%S", localTime);
std::cout << "now: " << result << " = " << outBuffer << std::endl;
system("pause");
}
now: 1522951679 = 2018-04-06 02:07:59
请按任意键继续…
功能:使用
time_t
的值来填充struct tm
结构,使用格林尼治标准时间(GMT)表示 。(ps: 可以对比localtime
函数)头文件:
#include
返回值:被填充了的
struct tm
结构参数:表示日历时间的
time_t
类型的指针
#include <iostream>
#include <time.h>
int main()
{
time_t result = time(nullptr);
struct tm* formatTime = gmtime(&result);
std::cout
<< "tm_sec: " << formatTime->tm_sec << std::endl
<< "tm_min: " << formatTime->tm_min << std::endl
<< "tm_hour: " << formatTime->tm_hour << std::endl
<< "tm_mday: " << formatTime->tm_mday << std::endl
<< "tm_mon: " << formatTime->tm_mon << std::endl
<< "tm_year: " << formatTime->tm_year << std::endl
<< "tm_wday: " << formatTime->tm_wday << std::endl
<< "tm_yday: " << formatTime->tm_yday << std::endl
<< "tm_isdst: " << formatTime->tm_isdst << std::endl;
system("pause");
}
tm_sec: 30
tm_min: 57
tm_hour: 16
tm_mday: 5
tm_mon: 3
tm_year: 118
tm_wday: 4
tm_yday: 94
tm_isdst: 0
请按任意键继续…
可以看到:日期时间较localtime不一样了,格林尼治标准时间(GMT)比当地时间要晚8个小时。
功能:与
gmtime
函数功能相反,使用一个struct tm
类型的指针获取一个日历时间 (ps,若参数time
指向的是格林尼治标准时间,返回的time_t
也是格林尼治标准时间,比当地时间晚8小时;若参数time
指向的是本地时间,返回的也是本地日期时间)头文件:
#include
返回值:日历时间
参数:已经被填充的
struct tm
结构指针
举例省略
功能:返回
time2
与time1
之间相差的秒数,time2
与time1
都是日历时间,都表示距离1970年1月1日00::00:00的秒数头文件:
#include
返回值:两个时间点之间相差的秒数
参数:两个日历时间
#include
#include
#include
int main()
{
time_t time1 = time(nullptr);
Sleep(3000); // 睡眠3秒
time_t time2 = time(nullptr);
std::cout << "程序运行的秒数:" << difftime(time2, time1) << std::endl;
system("pause");
}
程序运行的秒数:3
请按任意键继续…
功能:返回”进程开始启动”到“程序中调用
clock()
函数”时之间的CPU时钟计时单元(clock tick)数,通常使用两次clock调用,返回之间CPU使用时间头文件:
#include
返回值:CPU时钟计时单元数
参数:无
#include
#include
#include
int main()
{
clock_t start = clock();
Sleep(1234); // 睡眠1234毫秒
clock_t end = clock();
std::cout
<< "start: " << start << std::endl
<< "end: " << end << std::endl
<< "CLOCKS_PER_SEC: " << CLOCKS_PER_SEC << std::endl
<< "程序运行的秒数:" << (double)(end - start) / CLOCKS_PER_SEC << std::endl;
system("pause");
}
start: 253
end: 1487
CLOCKS_PER_SEC: 1000
程序运行的秒数:1.234
请按任意键继续…
可以看到,clock()
可以精确到毫秒
std::chrono
是C++11
在boost
中引入的,用于C++的时间相关的处理。
std::chrono
库,需要 #include
std::chrono
库的所有组件,实现都在 std::chrono
命名空间下,所以各个组件的调用都要加上 std::chrono
前缀duration
持续时间对象通过诸如分钟,两小时或十毫秒的计数来表示时间跨度。例如:
“60秒”这个持续时间:
可以用由60个周期为1s的时间长度表示;也可以由1个周期为1min的时间长度表示。
在 std::chrono
中,duration
可表示六种持续时间,分别是: 小时、分钟、秒、毫秒、微秒、纳秒,定义如下:
// duration TYPEDEFS
typedef duration<long long, nano> nanoseconds;
typedef duration<long long, micro> microseconds;
typedef duration<long long, milli> milliseconds;
typedef duration<long long> seconds;
typedef duration<int, ratio<60> > minutes;
typedef duration<int, ratio<3600> > hours;
duration
的声明如下:
// CLASS TEMPLATE duration
template<class _Rep, class _Period = ratio<1> >
class duration;
可以看到,duration
是一个模板类。可以把 _Rep
理解为上面提到的多少个周期,比如60,把 _Period
理解为一个周期有多长时间。 六种持续时间都是 duration
根据不同模板参数 typedef 而来的。
这里简单说一下周期的默认参数 ratio
这个类型。
ratio
的声明如下:
template1>
struct ratio;
模板类 ratio
有两个数值型模板参数,可以简单的把 ratio
想象成一个 比率
类型。_Nx
作为分子,_Dx
作为分母。根据 _Nx
与 _Dx
的相对大小区别不同的时间分辨率。
几个分辨率的定义:
typedef ratio<1, 1000000000> nano;
typedef ratio<1, 1000000> micro;
typedef ratio<1, 1000> milli;
以上对 duration
进行了简单介绍,下面看下 duration
的使用吧!
#include
#include
using namespace std::chrono;
int main()
{
// 定义 '100毫秒的持续时间'
duration<long long, std::milli> one_handurd_millis(100); // 或者这样定义: milliseconds one_handurd_millis(100);
std::cout << "分辨率(一个周期长): " << duration<long long, std::milli>::period::num << "/" << duration<long long, std::milli>::period::den << " s" << std::endl
<< "周期数: " << one_handurd_millis.count() << std::endl;
one_handurd_millis += milliseconds(30);
std::cout << "加上30ms后,原时间长度变为: " << one_handurd_millis.count() << std::endl;
system("pause");
}
结果:
分辨率(一个周期长): 1/1000 s
周期数: 100
加上30ms后,原时间长度变为: 130
请按任意键继续…
clock
三种时钟类型都是作为 time_point
类型的参数,单独看时钟类型没有意义。
只是 system_clock
和 steady_clock
都有一个静态成员函数: now()
可用。
#include
#include
using namespace std::chrono;
int main()
{
std::cout << system_clock::now().time_since_epoch().count() << std::endl;
std::cout << steady_clock::now().time_since_epoch().count() << std::endl;
time_t t = system_clock::to_time_t(system_clock::now());
std::cout << ctime(&t);
system("pause");
}
结果:
15230306967929631
90472482570038
Sat Apr 7 00:04:56 2018
请按任意键继续…
time_point
时间点需要以某一个时钟的计算为基准,使用 time_point
需要一个时钟类型。
#include
#include
using namespace std::chrono;
int main()
{
system_clock::time_point tp = system_clock::now(); // 或者这样定义:time_point tp;
time_t t = system_clock::to_time_t(tp);
std::cout << "距离Epoch时间点已经过去: " << duration_cast(tp.time_since_epoch()).count() << "ms" << std::endl
<< "现在时间是: " << ctime(&t);
tp += hours(24);
time_t t1 = system_clock::to_time_t(tp);
std::cout << "24小时后: " << ctime(&t1);
system("pause");
}
结果:
距离Epoch时间点已经过去: 1523032933770ms
现在时间是: Sat Apr 7 00:42:13 2018
24小时后: Sun Apr 8 00:42:13 2018
请按任意键继续…
Poco::Timestamp
内部存储了一个距离Epoch
时间点的微秒级整型变量,主要的对外接口有以下几个:
Poco::Timestamp
的默认构造函数会使用当前时间构造一个时间戳。例如:Poco::Timestamp ts;
。void update()
方法会把时间戳更新到当前时间。==、!=、>、>=、<、<=
。+、-、+=、-=
,这些方法的参数有 Timestamp
,也有另外的 Poco::Timespan
(时间间隔) ,如果需要深入了解这个类,可以看官网文档: https://pocoproject.org/docs/Poco.Timespan.html 。TimeDiff elapsed() const;
和 bool isElapsed(TimeDiff interval) const;
。Epoch
和 UTC
时间的相互转化。std::time_t epochTime() const;
和 UtcTimeVal utcTime() const;
静态函数 static Timestamp fromEpochTime(std::time_t t);
和 static Timestamp fromUtcTime(UtcTimeVal val);
。在服务器里面,要维护多个客户端的心跳,对每个客户端都用一个定时器维护心跳是不可能的,这时候 Poco::TImestamp
用来判断心跳是否超时以及是否到了发送心跳的时间就比较合适.
Poco::DateTime
内部存储了 short 类型的年、月、日、时、分、秒、毫秒、微秒
以及一个 int64 类型的分辨率为100纳秒的utc时间戳
。跟Poco::Timestamp
相比,Poco::DateTime
主要是对 ”年月日时分秒“ 进行操作与获取,接口类型也与Poco::Timestamp
类似,而这些在程序中较少用到,代码也不多,就不花篇幅讲述了。Poco::DateTime
的接口文档可以从官网获取 https://pocoproject.org/docs/Poco.DateTime.html 。
今天就先写到这儿,本来准备把 boost::date_time
也一块比较下,看了下书,发现 date_time
库很杂,而且 date_time
库还需要编译安装,boost
在平时的开发中用的也不多,就不放到这里了。