引言
在我遇到的标准库中,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_point
, duration
, Rep
, Period
类型。 操作有: - 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++ std::chrono中时钟的用法以及和time_t的转换和格式化
源码附录
#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;
}