关于c++的time梳理

引言

在我遇到的标准库中,c语言的time库给我带来不适感是最多的,特别是有些函数在windows下和Unix下是不同标准,还有一系列的数据结构不匹配,tm结构体不适配的问题。localtime函数在windows下居然默认编译不能通过?

比如gettimeofday()这个函数,就曾经给我带来了很多困扰。基于这个不爽,我试着把自己的总结与经验纪录下来,免得又要到处去搜索。

c++推行了一个新的单元重载符号,在需要设置一定单位的时间是可以直接添加字符后缀。

using namespace std::chrono_literals;
auto one_day=std::chrono::hours(24) // older
auto one_day=24h;   // latest
auto half_an_hour=std::chrono::minutes(30); // older
auto half_an_hour=30min;    // latest

下面正式走进chrono

chrono

一个完整的计时从一个时间点开始,到另一个时间点结束,这段时间点之间的计数我们称之为耗时。

clock

每一个clock类中都有确定的time_pointdurationRepPeriod类型。 操作有: - now()当前时间time_point - to_time_t() time_point转换成time_t秒 - from_time_t() 从time_t转换成time_point

在标准库中,Clock有三个选项 - system_clock: 当前系统范围内各进程统一的日历时钟,从机器启动时开始计时。 - steady_clock: 当前系统的维定始终,每个时间的跳动单位一致,对准的是UTC时间。 - high_resolution_clock: 高分辨率时钟。

time_point

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

time_point 表示的是一个时间点或者是时刻。 观察time_point的模板参数 - Clock 表示时钟来源 - Duration表示时间的计量单位 时间点都有一个时间戳,即时间原点。chrono库中采用的是Unix的时间戳1970年1月1日 00:00。 所以time_point也就是距离时间戳(epoch)的时间长度(duration)

//获取时间
chrono::system_clock::time_point now = chrono::system_clock::now();//当前时间time_point格式

std::time_t oldTime = time(nullptr);//c函数获取当前时间
cout << "oldTime = " << oldTime << endl;
chrono::system_clock::time_point timePoint = chrono::system_clock::now();//stl库获取当前时间
std::time_t newTime = chrono::system_clock::to_time_t(timePoint);//转换为旧式接口,单位:秒
cout<<"newTime = " << newTime <<endl;// oldTime == timeT

// 时间格式化
/*  chrono::system_clock::time_point与std::time_t类型可相互函数
 *  chrono::system_clock::to_time_t()   
 *  chrono::system_clock::from_time_t()     
 */
std::time_t nowTime = chrono::system_clock::to_time_t(now);//转换为 std::time_t 格式 
std::put_time(std::localtime(&nowTime), "%Y-%m-%d %X"); // 2019-06-18 14:25:56

// std::localtime非线程安全,使用localtime_r函数代替
struct tm cutTm = {0};
std::put_time(localtime_r(&nowTime, &cutTm), "%Y-%m-%d %X");

duration

template <class Rep, class Period = std::ratio<1>>
class duration;

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

template <std::intmax_t Num, std::intmax_t Denom = 1>
class ratio;

ratio 表示值等于 Num/Denom 的有理数,它的静态数据成员 num 与 den 表示由 Num 与 Denom 除以其最大公约数的分子与分母。

同时我们也能在标准库找到一些预先的时间定义

using std::chrono::duration;
typedef duration <Rep, ratio<3600,1>> hours;
typedef duration <Rep, ratio<60,1>> minutes;
typedef duration <Rep, ratio<1,1>> seconds;
typedef duration <Rep, ratio<1,1000>> milliseconds;
typedef duration <Rep, ratio<1,1000000>> microseconds;
typedef duration <Rep, ratio<1,1000000000>> nanoseconds;

// 此外,可以自己补充拓展days,months,years.
typedef duration <Rep, ratio<3600*24,1>> days;
typedef duration <Rep, ratio<3600*24*30,1>> months;
typedef duration <Rep, ratio<3600*24*30*365,1>> years;

