zoukankan      html  css  js  c++  java
  • C基础 time.h 简单思路扩展

    前言 - time 简单需求

      时间业务相关代码. 基本属于框架的最底层. 涉及的变动都很小. 以前参与游戏研发时候,

    这方面需求不少, 各种被策划花式吊打. 转行开发互联网服务之后很少遇到这方面需求.

    但是封装基础库的时候, 这方面好用api 功能是需要的. 在这就抛砖引玉简单包一层.

    首先看 time 库的封装有那些基础需求.

          0. 线程安全

          1. 时间串 和 时间戳互换

          2. 时间比较 ... 例如是否在同一天

          3. 毫秒精度时间串支持

    一切从简单出发, 通过上面需求, 带着大家展开作者的思路.

    times.h

    #ifndef _H_TIMES
    #define _H_TIMES
    
    #include <time.h>
    #include <stdbool.h>
    
    //
    // 1s = 1000ms  = 1000000us  = 1000000000ns
    // 1秒  1000毫秒  1000000微秒  1000000000纳秒
    // ~ 力求最小时间业务单元 ~ 
    //
    
    #ifdef __GNUC__
    
    #include <unistd.h>
    #include <sys/time.h>
    
    //
    // msleep - 睡眠函数, 颗粒度是毫秒.
    // m        : 待睡眠的毫秒数
    // return   : void
    //
    inline void msleep(int ms) {
        usleep(ms * 1000);
    }
    
    #endif
    
    #ifdef _MSC_VER
    
    #include <windows.h>
    
    inline void msleep(int ms) {
        Sleep(ms);
    }
    
    //
    // localtime_r - 安全的得到当前时间结构体
    // timep        : 输入的时间戳指针
    // result       : 返回输出时间结构
    // return       : 失败 NULL, 正常返回 result
    //
    inline struct tm * localtime_r(const time_t * timep, struct tm * result) {
        return localtime_s(result, timep) ? NULL : result;
    }
    
    #endif
    
    // times_t - 时间串类型
    #define INT_TIMES (64)
    typedef char times_t[INT_TIMES];
    
    #endif//_H_TIMES

     我们这里通过采用 localtime_r 解决 time_t 获取线程安全问题.

     稍微扯一点 其实这里 winds localtime_s 设计思路好于 localtime_r.

                _Check_return_wat_
                static __inline errno_t __CRTDECL localtime_s(
                    _Out_ struct tm*    const _Tm,
                    _In_  time_t const* const _Time
                    )
                {
                    return _localtime64_s(_Tm, _Time);
                }

    errno_t 显然比 struct tm * 判断语义判断更加精确. 但从已经确定 time_t 实现角度出发. 更好接口设计应该是这样的

    errno_t localtime_s(time_t t, struct tm * result);

    毕竟 time_t 传入结构就足够.  第 0 个需求 线程安全 就搞定了.

    哈哈 看着 __GUNC__ _MSC_VER 突然想笑, 未来也许引进 __clang__. 程序员 = 同类玩同类的生物

    正文 - time 简单思路

      1. 时间串 和 时间戳 互换. 不妨直接从函数实现角度出发. 可以分为两部

        i) 时间串 parse -> struct tm

        ii) struct tm -> time_t

    先看第 i 步思路

    // times_tm - 从时间串中提取出来年月日时分秒
    bool times_tm(times_t tsr, struct tm * pm) {
        int c, num, * es, * py;
        if ((!tsr) || !(c = *tsr) || c < '0' || c > '9')
            return false;
    
        num = 0;
        es = &pm->tm_sec;
        py = &pm->tm_year;
        do {
            if (c >= '0' && c <= '9') {
                num = 10 * num + c - '0';
                c = *++tsr;
                continue;
            }
    
            *py-- = num;
            if (py < es)
                break;
    
            // 去掉特殊字符, 重新开始
            for (;;) {
                if ((c = *++tsr) == '')
                    return false;
                if (c >= '0' && c <= '9')
                    break;
            }
            num = 0;
        } while (c);
    
        // true : py < es || c == '' && py == es
        if (py < es) return true;
        if (py == es) {
            *es = num;
            return true;
        }
        return false;
    }

    这个函数为了能够处理下面这些类型的时间串, 并返回简单结果

    "2018年3月11日 19点35分59秒"

    "2018-03-11 19:35:58"

    "2018年03月 :) 19点35分59秒"

    ... ... ...

    后面随意些了, 第 ii 步 struct tm -> time_t

    //
    // time_get - 解析时间串, 返回时间戳
    // tsr          : 时间串内容  
    // return       : < 0 is error
    //
    inline time_t 
    time_get(times_t tsr) {
        struct tm m;
        // 先高效解析出年月日时分秒
        if (!times_tm(tsr, &m))
            return -1;
    
        // 得到时间戳, 失败返回false
        m.tm_mon -= 1;
        m.tm_year -= 1900;
        return mktime(&m);
    }

    不知道有没有人好奇为什么会出现魔法数字, 1 1900. (注 0, -1 是 api 设计中默认潜规则的魔法数字)

    这个主要是遵从编译器 c runtime 底层设计的风格, 看下面

    //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    //
    // Types
    //
    //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    struct tm
    {
        int tm_sec;   // seconds after the minute - [0, 60] including leap second
        int tm_min;   // minutes after the hour - [0, 59]
        int tm_hour;  // hours since midnight - [0, 23]
        int tm_mday;  // day of the month - [1, 31]
        int tm_mon;   // months since January - [0, 11]
        int tm_year;  // years since 1900
        int tm_wday;  // days since Sunday - [0, 6]
        int tm_yday;  // days since January 1 - [0, 365]
        int tm_isdst; // daylight savings time flag
    };

    这么多数字. 说白了和数学有点关系的就直接数字吧. 来个宏有可能更不懂. 这里写代码有个原则

      最底层代码怎么写, 咱们就怎么写.

    应用到社会生活中, 老大怎么弄, 咱们就怎么弄. 他打那我们射那 ... .... (ps: sb 哲学)

       2. 时间比较 ... 例如是否在同一天

      这个实现思路, 涉及到时区问题. 我在这里只实现了 China 部分.

    //
    // time_day - 判断时间戳是否是同一天
    // n            : 第一个时间戳
    // t            : 第二个时间戳
    // return       : true 表示同一天
    //
    inline bool 
    time_day(time_t n, time_t t) {
        // China local 适用, 得到当前天数
        // GMT [World] + 8 * 3600 = CST [China]
        n = (n + 8UL * 3600) / (24 * 3600);
        t = (t + 8UL * 3600) / (24 * 3600);
        return n == t;
    }

    在国际化思路中. 可以通过 Configure 处理.

    对上面函数包装一个语法糖函数如下

    //
    // time_now - 判断时间戳是否是今天
    // t            : 待判断的时间戳
    // return       : 返回当前时间戳, -1 is error
    //
    inline time_t 
    time_now(time_t t) {
        time_t n = time(NULL);
        return time_day(n, t) ? n : -1;
    }

    或者直接通过时间串比较

    //
    // times_day - 判断时间串是否是同一天
    // ns           : 第一个时间串
    // ts           : 第二个时间串
    // return       : true 表示同一天
    //
    bool 
    times_day(times_t ns, times_t ts) {
        time_t t, n = time_get(ns);
        // 解析失败直接返回结果
        if ( (n < 0) || ((t = time_get(ts)) < 0))
            return false;
        return time_day(n, t);
    }

    更多相关判断业务, 也就是拼接积木了. 可以自己玩玩.

      3. 毫秒精度时间串支持

     这里我是这么封装的, 首先看声明

    //
    // times_fmt - 通过 fmt 格式最终拼接一个字符串
    // fmt          : 必须包含 %04d %02d %02d %02d %02d %02d %03ld
    // buf          : 最终保存的内容
    // sz           : buf 长度
    // return       : 返回生成串长度
    //
    int times_fmt(const char * fmt, char buf[], size_t sz);
    
    //
    // times_str - 得到毫秒串 [2016-07-10 22:38:34 500]
    // osr          : 返回生成串
    // return       : 返回生成串长度
    //
    #define STR_TIMES "%04d-%02d-%02d %02d:%02d:%02d %03ld"
    inline int times_str(times_t osr) {
        return times_fmt(STR_TIMES, osr, sizeof(times_t));
    }

    随后是实现部分

    int 
    times_fmt(const char * fmt, char buf[], size_t sz) {
        struct tm m;
        struct timespec s;
    
        timespec_get(&s, TIME_UTC);
        localtime_r(&s.tv_sec, &m);
    
        return snprintf(buf, sz, fmt,
                        m.tm_year + 1900, m.tm_mon + 1, m.tm_mday,
                        m.tm_hour, m.tm_min, m.tm_sec, 
                        s.tv_nsec / 1000000);
    }

    思路直白. 通过 struct tm 中各个字段意义填充. 使用 timespec_get 得到纳秒级别时间精度.

    times_str 在 log 日志库封装中很常用

    很简单是不是. 更多的封装, 可以在链接接口设计中扩展出去

      times.h https://github.com/wangzhione/structc/blob/master/structc/system/times.h

    开始起源于时光和星空 ~ 也许那些是真随机 : )

    后记 - time 纯属扯淡

       

      BUG 一定存在, 希望都在修改中提高. 心无旁贷 ~ 

  • 相关阅读:
    ural 1227 dfs判环&求最长路
    uva 11990 块状链表
    图论·二分图
    图论·双连通分量
    图论·欧拉迹
    组合博弈
    解题策略·状态精简
    组合计数·棋盘统计
    组合计数·图形统计
    组合计数·经典序列问题
  • 原文地址:https://www.cnblogs.com/life2refuel/p/8545842.html
Copyright © 2011-2022 走看看