你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

C/C++时间相关的那些函数

[复制链接]
gaosmile 发布时间:2020-10-6 22:04
大家平时工作,如果有计算函数耗时或者打印当前时间的需求,一定要来看看这篇文章!
首先介绍下C++标准中的chrono库
chrono是一个关于时间的库,起源于boost,现在是C++的标准,话说现在的C++标准好多都是源于boost,要进标准的特性似乎都会先在boost试验一番。
首先看一下使用chrono简单计时的示例代码:

  1. void func() {  // 计时
  2.    std::chrono::time_point<std::chrono::high_resolution_clock> begin = high_resolution_clock::now();
  3.    std::this_thread::sleep_for(std::chrono::milliseconds(20));
  4.    auto end = high_resolution_clock::now();
  5.    cout << "time " << duration_cast<milliseconds>(end - begin).count() << endl;
  6. }
复制代码

chrono中有三个概念duration、time_point、clock
duration:表示一段时间,三分钟、三秒等,它的定义如下:
  • template <class _Rep, class _Period = ratio<1>> class duration;
ratio的定义如下:
  • template <intmax_t N, intmax_t D = 1> class ratio;
Rep表示数据类型,int,long等,Period表示时间单位,N是分子,D是分母,直接看例子吧:

  1. using atto  = ratio<1, 1000000000000000000LL>;
  2. using femto = ratio<1, 1000000000000000LL>;
  3. using pico  = ratio<1, 1000000000000LL>;
  4. using nano  = ratio<1, 1000000000>;
  5. using micro = ratio<1, 1000000>;
  6. using milli = ratio<1, 1000>;
  7. using centi = ratio<1, 100>;
  8. using deci  = ratio<1, 10>;
  9. using deca  = ratio<10, 1>;
  10. using hecto = ratio<100, 1>;
  11. using kilo  = ratio<1000, 1>;
  12. using mega  = ratio<1000000, 1>;
  13. using giga  = ratio<1000000000, 1>;
  14. using tera  = ratio<1000000000000LL, 1>;
  15. using peta  = ratio<1000000000000000LL, 1>;
  16. using exa   = ratio<1000000000000000000LL, 1>;

  17. using nanoseconds  = duration<long long, nano>;
  18. using microseconds = duration<long long, micro>;
  19. using milliseconds = duration<long long, milli>;
  20. using seconds      = duration<long long>;
  21. using minutes      = duration<int, ratio<60>>;
  22. using hours        = duration<int, ratio<3600>>;

  23. using hours2       = duration<int, ratio<3600, 1>>;
  24. using hours2       = duration<int, ratio<7200, 2>>;
复制代码

详细看完上述例子您也明白了,ratio的默认的时间单位是1秒,以小时为例,一小时等于3600秒,3600 / 1 == 7200 / 2 == 3600,所以hours == hours2 == hours3。
标准库还提供了duration_cast用于转换各种duration。

  1. template <class _To, class _Rep, class _Period, enable_if_t<_Is_duration_v<_To>, int> = 0>
  2.    constexpr _To duration_cast(const duration<_Rep, _Period>&) noexcept(
  3.    is_arithmetic_v<_Rep>&& is_arithmetic_v<typename _To::rep>);

  4. template <class _Ty>
  5.    _INLINE_VAR constexpr bool _Is_duration_v = _Is_specialization_v<_Ty, duration>;

  6. template <class _Ty>
  7. _INLINE_VAR constexpr bool is_arithmetic_v = // determine whether _Ty is an arithmetic type
  8.    is_integral_v<_Ty> || is_floating_point_v<_Ty>;
复制代码

函数看着很繁琐,直接看看示例代码吧:

  1. void func() {
  2.    auto sec = std::chrono::seconds(10);
  3.    auto mill = std::chrono::duration_cast<std::chrono::milliseconds>(sec);
  4.    cout << sec.count() << endl; // 返回多少s
  5.    cout << mill.count() << endl; // 返回多少ms
  6. }
  7. 输出:
  8. 10
  9. 10000
复制代码

time_point:用来表示某个具体时间点。
定义如下:

  1. template <class _Clock, class _Duration = typename _Clock::duration>
  2.    class time_point;
