C++日期和时间编程总结


  • Why is 1/1/1970 the “epoch time”?。

    通过 time 函数获取当前时刻的纪元时间示例代码如下:

    time_t epoch_time = time(nullptr);
    cout << "Epoch time: " << epoch_time << endl;
    // Epoch time: 1660039180 (日历时间: Tue Aug  9 17:59:40 2022)
    

    time 函数接受一个指针,指向要存储时间的对象,通常可以传递一个空指针,然后通过返回值来接受结果。虽然标准中没有给出定义,但time_t 通常使用整形值来实现。

    2.5,输出时间和日期

    使用 ctime 函数,可以将时间以固定格式的字符串的形式打印出来,格式为:Www Mmm dd hh:mm:ss yyyy\n。代码示例如下:

    // 以字符串形式输出当前时间和日期
    time_t now = time(nullptr);
    cout << "Now is: " << ctime(&now);
    // Now is: Tue Aug  9 18:06:38 2022
    

    2.6,综合示例代码

    asctime()difftime() 函数等sample 代码如下(复制可直接运行):

    /* asctime example */
    #include       /* printf */
    #include        /* time_t, struct tm, time, localtime, asctime */
    #include 
    #include 
    
    using namespace std;
    
    // 冒泡排序: 将数据从小到大排序
    void bubbleSort(vector &arr){
        size_t number = arr.size();
        if (number <= 1) return;
        int temp;
        for(int i = 0; i < number; i++){
            for(int j = 0; j < number-i; j++){
                if (temp > arr[j+1]){
                    temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
    }
    
    // difftime() 函数: 计算时间差,单位为 s
    void difftime_test()
    {
        vector input_array;
        for (int i = 90000; i > 0; i--) {
            input_array.emplace_back(i);
        }
        time_t time1 = time(nullptr);
        bubbleSort(input_array);
        time_t time2 = time(nullptr);
        double time_diff = difftime(time2, time1);
        cout << "input array size is " << input_array.size() << " after bubbleSort time_diff: " << time_diff << "s" << endl;
    }
    
    // astime() 函数: 将本地时间 tm 结构体对象转换为字符串文本
    void astime_test()
    {
        time_t raw_time = time(nullptr);  // 获取当前时刻日历时间
        struct tm* local_timeinfo = localtime(&raw_time);
        printf ( "The current date/time is: %s", asctime (local_timeinfo) );
    }
    
    int main()
    {
        difftime_test();
        astime_test();
        // 3, 输出当前纪元时间
        time_t epoch_time = time(nullptr);
        cout << "Epoch time: " << epoch_time << endl;
        // 4,以字符串形式输出当前时间和日期
        time_t now = time(nullptr);
        cout << "Now is: " << ctime(&now);
    }
    

    g++ time_demo.cpp -std=c++11 编译后,运行程序 ./a.out 后,输出结果:

    三,chrono 库

    “chrono” 是英文 chronology 的缩写,其含义是“年表;年代学”。

    chrono 既是头文件名字也是子命名空间的名字,chrono 头文件下的所有 elements 都是在 std::chrono 命名空间下定义的。

    std::chrono 是 C++11 引入的日期时间处理库,chrono 库里包括三种主要类型:ClocksTime pointsDurations

    3.1,时钟

    C++11 chrono 库中包含了三种的时钟类:

    名称 说明
    chrono::system_clock 系统时钟(可以调整)
    chrono::steady_clock 单调递增时钟(不能调整)
    chrono::high_resolution_clock 拥有可用的最短嘀嗒周期的时钟

    system_clock 是当前所在系统的时钟。因为系统时钟随时都可能被调整,所以如果想要计算两个时间点的时间差,是不推荐使用系统时钟的。

    steady_clock 会保证时间的单调递增性,只会向前移动不会减少,所以最适合用来度量时间间隔

    high_resolution_clock 表示实现提供的拥有最小计次周期的时钟。它可以是 system_clock 或 steady_clock 的别名,也可能是第三个独立时钟。在不同的标准库中,high_resolution_clock 的实现不一致,所以官方不建议使用这个时钟。

    这三个时钟类有一些共同的成员函数和数据类型,如下所示:

    名称 说明
    now() 静态成员函数,返回当前时间,类型为 clock::time_point
    time_point 成员类型,当前时钟的时间点类型,用于表示一个具体时间,详情见下文“时间点”
    duration 成员类型,时钟的时长类型,用于表示时间间隔(一段时间),详情见下文“时长”
    rep 成员类型,时钟的 tick 类型,等同于 clock::duration::rep
    period 成员类型,时钟的单位,等同于 clock::duration::period
    is_steady 静态成员类型:是否是稳定时钟,对于 steady_clock 来说该值一定是 true

    每一个时钟类都有一个 now() 静态函数来获取当前时间,返回的类型由 time_*point 描述。std::chrono::time_point 是模板类,模版类实例如:std::chrono::time_pointcount object. period The second template parameter (Period) The ratio type that represents a period in seconds.

    durationRep 类型的计次数Period 类型的计次周期组成,其中计次周期是一个编译期有理数常量,表示从一个计次到下一个的秒数。存储于 duration 的数据仅有 Rep 类型的计次数。若 Rep 是浮点数,则 duration 能表示小数的计次数。 Period 被包含为时长类型的一部分,且只在不同时长间转换时使用。

    • Rep 表示一种数值类型,用来表示 Period 的数量,比如 int float double (count of ticks)。
    • Period 是 std::ratio 类型,用来表示【用秒表示的时间单位】比如 second milisecond (a tick period)。
    • 成员函数 count() 返回 Rep 类型的 Period 数量。

    常用的 duration 已经定义好了,在 std::chrono 头文件中,常用时长单位的代码如下:

    /// nanoseconds
    typedef duration 	nanoseconds;
    /// microseconds
    typedef duration 	microseconds;
    /// milliseconds
    typedef duration 	milliseconds;
    /// seconds
    typedef duration 		seconds;
    /// minutes
    typedef duration> 	minutes;
    /// hours
    typedef duration> 	hours;
    
    类型 定义
    std::chrono::nanoseconds duration
    std::chrono::microseconds duration
    std::chrono::milliseconds duration
    std::chrono::seconds duration
    std::chrono::minutes duration
    std::chrono::hours duration

    duration 类的 count() 成员函数返回时间间隔的具体数值。

    3.4.1,时间间隔转换函数 duration_cast

    因为有各种 duration 表示不同的时长单位,所以 chrono 库提供了 duration_cast 函数来换 duration 类型,其声明如下:

    template 
    constexpr ToDuration duration_cast(const duration& d);
    

    其定义比较复杂,但是我们日常使用可以直接使用 auto 推导函数返回对象类型,示例代码如下:

    #include 
    #include 
    #include 
    #include 
     
    void f()
    {
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
     
    int main()
    {
        auto t1 = std::chrono::high_resolution_clock::now();
        f();
        auto t2 = std::chrono::high_resolution_clock::now();
        // 整数时长:要求 duration_cast
        auto int_ms = std::chrono::duration_cast(t2 - t1);
        // 小数时长:不要求 duration_cast
        std::chrono::duration fp_ms = t2 - t1;
        std::cout << "f() took " << fp_ms.count() << " ms, "
                  << "or " << int_ms.count() << " whole milliseconds\n";
        // 程序输出结果: f() took 1000.23 ms, or 1000 whole milliseconds
    }
    

    3.5,时间点 time_point

    std::chrono::time_point 表示时间中的一个点(一个具体时间),如上个世纪80年代、你的生日、今天下午、火车出发时间等,只要它能用计算机时钟表示。其包含了时钟和时长两个信息。它被实现成如同存储一个 Duration 类型的自 Clock 的纪元起始开始的时间间隔的值。其声明如下:

    template<
        class Clock,
        class Duration = typename Clock::duration
    > class time_point;
    

    时钟的 now() 函数返回的值就是一个时间点。time_point 中的 time_since_epoch() 返回从其时钟起点开始的时长。可以通过两个时间点相减计算一个时间间隔,下面是代码示例:

    #include       /* printf */
    #include 
    #include 
    #include 
    
    using namespace std;
    
    void time_point_test()
    {
        auto start = chrono::steady_clock::now();
        double sum = 0;
        for(int i = 0; i < 100000000; i++) {
            sum += sqrt(i);
        }
        auto end = chrono::steady_clock::now();
        // 通过两个时间点相减计算一个时间间隔
        auto time_diff = end - start;
        // 将时间间隔单位转化为毫秒
        auto duration = chrono::duration_cast(time_diff);
        cout << "Sqrt Operation cost : " << duration.count() << "ms" << endl;
    }
    
    int main()
    {
        time_point_test();
        // 程序输出结果: Sqrt Operation cost : 838ms
    }
    

    3.5.1,时间点运算

    时间点有加法和减法操作,计算结果和常识一致:时间点 + 时长 = 时间点;时间点 - 时间点 = 时长。

    参考资料

    • C++ 日期和时间编程
    • C++日期和时间工具
    • C++ 头文件内容官方英文版资料