PRId64
int64_t
用来表示64位整数,在32位系统中是 long long int
,在64位系统中是 long int
,所以打印 int64_t
的格式化方法是:
printf("%ld", value); //@ 64bit OS
printf("%lld", value); //@ 32bit OS
跨平台的方法:
#include <inttypes.h>
printf("%" PRId64 "
", value);
//@ 相当于64位的:
printf("%" "ld" "
", value);
//@ 或32位的:
printf("%" "lld" "
", value);
这个是定义给 C 用的,C++要用它,就要定义一个 __STDC_FORMAT_MACROS
宏显示打开它。
Linux 下时间
术语
- Coordinated Universal Time(UTC):协调世界时,又称为世界标准时间,也就是大家所熟知的格林威治标准时间(Greenwich Mean Time,GMT)。比如,中国内地的时间与UTC的时差为+8,也就是UTC+8。美国是UTC-5。
- Calendar Time: 日历时间,是用“从一个标准时间点到此时的时间经过的秒数”来表示的时间。这个标准时间点对不同的编译器来说会有所不同,但对一个编译系统来说,这个标准时间点是不变的,该编译系统中的时间对应的日历时间都通过该标准时间点来衡量,所以可以说日历时间是“相对时间”,但是无论你在哪一个时区,在同一时刻对同一个标准时间点来说,日历时间都是一样的。
- epoch:时间点。时间点在标准C/C++中是一个整数,它用此时的时间和标准时间点相差的秒数(即日历时间)来表示。
- clock tick:时钟计时单元(而不把它叫做时钟滴答次数),一个时钟计时单元的时间长短是由CPU控制的。一个clock tick不是CPU的一个时钟周期,而是C/C++的一个基本计时单位。
数据结构
struct tm
ANSI C标准称使用 tm
结构的这种时间表示为分解时间(broken-down time)。
struct tm
{
int tm_sec; /* 秒 – 取值区间为[0,59] */
int tm_min; /* 分 - 取值区间为[0,59] */
int tm_hour; /* 时 - 取值区间为[0,23] */
int tm_mday; /* 一个月中的日期 - 取值区间为[1,31] */
int tm_mon; /* 月份(从一月开始,0代表一月) - 取值区间为[0,11] */
int tm_year; /* 年份,其值等于实际年份减去1900 */
int tm_wday; /* 星期 – 取值区间为[0,6],其中0代表星期天,1代表星期一,以此类推 */
int tm_yday; /* 从每年的1月1日开始的天数 – 取值区间为[0,365],其中0代表1月1日,1代表1月2日,以此类推 */
int tm_isdst; /* 夏令时标识符,实行夏令时的时候,tm_isdst为正。不实行夏令时的进候,tm_isdst为0;不了解情况时,tm_isdst()为负。*/
};
time_t
time_t
表示的时间(日历时间)是从一个时间点(例如:1970年1月1日0时0分0秒)到此时的秒数。
typedef long time_t; /* 时间值 */
对 time_t
数据类型的值来说,它所表示的时间不能晚于2038年1月18日19时14分07秒。为了能够表示更久远的时间,一些编译器厂商引入了64位甚至更长的整形数来保存日历时间。比如微软在 Visual C++ 中采用了 __time64_t
数据类型来保存日历时间,并通过 _time64()
函数来获得日历时间,这样就可以通过该数据类型保存3001年1月1日0时0分0秒(不包括该时间点)之前的时间。
struct timeval
struct timeval
{
__time_t tv_sec; /* 秒*/
__suseconds_t tv_usec; /* 微秒*/
};
tv_sec
为 Epoch 到创建struct timeval
时的秒数,tv_usec
为微秒数。
clock_t
typedef long clock_t;
struct timespec
struct timespec
{
time_t tv_sec; /* 秒*/
long tv_nsec; /* 纳秒*/
};
相关函数
获得日历时间
time_t time(time_t * timer);
- 参数为空(NULL),函数将只通过返回值返回现在的日历时间。
- 参数不为空(NULL),函数将通过返回值返回现在的日历时间或者通过
timer
获取现在的日历时间。
分解时间转化为日历时间
time_t mktime(struct tm * timeptr);
日历时间转换成分解时间
struct tm *gmtime(const time_t *timep); //@ 线程不安全
struct tm *gmtime_r(const time_t *timep, struct tm *result); //@ 线程安全
struct tm *localtime(const time_t *timep); // 线程不安全
struct tm *localtime_r(const time_t *timep, struct tm *result); //@ 线程安全
gmtime()
函数是将日历时间转化为世界标准时间(即格林尼治时间)。localtime()
函数是将日历时间转化为本地时间,即包含了时区。
固定的时间格式
char * asctime(const struct tm * timeptr);
char * ctime(const time_t *timer);
asctime()
函数是通过tm结构来生成具有固定格式的保存时间信息的字符串。ctime()
函数需要先参照本地的时间设置,把日历时间转化为本地时间,然后再生成格式化后的字符串。- 返回的时间格式为:
星期几 月份 日期 时:分:秒 年
自定义时间格式
size_t strftime(char *strDest,size_t maxsize,const char *format,const struct tm *timeptr);
- 根据
format
指向字符串中格式命令把timeptr
中保存的时间信息放在strDest
指向的字符串中,最多向strDest
中存放maxsize
个字符。该函数返回向strDest
指向的字符串中放置的字符数。 - 格式命令列在下面,它们是区分大小写的:
%a 星期几的简写
%A 星期几的全称
%b 月份的简写
%B 月份的全称
%c 标准的日期的时间串
%C 年份的后两位数字
%d 十进制表示的每月的第几天
%D 月/天/年
%e 在两字符域中,十进制表示的每月的第几天
%F 年-月-日
%g 年份的后两位数字,使用基于周的年
%G 年分,使用基于周的年
%h 简写的月份名
%H 24小时制的小时
%I 12小时制的小时
%j 十进制表示的每年的第几天
%m 十进制表示的月份
%M 十时制表示的分钟数
%n 新行符
%p 本地的AM或PM的等价显示
%r 12小时的时间
%R 显示小时和分钟:hh:mm
%S 十进制的秒数
%t 水平制表符
%T 显示时分秒:hh:mm:ss
%u 每周的第几天,星期一为第一天 (值从0到6,星期一为0)
%U 第年的第几周,把星期日做为第一天(值从0到53)
%V 每年的第几周,使用基于周的年
%w 十进制表示的星期几(值从0到6,星期天为0)
%W 每年的第几周,把星期一做为第一天(值从0到53)
%x 标准的日期串
%X 标准的时间串
%y 不带世纪的十进制年份(值从0到99)
%Y 带世纪部分的十制年份
%z,%Z 时区名称,如果不能得到时区名称则返回空字符。
%% 百分号
计算持续的时间长度
double difftime(time_t time1, time_t time0);
- 只能精确到秒。
获取时间和时区
int gettimeofday (struct timeval *__restrict __tv, __timezone_ptr_t __tz);
- 得到当前时间和时区,分别写到
tv
和tz
中,如果tz
为NULL
则不向tz
写入。 - 可以精确到微秒。
clock
clock_t clock(void) ;
- 表示进程运行时间,单位是CPU时钟计时单元(clock tick)数,或者说滴答数(ticks)。
CLOCKS_PER_SEC
是一个常数,表示一秒钟有多少个时钟计时单元。- 只能精确到秒。
- 线程不安全,因为
clock()
返回进程总的时钟计时单元数量,而不是当前线程的。
clock_gettime
int clock_gettime(clockid_t clk_id, struct timespec* tp);
clk_id
: 检索和设置的clk_id
指定的时钟时间:CLOCK_REALTIME
:系统实时时间,随系统实时时间改变而改变,即从 UTC1970-1-1 0:0:0 开始计时,中间时刻如果系统时间被用户改成其他,则对应的时间相应改变。CLOCK_MONOTONIC
:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响。CLOCK_PROCESS_CPUTIME_ID
:本进程到当前代码系统 CPU 花费的时间。CLOCK_THREAD_CPUTIME_ID
:本线程到当前代码系统 CPU 花费的时间。
计时函数总结
源码实现
TimeStamp.h
#pragma once
#include <cstdint>
#include <string>
#include "Copyable.h"
namespace muduo
{
//@ 源代码还继承自 boost::equality_comparable<Timestamp>, boost::less_than_comparable<Timestamp>
class TimeStamp : public Copyable
{
public:
static const int kMicroSecondsPerSecond = 1000 * 1000;
TimeStamp() : micro_seconds_since_epoch_(0) {}
explicit TimeStamp(int64_t micro_seconds) : micro_seconds_since_epoch_(micro_seconds) {}
void swap(TimeStamp &that)
{
std::swap(micro_seconds_since_epoch_, that.micro_seconds_since_epoch_);
}
std::string to_string() const;
std::string to_formatted_string(bool show_micro_seconds = true) const;
bool valid() const { return micro_seconds_since_epoch_ > 0; }
int64_t micro_seconds_since_epoch() const { return micro_seconds_since_epoch_; }
int64_t secnods_since_epoch() const
{
return static_cast<time_t>(micro_seconds_since_epoch_ / kMicroSecondsPerSecond);
}
static TimeStamp now();
static TimeStamp invalid()
{
return TimeStamp(); //@ 获取一个无效的时间戳
}
static TimeStamp from_unix_time(time_t t)
{
return from_unix_time(t, 0);
}
static TimeStamp from_unix_time(time_t t, int micro_seconds)
{
return TimeStamp(static_cast<int64_t>(t) * kMicroSecondsPerSecond + micro_seconds);
}
private:
int64_t micro_seconds_since_epoch_;
};
inline bool operator<(TimeStamp lhs, TimeStamp rhs)
{
return lhs.micro_seconds_since_epoch() < rhs.micro_seconds_since_epoch();
}
inline bool operator==(TimeStamp lhs, TimeStamp rhs)
{
return lhs.micro_seconds_since_epoch() == rhs.micro_seconds_since_epoch();
}
inline double time_difference(TimeStamp high, TimeStamp low)
{
int64_t diff = high.secnods_since_epoch() - low.secnods_since_epoch();
return static_cast<double>(diff) / TimeStamp::kMicroSecondsPerSecond;
}
inline TimeStamp add_time(TimeStamp time_stamp, double seconds)
{
int64_t delta = static_cast<int64_t>(seconds * TimeStamp::kMicroSecondsPerSecond);
return TimeStamp(time_stamp.secnods_since_epoch() + delta);
}
}
TimeStamp.cc
#include "TimeStamp.h"
#include <sys/time.h>
#include <inttypes.h> //@ PRId64
namespace muduo
{
static_assert(sizeof(TimeStamp) == sizeof(int64_t), "TimeStamp should be same size as int64_t");
std::string TimeStamp::to_string() const
{
char buf[32] = {0};
int64_t seconds = micro_seconds_since_epoch_ / kMicroSecondsPerSecond;
int64_t micro_seconds = micro_seconds_since_epoch_ % kMicroSecondsPerSecond;
snprintf(buf, sizeof(buf), "%" PRId64 ".%06" PRId64 "", seconds, micro_seconds);
return buf;
}
std::string TimeStamp::to_formatted_string(bool show_micro_seconds) const
{
char buf[64] = {0};
time_t seconds = static_cast<time_t>(micro_seconds_since_epoch_ / kMicroSecondsPerSecond);
struct tm tm_time;
gmtime_r(&seconds, &tm_time); //@ utc 时间
if (show_micro_seconds)
{
int micro_seconds = static_cast<int>(micro_seconds_since_epoch_ % kMicroSecondsPerSecond);
snprintf(buf, sizeof(buf), "%04d%02d%02d %02d:%02d:%02d.%06d", tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,
tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, micro_seconds);
}
else
{
snprintf(buf, sizeof(buf), "%04d%02d%02d %02d:%02d:%02d", tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,
tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec);
}
return buf;
}
TimeStamp TimeStamp::now()
{
struct timeval tv; //@ 精确到微秒
gettimeofday(&tv, NULL); //@ 忽略时区
int64_t seconds = tv.tv_sec;
return TimeStamp(seconds * kMicroSecondsPerSecond + tv.tv_usec);
}
}