复制代码

使用方式如下:

  1. void func() {
  2.    std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds> tp(std::chrono::seconds(12));
  3.    cout << tp.time_since_epoch().count() << endl;
  4.    std::time_t t = system_clock::to_time_t(tp);
  5.    cout << "time " << ctime(&t) << endl;
  6. }
  7. 输出:
  8. 12000
  9. time Thu Jan  1 08:00:12 1970
复制代码

这里有个函数time_since_epoch(),表示这个time_point距离元年也就是1970年1月1日所经过的duration。
time_point也有各种表示方式,类似于duration,也提供了转换函数time_point_cast()。

  1. void func() {
  2.     time_point<system_clock, milliseconds> tp(seconds(12));
  3.     cout << tp.time_since_epoch().count() << endl;
  4.     time_point<system_clock, seconds> tp2 = time_point_cast<seconds>(tp);
  5.     cout << tp2.time_since_epoch().count() << endl;
  6. }
  7. 输出:
  8. 12000
  9. 12
复制代码

Clocks:
这里的时钟大体有三种:
system_clock
steady_clock
high_resolution_clock
system_clock表示当前的系统时钟,有三个函数:

  1. now():表示当前时间的time_point
  2. to_time_t():将time_point转换成time_t秒
  3. from_time_t():将time_t转换成time_point
复制代码

源码如下:

  1. struct system_clock { // wraps GetSystemTimePreciseAsFileTime/GetSystemTimeAsFileTime
  2.     using rep = long long;

  3.     using period = ratio_multiply<ratio<_XTIME_NSECS_PER_TICK, 1>, nano>;

  4.     using duration                  = chrono::duration<rep, period>;
  5.     using time_point                = chrono::time_point<system_clock>;
  6.     static constexpr bool is_steady = false;

  7.     _NODISCARD static time_point now() noexcept { // get current time
  8.         return time_point(duration(_Xtime_get_ticks()));
  9.     }

  10.     _NODISCARD static __time64_t to_time_t(const time_point& _Time) noexcept { // convert to __time64_t
  11.         return static_cast<__time64_t>(_Time.time_since_epoch().count() / _XTIME_TICKS_PER_TIME_T);
  12.     }

  13.     _NODISCARD static time_point from_time_t(__time64_t _Tm) noexcept { // convert from __time64_t
  14.         return time_point(duration(_Tm * _XTIME_TICKS_PER_TIME_T));
  15.     }
  16. };
复制代码

steady_clock表示稳定的时钟,它只有一个函数,就是now(),后一次调用now()肯定比上一次调用now()的返回值大,不受系统时间修改的影响。
源码如下:

  1. struct steady_clock { // wraps QueryPerformanceCounter
  2.     using rep                       = long long;
  3.     using period                    = nano;
  4.     using duration                  = nanoseconds;
  5.     using time_point                = chrono::time_point<steady_clock>;
  6.     static constexpr bool is_steady = true;

  7.     _NODISCARD static time_point now() noexcept { // get current time
  8.         const long long _Freq = _Query_perf_frequency(); // doesn't change after system boot
  9.         const long long _Ctr  = _Query_perf_counter();
  10.         static_assert(period::num == 1, "This assumes period::num == 1.");
  11.         const long long _Whole = (_Ctr / _Freq) * period::den;
  12.         const long long _Part  = (_Ctr % _Freq) * period::den / _Freq;
  13.         return time_point(duration(_Whole + _Part));
  14.     }
  15. };
复制代码

使用方式和之前的都相同:

  1. void func() {  // 计时
  2.     std::chrono::time_point<std::chrono::steady_clock> begin = steady_clock::now();
  3.     std::this_thread::sleep_for(std::chrono::milliseconds(20));
  4.     auto end = steady_clock::now();
  5.     cout << "time " << duration_cast<milliseconds>(end - begin).count() << endl;
  6. }
复制代码

high_resolution_clock表示高精度时钟,是系统可用的最高精度的时钟,它其实就是system_clock或者steady_clock的别名:
  • using high_resolution_clock = steady_clock;
介绍完了C++的chrono那下面再看下C语言的各种时间相关的API吧:
首先可以通过C语言的clock拿到程序执行时处理器所使用的时钟数来计时:
  • clock_t clock(void);
