C++标准库之时间戳、时间段_c++ 时间戳的单位_Roy-bin的博客-程序员宅基地

技术标签: C++  

以前的时间日期库只能支持到秒、毫秒。并不能支持微妙纳秒,C++11带来了chrono,来提供高精度的时间日期库。头文件 < chrono >

chrono这个库主要目的是为不同的系统提供高精度的时间和时钟。为了不用每隔10年为一种时间类型重新解读,这次chrono干脆整出了两个新的概念: 
duration:时间段 eg:2分钟、120秒 
timepoint:时间点,时间点是由两部分组成:时间段 + 起始时间。eg:2016年新年夜(解释方式:1970/01/01 + xxx秒。 这是unix和posix系统的计时方式)

命名空间:std::chrono

一个duration对象包含两个部分:一个表示ticks的值,一个秒的单位。

std::ratio可以用来表示小数点后面的部分 
ratio的英文翻译:比率、系数 
用x/y秒来描述时间的单位。eg:60s表示分钟单位;1/1000s表示毫秒单位等

std::chrono::duration<int> d1(20);
std::chrono::duration<double, std::ratio<60>> d2(0.5);
std::chrono::duration<long, std::ratio<1, 1000>> d3(1);
 
 
  
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

分析一下上面的3行,duration是一个类模版,有两个模版参数,第一个表示数值ticks,第二个表示单位(可选,默认是秒)。 
先分析一下第二个参数:std::ratio< 60 > 也有两个参数,第二个参数是可选的,默认为1,第一个参数表示分子,第二个表示分母。结合duration来讲,就可以任意指定秒的单位:60s(一分钟)为单位,0.001s(1毫秒),100s(百秒,这个是自定义的)。正常生活中是年月日 时分秒 毫秒 微妙 纳秒 皮秒(c++11可以支持到纳秒)。

再看看上面的例子:第一个是20秒,第二个是半分钟,第三个是1毫秒。 
实际场景中绝大多数都是时分秒 一直到纳秒。c++11已经考虑到这些单位的常用性,为我们定义了6个常用的单位: 
std::chrono::hours 
std::chrono::minutes 
std::chrono::seconds 
std::chrono::milliseconds 
std::chrono::microseconds 
std::chrono::nanoseconds 
对应上面3个定义,可以用下面的代替

std::chrono::seconds d1(20);
std::chrono::minutes d2(0.5);
std::chrono::milliseconds(1);
 
 
  
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

下面说一下duration的算术操作: 
两个durtion的加减乘除 
durtion和ticks的之间的加减 
比较

这些算术操作需要注意他们的单位。

std::chrono::seconds d1(1); // 1std::chrono::milliseconds d2(1); // 1毫秒
 
 
  
  • 1
  • 2
  • 1
  • 2

d1 - d2: 
结果是999 单位是 毫秒 
如果不用标准库提供的单位:

std::chrono::durtion<int, std::ratio<1, 5>> d1(2); // 2/5std::chrono::durtion<int, std::ratio<1, 3>> d2(1); // 1/3
 
 
  
  • 1
  • 2
  • 1
  • 2

d1 + d2: 
结果是11 单位是1/15秒。15就是3和5的最大公约数

大部分算术操作和比较操作都适用于duration:eg:数值1和duration就没法进行比较操作。

不同的秒单位都可以进行隐式转换

duration的默认构造函数,只指定默认单位为秒,但是ticks值是未定的, 
拷贝构造函数可能发生单位的隐式转换

对于打印duration 标准库并没有提供<<操作 
我们可以重载操作符<<来做到:

template <typename V, typename R>
std::ostream& operator << (std::ostream& s, const std::chrono::duration<V, R>& d)
{
  s << d.count() << " of " << R::num << "/" << R::den;

  return s;
}

// 调用
std::chrono::milliseconds d2(3);
std::cout << d2 << std::endl;
 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

需要注意的是:上面这种写法,如果adl不工作,那么上面的函数也不会工作 
关于adl,以后再说。

这上面说的单位转换都是隐式的,但是有一个问题:向高精度转换是没问题的,向低精度转换有可能会丢失数据。这时可以用显示转换来避免这种情况:

std::chrono::seconds d1(55); // 55std::chrono::minutes d2(d1); // error
std::chrono::minutes d3 = std::chrono::duration_cast<std::chrono::minutes>(d1); // ok
 
 
  
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

