当前位置:首页 > 单片机 > CPP开发者
[导读]↓推荐关注↓日期和时间是编程中非常常用的功能。本文是对C11到C17中相关编程接口的介绍。介绍C中可以使用的日期时间API主要分为两类:C-style日期时间库,位于头文件中。这是原先头文件的C版本。chrono库:C11中新增API,增加了时间点,时长和时钟等相关接口。在C11...

推荐关注↓

日期和时间是编程中非常常用的功能。本文是对C 11到C 17中相关编程接口的介绍。

介绍

C 中可以使用的日期时间API主要分为两类:

  • C-style 日期时间库,位于头文件中。这是原先头文件的C 版本。
  • chrono库:C 11中新增API,增加了时间点,时长和时钟等相关接口。
在C 11之前,C 编程只能使用C-style日期时间库。其精度只有秒级别,这对于有高精度要求的程序来说,是不够的。

但这个问题在C 11中得到了解决,C 11中不仅扩展了对于精度的要求,也为不同系统的时间要求提供了支持。

另一方面,对于只能使用C-style日期时间库的程序来说,C 17中也增加了timespec将精度提升到了纳秒级别。

代码示例

本文中所贴出的代码示例可以到我的Github上获取:paulQuei/cpp-date-time[1]

或者,你也可以直接通过下面这条命令获取所有源码:

git clone https://github.com/paulQuei/cpp-date-time.git
为了简化书写,本文中给出的代码都已经默认做了以下操作:

#include 
#include 
#include 

using namespace std;

C-style 日期时间库

C-style 日期时间库中包含的函数和数据类型说明如下:

函数

函数说明
std::clock_t clock()返回自程序启动时起的处理器时钟时间
std::time_t time(std::time_t* arg)返回自纪元起计的系统当前时间
double difftime(std::time_t time_end,   std::time_t time_beg)计算时间之间的差
int timespec_get(std::timespec* ts, int base)∗∗返回基于给定时间基底的日历时间
char* ctime(const std::time_t* time)转换 time_t 对象为文本表示
char* asctime(const std::tm* time_ptr)转换 tm 对象为文本表示
std::size_t strftime(char* str,   std::size_t count, const char* format,   const std::tm* time)转换 tm 对象到自定义的文本表示
std::size_t wcsftime( wchar_t* str,   std::size_t count, const wchar_t* format,   const std::tm* time)转换 tm 对象为定制的宽字符串文本表示
std::tm* gmtime(const std::time_t* time)将time_t转换成UTC表示的时间
std::tm* localtime(const std::time_t *time)将time_t转换成本地时间
std::time_t mktime(std::tm* time)将tm格式的时间转换成time_t表示的时间

数据类型

名称说明
time_t从纪元起的时间类型
tm日历时间类型
timespec∗∗以秒和纳秒表示的时间
clock_t进程运行时间
size_tsizeof 运算符返回的无符号整数类型

结构梳理

这里有不少的函数和数据类型,刚开始接触的时候似乎不太容易记得住。

但实际上,如果我们把它们画成一张图就比较好理解了,如下所示:

img
在这幅图中,以数据类型为中心,带方向的实线箭头表示该函数能返回相应类型的结果。

  • clock函数是相对独立的一个函数,它返回进程运行的时间,具体描述见下文。
  • time_t描述了纪元时间,通过time函数可以获得它。但它只能精确到秒级别。
  • timespec类型在time_t的基础上,增加了纳秒的精度,通过timespec_get获取。这是C 17上新增的
  • tm是日历类型,因为它其中包含了年月日等信息。通过gmtime,localtime和mktime函数可以将time_t和tm类型互相转换。
  • 考虑到时区的差异,因此存在gmtime和localtime两个函数。
  • 无论是time_t还是tm结构,都可以将其以字符串格式输出。ctime和asctime输出的格式是固定的。如果需要自定义格式,需要使用strftime或者wcsftime函数。