在不同时长之间转换时,可以使用std::duration的构造函数或duration_cast进行无损转换.

chrono::minutes mintu{2};//2分钟
chrono::seconds sec{3};//3秒钟
chrono::milliseconds mills{500};//500毫秒
auto dul = sec - mills;//两者差值,单位默认转到更小的 2500ms
dul.count(); //值为2500

chrono::duration_cast<chrono::seconds>(mintu).count(); //2分钟换算为120秒

time

了解<time.h>中的常用函数

//返回一个指向字符串的指针,它代表了结构 timeptr 的日期和时间。
char *asctime(const struct tm *timeptr)

//返回程序执行起(一般为程序的开头),处理器时钟所使用的时间。
clock_t clock(void)

//返回一个表示当地时间的字符串,当地时间是基于参数 timer。
char *ctime(const time_t *timer)

//返回 time1 和 time2 之间相差的秒数 (time1-time2)。
double difftime(time_t time1, time_t time2)

//timer 的值被分解为 tm 结构,并用协调世界时(UTC)也被称为格林尼治标准时间(GMT)表示。
struct tm *gmtime(const time_t *timer)

//timer 的值被分解为 tm 结构,并用本地时区表示。
struct tm *localtime(const time_t *timer)

//把 timeptr 所指向的结构转换为一个依据本地时区的 time_t 值。
time_t mktime(struct tm *timeptr)

//根据 format 中定义的格式化规则,格式化结构 timeptr 表示的时间,并把它存储在 str 中。
size_t strftime(char *str, size_t maxsize, const char *format, const struct tm *timeptr)

//C 库函数 time_t time(time_t *seconds) 返回自纪元 Epoch(1970-01-01 00:00:00 UTC)起经过的时间,以秒为单位。如果 seconds 不为空,则返回值也存储在变量 seconds 中。
time_t time(time_t *timer)

不难知道其中有些方法可以复用,于是就得到了附录的转化,我使用了模板time_cast进行匹配自动转换,省去了记忆库函数的步骤,不管三七二十一,先转起来。

std::time_t t = std::time(0);
auto my_tm = time_cast<std::tm>(t); // 类型是std::tm
auto originTime = time_cast<std::time_t>(my_tm);   //转回std::time_t

std::string strCurTime("2020-10-21 11:22:33"); 
my_tm = time_cast<std::tm>(strCurTime);     // 字符转时间
originTime = time_cast<std::time_t>(strCurTime); // 字符串转时间戳

参考资料

C++11 std::chrono时间库

C++ std::chrono中时钟的用法以及和time_t的转换和格式化

C++11 std::chrono库详解

C++11 STL 的時間函式庫:chrono

c++11 chrono全面解析(最高可达纳秒级别的精度)

源码附录

#include <type_traits>
#include <string>
#include <stdexcept>
#include <sstream>
#include <locale>
#include <iomanip>
#include <chrono>

struct time_util
{
    static std::tm getUtcfrom(std::time_t _t)
    {
#ifdef _WIN32
        std::tm tm{};
        gmtime_s(&tm, &_t);
#elif __unix__
        std::tm tm = *std::gmtime(&_t);
#endif

        return tm;
    }

    static std::tm getLocalfrom(std::time_t _t)
    {
#ifdef _WIN32
        std::tm tm{};
        localtime_s(&tm, &_t);
#elif __unix__
        std::tm tm = *std::localtime(&_t);
#endif

        return tm;
    }

    static std::time_t fromTm(struct std::tm _tm)
    {
        return mktime(&_tm);
    }
};

class TimeClock
{
public:
    TimeClock() { update(); }

    void update() { m_tStart__ = std::chrono::high_resolution_clock::now(); }
    long long getTimerMicroSec()
    {
        using namespace ::std;
        using namespace std::chrono;
        return duration_cast<microseconds>(high_resolution_clock::now() - _start).count();
    }