使用durtion_cast来进行显示的单位转换。 
下面是一种特殊的情况,来补充上面”向高精度转换是没问题的”这点。 
半分钟(0.5) 转换成30秒,秒单位是从低精度转向了高精度(分钟转秒),但是ticks的值从double转成了int,从高精度转成了低精度,这时也会发生数据丢失,隐式转换会发生错误,需要使用显示转换duration_cast。

显示转换另一个用的比较多的地方:取一个duration的时分秒,是截断一部分信息,常用在打印部分。

std::chrono::milliseconds ms(12345679);
std::chrono::hours hour = duration_cast<std::chrono::hours>(ms % std::chrono::hours(1));
 
 
  
  • 1
  • 2
  • 1
  • 2

利用%(取模操作)可以轻易进行取时取分取秒。

duration还有3个静态成员函数: 
zeor() : 产生一个0秒的duration对象 
max() min() :产生一个duration对象,里面的ticks尽可能是最大最小值。

下面说一下由duration + 起始点epoch =的时间点timepoint: 
起始点epoch可以由clock产生,每个clock产生的起始点都有可能不一样,不过一般都是用1970.0.1.01这个起始点。

clock定义了一个epoch(起点)和一个period(时间段)。 
clock可以计算从1970.01.01到现在的毫秒数,也可以通过now接口产生一个当前时间对象。 
timepoint就是clock +/- 一个duration。

下面先讲一下clock: 
c++标准库定了了3中clock:

system_clock:系统时钟,提供了to_time_t()from_time_t()接口来将timepoint和c系统时间time_t做转换。
steady_clock:不变时钟,并不是使用物理时间的增长来描述的,而是以一个固定的比率来增长。
high_resolution_clock:高精度时钟。
 
 
  
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

为什么一个叫系统时钟一个加不变时钟? 
系统时钟就是电脑右下角的时钟,我们可以任意设置,eg:现在是早上10点,我们可以把系统时钟设置为下午3点,也可以设置9点。不变时钟就是针对系统时钟这种变化性设计出的另一种不可更改的时钟,意思是我们无法去调整时钟的值,只能看着她以一个不变的比率,一直增加。

c++新标准并没有为这三种时钟提供必须要的精度、起始时间、范围。 
如果需要一个自定义的起始时间、或是时钟并不覆盖的一个时间点,这时,需要用到转换函数。

namespace
{

template <typename C>
void print_clock()
{
  std::cout << " - precision: ";

  typedef typename C::period P;
  if ( std::ratio_less_equal<P, std::milli>::value )
  {
    typedef typename std::ratio_multiply<P, std::kilo> T;
    std::cout << std::fixed << double(T::num) / T::den << " milliseconds" << std::endl;
  }
  else
    std::cout << std::fixed << double(P::num) / P::den << " seconds" << std::endl;

  std::cout << " - is_steady: " << std::boolalpha << C::is_steady << std::endl << std::endl;
}

}


void test_cpp2()
{
  std::cout << "----------------- test clock----------------" << std::endl;

  std::cout << " system_clock/high_resolution_clock/steady_clock " << std::endl;
  print_clock<std::chrono::system_clock>();
  print_clock<std::chrono::high_resolution_clock>();
  print_clock<std::chrono::steady_clock>();

}

 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

运行结果是:

----------------- test clock----------------
 system_clock/high_resolution_clock/steady_clock
 - precision: 0.000100 milliseconds
 - is_steady: false

 - precision: 0.000001 milliseconds
 - is_steady: true

 - precision: 0.000001 milliseconds
 - is_steady: true
 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

上面的代码说明 
system_clock 、high_resolution_clock的精度都是100ns,steady_clock的精度是1ms。 
high_resolution_clock、steady_clock的时钟都不可调整 
测试环境:win10 x64 + vs2015,不同的系统检测出的可能有所不同(eg:高精度和系统时钟又可能是一样的)。

steady_clock主要用在处理两个时钟的比较和计算,如果是用system_clock来完成这件事,其中,如果时钟被改变了,那么基于system_clock的计算就会出现错误,同样的,如果计算程序执行时间,如果使用system_clock,那么得出来的结果,也算不了数,甚至执行时间为负。所以说,使用steady_clock来计算两个时钟的比较和计算是比较合适的。