进程运行时间

clock函数返回进程迄今为止所用的处理器时间。单独调度该函数一次所返回的值是没有意义的,只有两次不同值的差才有意义。

该值表示了进程从关联到程序执行的实现定义时期开始,所用的粗略处理器时间。而且这个值仅仅是处理器的时钟周期。如果希望将其转换为以秒为单位,还需要将它除以常量 CLOCKS_PER_SEC

下面是一段代码示例:

clock_t time1 = clock();
double sum = 0;
for(int i = 0; i < 100000000; i ) {
  sum  = sqrt(i);
}
clock_t time2 = clock();

double t = ((double)(time2 - time1)) / CLOCKS_PER_SEC ;
cout << "CLOCKS_PER_SEC: " << CLOCKS_PER_SEC << endl;
cout << "Process running time: " << t << "s" << endl;
其输出如下:

CLOCKS_PER_SEC: 1000000
Process running time: 0.80067s
你可能知道,现代的操作系统上进程都是分时占用处理器的,所以程序的处理器时间会小于真实世界流逝的时间。但这仅仅是对于单处理器而言的。在多处理器系统上,如果你的进程使用了多线程,那么其所用的处理器时间可能比真实世界流逝的时间值还要大。

关于纪元时间

纪元时间(Epoch time)又叫做Unix时间或者POSIX时间。它表示自1970 年 1 月 1 日 00:00 UTC 以来所经过的秒数(不考虑闰秒)。它在操作系统和文件格式中被广泛使用。

这个想法很简单:以一个时间为起点加上一个偏移量便可以表达任何一个其他的时间。

如果你好奇为什么选这个时间作为起点,可以点击这里:Why is 1/1/1970 the “epoch time”?[2]

下面是一个代码示例:

time_t epoch_time = time(nullptr);
cout << "Epoch time: " << epoch_time << endl;
其输出如下:

Epoch time: 1577433897
time函数接受一个指针,指向要存储时间的对象,通常可以传递一个空指针,然后通过返回值来接受结果。

虽然标准中没有给出定义,但time_t通常使用整形值来实现。

作为一个程序员,你可能马上会意识到整形的位数和溢出的问题。事实也刚好是这样,在一些历史实现上使用了32位有符号整数来实现time_t,其造成的结果就是:在2038-01-19 03:14:07[3]这个时间点,这个值会溢出。

不过不用担心太多,这个时间距现在还有将近20年,到那个时候,估计那些有问题的系统已经不会再继续运转或者已经被升级了。

计算时间差

在一些情况下,我们需要计算一个操作的时间长度。这自然的就需要计算两个时间点的差分。这时就可以使用difftime函数。

事实上,我们知道time_t以秒级别表示纪元时间,并且它又是以整形实现的,直接将两个time_t相减,可以得到相同的结果。

下面是一个代码示例:

time_t time1 = time(nullptr);
double sum = 0;
for(int i = 0; i < 1000000000; i ) {
  sum  = sqrt(i);
}
time_t time2 = time(nullptr);

double time_diff = difftime(time2, time1);
cout << "time1: " << time1 << endl;
cout << "time2: " << time2 << endl;
cout << "time_diff: " << time_diff << "s" << endl;
其输出如下,可以看到这正是time1和time2两个整数相减的结果:

time1: 1577434406
time2: 1577434414
time_diff: 8s
注意:time_t只精确到秒,它无法描述毫秒级别的时间,所以在有更高精度要求的情况下,需要使用下文提到的其他方法。

输出时间和日期

当然,我们还希望将时间以字符串的形式打印出来。这时就可以使用ctime函数。不过该函数打印的格式是固定的:Www Mmm dd hh:mm:ss yyyy\n。如果你希望自定义输出的格式,可以使用下文提到的其他方法。

下面是一个代码示例:

time_t now = time(nullptr);
cout << "Now is: " << ctime(
本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