    double getTimerMilliSec() { return getTimerMicroSec() * 1e-3; }
    double getTimerSecond() { return getTimerMicroSec() * 1e-6; }

private:
    std::chrono::time_point<std::chrono::high_resolution_clock> m_tStart__;
};

namespace time_detail
{
    template <typename To, typename From>
    struct Converter
    { };

    template <typename From>
    struct Converter<std::tm, From>
    {
        static std::tm convert(std::string &&From_F)      
        {
            return convert(From_F);          
        }                                               

        static std::tm convert(const std::string &From_F) 
        {                                               
            std::tm t = {};
            std::istringstream iss(From_F);
            iss.imbue(std::locale("zh_CN.utf8"));
            iss >> std::get_time(&t, "%Y-%m-%d %T");
            if (iss.fail()) throw std::invalid_argument(From_F.data());

            return t;        
        }                                               

        static std::tm convert(const char *From_F)        
        {                                               
            return convert(std::string{From_F});                  
        }                                               
    };

    template <typename From>
    struct Converter<std::time_t, From>
    {
        static std::time_t convert(std::string &&From_F)      
        {
            return convert(From_F);          
        }                                               

        static std::time_t convert(const std::string &From_F) 
        {                                               
            std::tm t = {};
            std::istringstream iss(From_F);
            iss.imbue(std::locale("zh_CN.utf8"));
            iss >> std::get_time(&t, "%Y-%m-%d %T");
            if (iss.fail()) throw std::invalid_argument(std::string(__FUNCTION__) + From_F.data());

            return time_util::fromTm(t);        
        }                                               

        static std::time_t convert(const char *From_F)        
        {                                               
            return convert(std::string{From_F});                  
        }                                               
    };

    template <>
    struct Converter<std::string, std::tm>
    {
        static std::string convert(std::tm &&From_F)
        {
            return convert(From_F);
        }

        static std::string convert(const std::tm &From_F)
        {
            std::ostringstream oss;
            oss.imbue(std::locale("zh_CN.utf8"));
            oss << std::put_time(&From_F, "%F %T");
            return oss.str();
        }
    };

    template <>
    struct Converter<std::string, std::time_t>
    {
        static std::string convert(std::time_t From_F)
        {
            return Converter<std::string, std::tm>::convert(time_util::getLocalfrom(From_F));
        }
    };

    template <>
    struct Converter<std::string, std::time_t &>
    {
        static std::string convert(std::time_t From_F)
        {
            return Converter<std::string, std::time_t>::convert(From_F);
        }
    };

    template <>
    struct Converter<std::tm, std::time_t &>
    {
        static std::tm convert(std::time_t From_F)
        {
            return time_util::getLocalfrom(From_F);
        }
    };

    template <>
    struct Converter<std::time_t, std::tm &>
    {
        static std::time_t convert(std::tm &&From_F)
        {
            return time_util::fromTm(From_F);
        }

        static std::time_t convert(std::tm const &From_F)
        {
            return convert(From_F);
        }
    };

    template <>
    struct Converter<std::time_t, std::tm &&>
    {
        static std::time_t convert(std::tm &&From_F)
        {
            return Converter<std::time_t, std::tm &>::convert(From_F);
        }

        static std::time_t convert(std::tm const &From_F)
        {
            return convert(From_F);
        }
    };
}   //end time_detail


template <typename To, typename From = std::string>
typename std::enable_if<!std::is_same<To, From>::value, To>::type time_cast(From &&From_F)
{
    return time_detail::Converter<To, From>::convert(std::forward<From>(From_F));
}

template <typename To, typename From = std::string>
typename std::enable_if<!std::is_same<To, From>::value, To>::type time_cast(const From &From_F)
{
    return time_detail::Converter<To, From>::convert(From_F);
}

template <typename To, typename From = std::string>
typename std::enable_if<std::is_same<To, From>::value, To>::type time_cast(const From &From_F)
{
    return From_F;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值