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) == '\0')
                return false;
            if (c >= '0' && c <= '9')
                break;
        }
        num = 0;
    } while (c);

    // true : py < es || c == '\0' && 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 一定存在, 希望都在修改中提高. 心無旁貸 ~ 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM