muduo 库解析之二:TimeStamp

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);
  • 得到当前时间和时区,分别写到 tvtz 中,如果 tzNULL 则不向 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);
    }

}
原文地址:https://www.cnblogs.com/xiaojianliu/p/14693418.html