上面已经说过了duration和clock,那么基于duration和clock的timepoint在下面开始分析:

namespace std { namespace chrono {

template <typename Clock,
                  typename Durtion = typename Clock:Durtion>
class time_point;               

}}
 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

说到时间点,有4个比较特殊的: 
epoch:可由任意时钟的time_point默认构造产生 
current time:由任意时钟的静态成员函数now()产生 
minimum timepoint:有任意时钟的time_point静态成员函数min()产生 
maximum timepoint:有任意时钟的time_point静态成员函数max()产生

下面两种写法是一样的意思:

std::chrono::system_clock::time_point t;
std::chrono::time_point<std::chrono::system_clock> t;
 
 
  
  • 1
  • 2
  • 1
  • 2

time_point 对象只有一个成员 duration,这个值可以被成员函数time_since_epoch()获取到,同时,time_point对象之间的比较或是和duration之间的算术操作,类time_point都有提供。

只有当time_point 和 durtion组合起来,才能表达出更丰富的时间点,但这其中还需要考虑很多问题:秒单位转换时的截取和四舍五入;闰年和闰秒等等。

总结:chrono描述的更多的是chrono和duration而不是日期时间库。

以上是c++新标准库对时钟和时间的描述,c标准和posix的接口,在c++程序中也是可用的,下面介绍一下time.h(在c++可写成ctime)。 命名空间是std

CLOCKS_PER_SEC : 表示1s内有多少个ticks,1个嘀嗒(ticks)占的时间是1/CLOCKS_PER_SEC。

ctime() 将一个time_t转换成一个日期字符串, mktime()将struct tm转换成time_t 
而c++标准则提供了frome_time_t(),to_time_t() 来将time_point和time_t进行转换

接下来看看duration和timepoint在定时器和阻塞方面的知识: 
阻塞线程:由this_thread提供的sleep_for和sleep_until 
等待互斥量:try_lock_fro和try_lock_unitl 
等待条件变量:wait_for和wait_until 
所有以_for结尾的接口都是利用duration来实现阻塞;所有以_until都是利用timepoint来实现阻塞。

这里有一个需要注意的地方:以_for结尾的接口用的是duration时间段来处理的,和时钟无关;而以_until结尾的接口用的是time_point来处理,这里如果将时钟修改了,那么会影响到执行的逻辑。eg:现在是下午3点,sleep_unitl(下午4点),如果此时将时间改为下午4点,那么定时器会立马结束。

但计算机的事情并没有绝对,例如上面说的_for不会收到时钟调整的影响,但是如果硬件平台不提供steady_clock,那么软件就没办法在保证计时不会被影响,所以,在这种情况下,_for接口也会被时钟调整所影响。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/gxb0505/article/details/71514073

智能推荐

RichEdit控件 SDK 参考手册-程序员宅基地

RichEdit控件 SDK 参考手册 摘要: 本文对Rich Edit控件底层消息机制进行了讲解,以期读者对Windows平台下的Rich Edit控件有一个更深入的认识,同时对于使用Win32 SDK进行开发的人员具有一定参考价值。因为文章的初衷是引领VB程序员通过Win32 API调用来扩展VB下的Rich Edit控件的功能,所以对于每个消息的详细说明和注意事项未作过多说明,感兴趣

python中列表实现去重使用_Python对list列表结构中的值进行去重的方法总结-程序员宅基地

