chrono since C++11 库的参考手册(英文)| cppreference

chrono 库定义了三种(直到c++ 20)五种(从c++ 20开始)主要类型以及实用函数和常用类型:
- cokcks
- time points
- durations
- calendar dates (since C++20)
- time zone information (since C++20)
clocks
时钟由起点(或历元)和滴答率组成。例如,时钟的历元可能是1970年1月1日,并且每秒钟滴答一次。c++定义了几种时钟类型:
| 定义在 |
|
|---|---|
| system_clock | 挂钟时间来自系统的实时时钟 |
| steady_clock | 永远不会被调整的匀速时钟 |
| ... 更多时钟类型见: | https://en.cppreference.com/w/cpp/chrono |
epoch
unix时间戳(Unix timestamp)
UNIX时间戳:Unix时间戳(英文为Unix time, POSIX time 或 Unix timestamp)是从 Epoch(1970年1月1日00:00:00 UTC)开始所经过的秒数,不考虑闰秒。
一个小时表示为UNIX时间戳格式为:3600秒;一天表示为UNIX时间戳为86400秒,闰秒不计算。
参考网址:
chrono since C++11 库的参考手册(英文)| cppreference
time points
时间点(time point)是从某个特定时钟的历元开始的一段时间。
| 定义在 |
|---|
| time_point |
#include <iostream>
#include <chrono>
int main(int argc, char *argv[])
{
auto start = std::chrono::system_clock::now();
std::cout << start << std::endl;
std::cout << start.time_since_epoch() << std::endl;
std::cout
<< std::chrono::duration_cast<std::chrono::seconds>(start.time_since_epoch())
<< std::endl;
std::cout
<< std::chrono::duration_cast<std::chrono::minutes>(start.time_since_epoch())
<< std::endl;
return 0;
}
输出:
2024-11-17 13:33:22.246335500
1731850402246335500ns
1731850402s
28864173min