该函数返回程序执行起(一般为程序的开头),处理器时钟所使用的时间。也获取 CPU 所使用的秒数,除以 CLOCKS_PER_SEC即可,返回的clock_t其实就是long类型的重命名。
使用方式如下:

  1. void func() {
  2.     clock_t start_t = clock();
  3.     cout << start_t << " 个时钟 \n";
  4.     for (int i = 0; i < 100000000; i++) {
  5.     }
  6.     clock_t end_t = clock();
  7.     cout << end_t << " 个时钟 \n";
  8.     cout << "循环的秒数:" << (double)(end_t - start_t) / CLOCKS_PER_SEC << endl;
  9. }
复制代码

如何获取当前时间戳,单位为秒

  1. void func() {  // 获取当前时间戳,单位为秒
  2.     struct timeval time;
  3.     gettimeofday(&time, NULL);
  4.     cout << time.tv_sec << " s \n";
  5. }
复制代码

也可以使用time函数:
  • time_t time(time_t *time);
该函数返回系统的当前日历时间,返回的是自1970年1月1日以来所经过的秒数。
time_t其实就是一个整数类型,是int64_t的重命名,该函数直接使用返回值就好,参数一般传空即可。
timer 存取结果的时间指针变量,类型为time_t,指针变量可以为null。
如果timer指针非null,则time()函数返回值变量与timer指针一样,都指向同一个内存地址;
否则如果timer指针为null,则time()函数返回一个time_t变量时间。

  1. void func() {  // 获取当前时间戳,单位为秒
  2.     time_t now = time(NULL);
  3.     cout << static_cast<int64_t>(now) << " s \n";
  4. }
复制代码

如何获取当前时间戳?单位为毫秒

  1. void func() {  // 获取当前时间戳,单位为毫秒
  2.     struct timeval time;
  3.     gettimeofday(&time, NULL);
  4.     cout << time.tv_sec * 1000 + time.tv_usec / 1000 << " ms \n";
  5. }
复制代码

如何显示当前的系统时间呢?可以使用ctime显示当前时间:
  • char* ctime(const time_t* time);
该函数返回一个表示当地时间的字符串指针,输出内容格式如下:
  • day month year hours:minutes:seconds year\n\0。
示例代码如下:

  1. void func() {
  2.     time_t now = time(NULL);
  3.     char* dt = ctime(&now);
  4.     cout << "cur time is: " << dt;
  5. }
  6. 输出:
  7. Tue Sep 22 22:01:40 2020
复制代码

可以使用tm结构自定义显示当前时间的格式:
  • struct tm * localtime(const time_t * timer);
将日历时间转换为本地时间,从1970年起始的时间戳转换为1900年起始的时间数据结构
另一个类似的函数是gmtime函数:
  • struct tm *gmtime(const time_t *time);
只是该函数返回的是UTC时间,协调世界时(UTC)也被称为格林尼治标准时间(GMT)。
tm结构如下:

  1. struct tm {
  2.   int tm_sec;   // 秒,正常范围从 0 到 59,但允许至 61
  3.   int tm_min;   // 分,范围从 0 到 59
  4.   int tm_hour;  // 小时,范围从 0 到 23
  5.   int tm_mday;  // 一月中的第几天,范围从 1 到 31
  6.   int tm_mon;   // 月,范围从 0 到 11
  7.   int tm_year;  // 自 1900 年起的年数
  8.   int tm_wday;  // 一周中的第几天,范围从 0 到 6,从星期日算起
  9.   int tm_yday;  // 一年中的第几天,范围从 0 到 365,从 1 月 1 日算起
  10.   int tm_isdst; // 夏令时
  11. };
复制代码