今天遇到一个问题,在同事随意的提示下,用了 itertools.groupby 这个函数。不过这个东西最终还是没用上。问题就是对一个list中的新闻id进行去重,去重之后要保证顺序不变。直观方法最简单的思路就是:ids = [1,2,3,3,4,2,3,4,5,6,1]news_ids = []for id in ids:if id not in news_ids:news_ids.append(...

java 调用webservice方法_Java调用WebService接口的方法_薛颠的博客-程序员宅基地

本文实例讲述了Java调用WebService接口的方法。分享给大家供大家参考。具体如下:这里讲述有参方法Add,代码如下:复制代码 代码如下:public static void addTest() {try ...{Integer i = 1;Integer j = 2;//WebService URLString service_url = "http://localhost:4079/ws..._service service = new service(); // 创建调用 call = (call) service.createcal

三层架构创建项目Javaweb实战-程序员宅基地

首先创建web项目,调整Tomcat,创建lib导入需要的jar包2.三层架构分别是web层,业务逻辑层,和数据访问层,分别对应web文件夹,service,dao文件夹src下创建三层架构文件夹,分别存储不同的文件domain:创建需要的类util:存储需要的工具类dao:存储与数据库交互的接口和实现类web:存储servletservice:存储service的接口和实现类...

使用AngularJS导出/下载excel文件_angularjs下载excel-程序员宅基地

通常下载一个文件用window.location.href = "接口内容"就能实现下载一个文件的需求。但是如果遇到一些特殊的需求,比如说需要在请求头重加一些属性和值,这样window.location.href就不能满足了。但是可以用angularJS自带的$http来请求。 $http({ url: '你的接口内容', metho..._angularjs下载excel

NYOJ 311 完全背包(完全背包)-程序员宅基地

完全背包时间限制:3000 ms | 内存限制:65535 KB难度:4描述直接说题意,完全背包定义有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的体积是c,价值是w。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。本题要求是背包恰好装满背包时,求出最大价值总和是多少。如果不能恰好装满背包,输出NO

随便推点

layui 弹框里下拉框选项多时的双滚动条问题解决_layui下拉菜单加载滚动效果-程序员宅基地

如图为layui写的弹框,里面的下拉框选项多的时候会出现滚动条,外侧也会出现滚动条,而且下拉框选项显示不全:html:<form class="layui-form layui-form-pane" id="saveSceneModal" style="display: none;padding: 40px 0 0 40px;"> <div class="layui-form-item"> <label class="layui-f.._layui下拉菜单加载滚动效果

java从url下载文件_Java从URL下载文件_胖猫读历史的博客-程序员宅基地

在这篇文章中将学习如何从java下载URL中的文件。使用java.net.URL openStream()方法从java程序中的URL下载文件。也可以使用Java NIO Channels或Java IO InputStream从URL打开流中读取数据,然后将它保存到文件中。下面是从指定URL下载的简单Java程序。它演示了如何在java中从指定URL下载文件的两种方法。import java.i..._java下载url文件

matlab BUG合集-程序员宅基地

matlab BUG合集1. 读取数据的时候出现文件不存在虽然matlabBUG很好解决,但是我粗心啊,总是出错也挺麻烦,真的不适合编程,为了节约时间还是要记录一下1. 读取数据的时候出现文件不存在明明路径、文件名都写好了,出现问题后来发现虽然路径写好了,但是读取的时候并没有加入路径,所以程序还是找不到这个文件在哪,所以不存在...

python订单管理系统功能_后台系统:订单管理_weixin_39600823的博客-程序员宅基地

订单管理记录了所有的交易数据,在后台系统设计工作中是非常重要的一环。订单管理是后台系统中较为重要的一部分,它记录了所有的交易数据,可以对订单进行监控和操作,与用户、运营、财务等都有着密切的关系。以下就来总结一下后台系统中订单管理的设计。一般来说,订单管理后台的操作用户都是公司内部人员,但需要支持的实际上还有C端用户的需求。所以在设计时,订单管理系统需要包括两部分的内容:一是要能够与C端用户在整个订..._订单跟踪python

Request对象_javazrequest对象是干嘛的-程序员宅基地

Request对象的主要功能是是服务器获取从客户端浏览器提交或者上传的信息,它可以访问任何网页请求所传递的信息,可以用Post或Get方法进行数据的传递。Request封装了客户端的请求信息。1.Request获取QueryString的值,用QueryString来获得从上一个页面传递来的字符串参数。&lt;a href ="Page2.aspx?ID=6 &amp; Name=Wang"&..._javazrequest对象是干嘛的

[ios]socket通信 服务端,客户端 【转】_asyncudp.h库怎么下载-程序员宅基地

CocoaAsyncSocket支持tcp和udp。其中:AsyncSocket类是支持TCP的AsyncUdpSocket是支持UDP的AsyncSocket是封装了CFSocket和CFSteam的TCP/IP socket网络库。它提供了异步操作,本地cocoa类的基于delegate的完整支持。主要有以下特性:队列的非阻塞的读和写,而且可选超时。你可以调用它读取_asyncudp.h库怎么下载