使用 chrono 最关键的是什么?
- 获取代码(一个循环,一个函数)的运行时间
- 得到当前时间
- 格式化显示时间
- 得到当前时间之后的时间,比如,10秒后,1小时后,一天后,三天后
使用
代码的执行时间
使用 system_clock 计算代码的执行时间:
#include<iostream>
#include<chrono>
#include<thread>
int main(int argc, char *argv[])
{
using namespace std::chrono_literals;
auto s = std::chrono::system_clock::now();
std::this_thread::sleep_for(3s);
auto e = std::chrono::system_clock::now();
auto diff = std::chrono::duration_cast<std::chrono::duration<double>>(e-s);
std::cout << diff.count() << std::endl;
return 0;
}
使用 steady_clock 计算代码的执行时间:
#include <iostream>
#include <chrono>
int main() {
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
// 这里可以放入需要测量执行时间的代码
for (int i = 0; i < 1000000; ++i) {
}
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
std::chrono::duration<double> elapsed_seconds = std::chrono::duration_cast<std::chrono::duration<double>>(end - start);
std::cout << "代码执行时间: " << elapsed_seconds.count() << "秒" << std::endl;
return 0;
}
得到当前时间
using namespace std;
int main(int argc, char **argv)
{
auto now = chrono::system_clock::now(); // 获取当前时间戳
// cout << "Now: " << now << endl; // 不能输出,类型不对
// 将时间戳转换毫秒数
auto now_ms = chrono::time_point_cast<chrono::milliseconds>(now);
auto value = now_ms.time_since_epoch().count();
cout << "Milliseconds since epoch: " << value << endl;
return 0;
}
格式化显示时间
c 风格
#include <iostream>
#include <string>
#include <chrono>
#include <ctime>
using namespace std;
int main(int argc, char *argv[])
{
auto tnow = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
auto t = std::gmtime(&tnow);
char buf[100];
auto result = std::strftime(buf, sizeof buf, "%Y-%m-%d", t);
std::cout << buf << std::endl;
return 0;
}
std::gmtime将始于 epoch 的时间转换为由 Universal Coordinated Time 表示的日历时,返回值为std::tm*std::tm是一个结构体,用于表示时间:
struct tm {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};
有以下的几个问题需要回答:
- t 是一个指针,其资源需要释放吗?
- 在C语言中,
std::gmtime函数返回的struct tm*对象不需要手动释放。该函数将时间戳转换为协调世界时(UTC)表示的时间结构,返回的指针指向一个静态分配的内部缓冲区,由标准库管理内存。每次调用该函数时,这个缓冲区会被覆盖,因此: - 无需释放内存:不要使用
free()或类似函数释放返回的指针,否则会导致未定义行为(如程序崩溃)。 - 数据覆盖风险:后续调用
gmtime()、localtime()等函数可能会覆盖该缓冲区的内容,因此建议立即复制数据到自定义的struct tm变量中。 - 线程不安全:
gmtime()在多线程环境中不安全,因为多个线程可能同时修改同一个静态缓冲区。可使用线程安全的替代函数gmtime_r()(POSIX)或localtime_s()(Windows)。
std::strftime转换由日历时表示的日期、时间信息为 null 终结的字符串,依据格格式化字符串进行转换,最多只有指定数量的字节被写入结果中。std::size_t strftime( char* str, std::size_t count, const char* format, const std::tm* tp );- str 存储转换结果
- count 存储字节的数量
- format 格式化字符串
- tp 日历时
- 返回值:转换的字节数
得到指定的时间点
使用 duration:
#include <iostream>
#include <string>
#include <chrono>
using namespace std;
int main(int argc, char *argv[])
{
auto now = chrono::system_clock::now();
std::chrono::duration<int, std::ratio<1, 1>> d(120);
std::cout << now + d << std::endl; // 两分钟后
return 0;
}
使用 chrono literals:
using namespace std::chrono_literals;
auto now = chrono::system_clock::now();
std::cout << now + 10min << std::endl;
| 操作符 | 解释 | 标准 |
|---|---|---|
| operator""h | a std::chrono::duration literal representing hours | C++14 |
| operator""min | a std::chrono::duration literal representing minutes | C++14 |
| operator""s | a std::chrono::duration literal representing seconds | C++14 |
| operator""ms | a std::chrono::duration literal representing milliseconds | C++14 |
| operator""us | a std::chrono::duration literal representing microseconds | C++14 |
| operator""ns | a std::chrono::duration literal representing nanoseconds | C++14 |
使用 std::duration 表示秒
#include <iostream>
#include <string>
#include <chrono>
using namespace std;
int main(int argc, char *argv[])
{
std::chrono::duration<int, std::ratio<1, 1>> d(30);
//std::cout << d << std::endl;
std::cout << d.count() << std::endl;
return 0;
}
chromo 的实际使用
- 创建一个时钟:
using namespace std;
int main(int argc, char **argv)
{
auto now = chrono::system_clock::now(); // 获取当前时间戳
// cout << "Now: " << now << endl; // 不能输出,类型不对, mingw 不能输出;msvc 可以;
// 将时间戳转换毫秒数
auto now_ms = chrono::time_point_cast<chrono::milliseconds>(now);
auto value = now_ms.time_since_epoch().count();
cout << "Milliseconds since epoch: " << value << endl;
return 0;
}
#include <algorithm>
#include <chrono>
#include <ctime>
#include <iomanip>
#include <iostream>
void slow_motion()
{
static int a[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
// Generate Γ(13) == 12! permutations:
while (std::ranges::next_permutation(a).found) {}
}
int main()
{
using namespace std::literals; // enables literal suffixes, e.g. 24h, 1ms, 1s.
const std::chrono::time_point<std::chrono::system_clock> now =
std::chrono::system_clock::now();
const std::time_t t_c = std::chrono::system_clock::to_time_t(now - 24h);
std::cout << "24 hours ago, the time was "
<< std::put_time(std::localtime(&t_c), "%F %T.\n") << std::flush;
const std::chrono::time_point<std::chrono::steady_clock> start =
std::chrono::steady_clock::now();
std::cout << "Different clocks are not comparable: \n"
" System time: " << now.time_since_epoch() << "\n"
" Steady time: " << start.time_since_epoch() << '\n';
slow_motion();
const auto end = std::chrono::steady_clock::now();
std::cout
<< "Slow calculations took "
<< std::chrono::duration_cast<std::chrono::microseconds>(end - start) << " ≈ "
<< (end - start) / 1ms << "ms ≈ " // almost equivalent form of the above, but
<< (end - start) / 1s << "s.\n"; // using milliseconds and seconds accordingly
}
理解 chrono
duration
表示持续时间。
duration 声明:
template<
class Rep,
class Period = std::ratio<1>
> class duration;
- rep = representing = 表示时间数值的算术类型
秒:
std::duration<int, std::ration<1, 1>> seconds(30); // 30秒
time_point
template<
class Clock,
class Duration = typename Clock::duration
> class time_point;
time_point 表示一个具体的时间,比如 2025-6-25 20:56。它的实现是存储了从 epoch 开始到指定时间的值,是一个整数,然后经过转换后得到这样格式的时间 2025-6-25 20:56。
- clock = 时钟类型
- duration = 持续时间类型
- representing = 表示,一个用于表示时间数值的算术类型
- period = 周期,比如一分钟多少秒,这时一分钟就是一个周期,同样的,一小时、一天以及一个月等都是一个周期。
period
秒是周期,分钟是周期,小时是周期,那么最基础的单位是什么?单位是人规定的,可以有很多个,只是具体数值来根据测量精度来定。如果测量精度是秒,那么没有更细分的数值,但是单位有。在时间里,以秒周期来说,就是一个数字有多少个秒周期,多余的数值并不会丢失,只是不能转换为秒(不够一秒)。
msvc stl 实现:
_EXPORT_STD using nanoseconds = duration<long long, nano>;
_EXPORT_STD using microseconds = duration<long long, micro>;
_EXPORT_STD using milliseconds = duration<long long, milli>;
_EXPORT_STD using seconds = duration<long long>;
_EXPORT_STD using minutes = duration<int, ratio<60>>;
_EXPORT_STD using hours = duration<int, ratio<3600>>;
#if _HAS_CXX20
_EXPORT_STD using days = duration<int, ratio_multiply<ratio<24>, hours::period>>;
_EXPORT_STD using weeks = duration<int, ratio_multiply<ratio<7>, days::period>>;
_EXPORT_STD using years = duration<int, ratio_multiply<ratio<146097, 400>, days::period>>;
_EXPORT_STD using months = duration<int, ratio_divide<years::period, ratio<12>>>;
ratio
msvc stl 实现:
_EXPORT_STD template <intmax_t _Nx, intmax_t _Dx = 1>
struct ratio { // holds the ratio of _Nx to _Dx
static_assert(_Dx != 0, "zero denominator");
static_assert(-INTMAX_MAX <= _Nx, "numerator too negative");
static_assert(-INTMAX_MAX <= _Dx, "denominator too negative");
static constexpr intmax_t num = _Sign_of(_Nx) * _Sign_of(_Dx) * _Abs(_Nx) / _Gcd(_Nx, _Dx);
static constexpr intmax_t den = _Abs(_Dx) / _Gcd(_Nx, _Dx);
using type = ratio<num, den>;
};
- ratio = 比例,得到最简分式
- numerator,num = 分子
- denominator,den = 分母
_Sing_of得到符号(1 或 -1)_Abs得到绝值
比例:
_EXPORT_STD template <class _Rx1, class _Rx2>
_INLINE_VAR constexpr bool ratio_greater_equal_v = ratio_greater_equal<_Rx1, _Rx2>::value;
_EXPORT_STD using atto = ratio<1, 1000000000000000000LL>;
_EXPORT_STD using femto = ratio<1, 1000000000000000LL>;
_EXPORT_STD using pico = ratio<1, 1000000000000LL>;
_EXPORT_STD using nano = ratio<1, 1000000000>;
_EXPORT_STD using micro = ratio<1, 1000000>;
_EXPORT_STD using milli = ratio<1, 1000>;
_EXPORT_STD using centi = ratio<1, 100>;
_EXPORT_STD using deci = ratio<1, 10>;
_EXPORT_STD using deca = ratio<10, 1>;
_EXPORT_STD using hecto = ratio<100, 1>;
_EXPORT_STD using kilo = ratio<1000, 1>;
_EXPORT_STD using mega = ratio<1000000, 1>;
_EXPORT_STD using giga = ratio<1000000000, 1>;
_EXPORT_STD using tera = ratio<1000000000000LL, 1>;
_EXPORT_STD using peta = ratio<1000000000000000LL, 1>;
_EXPORT_STD using exa = ratio<1000000000000000000LL, 1>;
浙公网安备 33010602011771号