tm_sec 在C89的范围是[0-61],在C99更正为[0-60]。通常范围是[0-59],貌似有些系统会出现60秒的跳跃。
tm_mon 是从零开始的,所以一月份为0,十二月份为11。
tm_year是从1900年开始计算,所以显示年份的时候需要加上1900

  1. void func() {
  2.     time_t rawtime = time(NULL);
  3.     struct tm* ptminfo = localtime(&rawtime);
  4.     printf("cur time is: %02d-%02d-%02d %02d:%02d:%02d\n", ptminfo->tm_year + 1900, ptminfo->tm_mon + 1,
  5.            ptminfo->tm_mday, ptminfo->tm_hour, ptminfo->tm_min, ptminfo->tm_sec);
  6.     ptminfo = gmtime(&rawtime);
  7.     printf("cur time is: %02d-%02d-%02d %02d:%02d:%02d\n", ptminfo->tm_year + 1900, ptminfo->tm_mon + 1,
  8.            ptminfo->tm_mday, ptminfo->tm_hour, ptminfo->tm_min, ptminfo->tm_sec);
  9. }
  10. 输出:
  11. cur time is: 2020-09-23 21:27:37
  12. cur time is: 2020-09-23 13:27:37
复制代码

可以通过asctime显示tm结构的时间:
  • char * asctime ( const struct tm * time );
和ctime类似,返回的都是一个固定时间格式的字符串,只是传入的参数不同。

  1. void func() {
  2.     time_t rawtime = time(NULL);
  3.     struct tm* info1 = localtime(&rawtime);
  4.     cout << "正常 日期和时间:" << asctime(info1) << endl;
  5.     info1 = gmtime(&rawtime);
  6.     cout << "UTC 日期和时间:" << asctime(info1) << endl;
  7. }
  8. 输出:
  9. 正常 日期和时间:Wed Sep 23 21:47:44 2020
  10. UTC 日期和时间:Wed Sep 23 13:47:44 2020
复制代码

也可以使用strftime()函数,该函数可用于格式化日期和时间为指定的格式,如果产生的 C 字符串小于 size 个字符(包括空结束字符),则会返回复制到 str 中的字符总数(不包括空结束字符),否则返回零。

  1. size_t strftime(
  2.     char *str, // 指向目标数组的指针,用来复制产生的C字符串
  3.     size_t maxsize, // 最多传出字符数量
  4.     const char *format, // 格式化方式
  5.     const struct tm *timeptr // tm指针
  6. );
复制代码

format格式如下:

  1. %a 星期几的缩写
  2. %A 星期几的全称
  3. %b 月份的缩写
  4. %B 月份的全称
  5. %c 标准的日期的时间串
  6. %C 年份的前两位数字
  7. %d 十进制表示的每月的第几天(值从1到31)
  8. %D 月/天/年
  9. %e 在两字符域中,十进制表示的每月的第几天
  10. %F 年-月-日
  11. %g 年份的后两位数字,使用基于周的年
  12. %G 年份,使用基于周的年
  13. %h 简写的月份名
  14. %H 24小时制的小时(值从0到23)
  15. %I 12小时制的小时(值从1到12)
  16. %j 十进制表示的每年的第几天(值从1到366)
  17. %m 十进制表示的月份(值从1到12)
  18. %M 十时制表示的分钟数(值从0到59)
  19. %n 换行符
  20. %p 本地的AM或PM的等价显示
  21. %r 12小时的时间
  22. %R 显示小时和分钟:hh:mm
  23. %S 十进制的秒数(值从0到61)
  24. %t 水平制表符
  25. %T 显示时分秒:hh:mm:ss
  26. %u 每周的第几天,星期一为第一天 (值从1到7,星期一为1)
  27. %U 第年的第几周,把星期日作为第一天(值从0到53)
  28. %V 每年的第几周,使用基于周的年
  29. %w 十进制表示的星期几(值从0到6,星期天为0)
  30. %W 每年的第几周,把星期一做为第一天(值从0到53)
  31. %x 标准的日期串
  32. %X 标准的时间串
  33. %y 不带世纪的十进制年份(值从0到99)
  34. %Y 带世纪部分的十制年份
  35. %Z 时区名称,如果不能得到时区名称则返回空字符。
  36. %% 一个%符号
复制代码

使用代码如下:

  1. void func() {
  2.     time_t rawtime = time(NULL);
  3.     char buf[256];
  4.     strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&rawtime));
  5.     cout << buf << endl;
  6. }
复制代码

收藏 评论0 发布时间:2020-10-6 22:04

举报

0个回答

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版