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 * );
} #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 < '' || c > '')
return false; num = ;
es = &pm->tm_sec;
py = &pm->tm_year;
do {
if (c >= '' && c <= '') {
num = * num + c - '';
c = *++tsr;
continue;
} *py-- = num;
if (py < es)
break; // 去掉特殊字符, 重新开始
for (;;) {
if ((c = *++tsr) == '\0')
return false;
if (c >= '' && c <= '')
break;
}
num = ;
} 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 -; // 得到时间戳, 失败返回false
m.tm_mon -= ;
m.tm_year -= ;
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 * ) / ( * );
t = (t + 8UL * ) / ( * );
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 : -;
}
或者直接通过时间串比较
//
// times_day - 判断时间串是否是同一天
// ns : 第一个时间串
// ts : 第二个时间串
// return : true 表示同一天
//
bool
times_day(times_t ns, times_t ts) {
time_t t, n = time_get(ns);
// 解析失败直接返回结果
if ( (n < ) || ((t = time_get(ts)) < ))
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 + , m.tm_mon + , m.tm_mday,
m.tm_hour, m.tm_min, m.tm_sec,
s.tv_nsec / );
}
思路直白. 通过 struct tm 中各个字段意义填充. 使用 timespec_get 得到纳秒级别时间精度.
times_str 在 log 日志库封装中很常用
很简单是不是. 更多的封装, 可以在链接接口设计中扩展出去
times.h https://github.com/wangzhione/structc/blob/master/structc/system/times.h
开始起源于时光和星空 ~ 也许那些是真随机 : )
后记 - time 纯属扯淡

BUG 一定存在, 希望都在修改中提高. 心无旁贷 ~
C基础 time.h 简单思路扩展的更多相关文章
- PHP扩展开发-简单类扩展
今天来学习简单类扩展开发 实现目标为如下php的类 <?php class classext(){ private $username; CONST URL="http://www.g ...
- JS基础(超级简单)
1 JS基础(超级简单) 1.1 数据类型 1.1.1 基本类型: 1) Number:特别注意:NaN的检测方法:Nan!=NaN;或者使用isNaN方法 2) ...
- JMeter基础之一 一个简单的性能测试
JMeter基础之一 一个简单的性能测试 上一节中,我们了解了jmeter的一此主要元件,那么这些元件如何使用到性能测试中呢.这一节创建一个简单的测试计划来使用这些元件.该计划对应的测试需求. 1)测 ...
- .NET 简单的扩展方法使用。
写代码时,我们经常会碰到dll中提供的方法,不够用或者不好用的情况.而且我们也不方便去更改dll本身的源码. 这时候我们可以使用.NET提供的"扩展方法"去解决这个问题. 下面我写 ...
- ArcGIS Pro 简明教程(2)基础操作和简单制图
ArcGIS Pro 简明教程(2)基础操作和简单制图 By 李远祥 本章主要介绍ArcGIS Pro如何加载数据并进行简单的地图制作,以基本的操作为主. 上一章节介绍过,ArcGIS Pro是可以直 ...
- Android BLE与终端通信(一)——Android Bluetooth基础API以及简单使用获取本地蓝牙名称地址
Android BLE与终端通信(一)--Android Bluetooth基础API以及简单使用获取本地蓝牙名称地址 Hello,工作需要,也必须开始向BLE方向学习了,公司的核心技术就是BLE终端 ...
- EasyUI datagrid 明细表格中编辑框 事件绑定 及灵活计算 可根据此思路 扩展其他
原创 : EasyUI datagrid 明细表格中编辑框 事件绑定 及灵活计算 可根据此思路 扩展其他 转载,请注明出处哦!谢谢! 原创 : EasyUI datagrid 明细表格中编辑框 事件绑 ...
- ios - UINavigationBar添加背景图片的几种简单思路
UITabBarController下面常常需要为多个ViewController设置导航栏样式,总结了一下遇到过的为UINavigationBar添加背景图片的几种简单思路 以设置背景图片为例: 第 ...
- 将一个对象相同的属性(不区分大小写)赋值给一个新对象 DataTable的一个简单的扩展
将一个对象相同的属性(不区分大小写)赋值给一个新对象 1 public static T Mapper<S, T>(S source) 2 { 3 T t = Activator.Cr ...
随机推荐
- Dll劫持漏洞详解
一.dll的定义 DLL(Dynamic Link Library)文件为动态链接库文件,又称“应用程序拓展”,是软件文件类型.在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分 ...
- 解题:AHOI 2005 航线规划
题面 这种不断删边的首先肯定想到时光倒流啊=.= 在最后剩下的连通图上跑出一棵搜索树,先将边权都赋为$1$,那么两点间的关键航线就是链上边权和,而每加入一条非树边$u,v$都会使得$u,v$链上的边的 ...
- ros error : c++: error: $(catkin_LIBRARIES): 没有那个文件或目录
卧槽,真是........................瞎眼了. 一个半小时才找出错误来..... c++: error: $(catkin_LIBRARIES): 没有那个文件或目录 Oh my ...
- html中的事件属性
Window 事件属性 针对 window 对象触发的事件(应用到 <body> 标签): 属性 值 描述 onafterprint script 文档打印之后运行的脚本. onbefor ...
- 洛谷P1396 营救
题目描述 “咚咚咚……”“查水表!”原来是查水表来了,现在哪里找这么热心上门的查表员啊!小明感动的热泪盈眶,开起了门…… 妈妈下班回家,街坊邻居说小明被一群陌生人强行押上了警车!妈妈丰富的经验告诉她小 ...
- jetbrains phpstorm插件开发环境搭建
2018.04.14 重要更新: 使用 gradle 进行构建可以免去下面大部分步骤,使用 gradle 我们仅需下载安装 JDK.Idea. 使用 gradle 的方法是,新建 Project,然后 ...
- python学习(十四)正则表达式
原文链接 ## 什么是正则表达式`正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符.及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑 ...
- 安装vim with python
http://note.youdao.com/noteshare?id=4eaddfef93696451de7ff890a6af3cc4
- c++构造是否要加大括号
笔者被这个问题困扰良久,终于下决心看个究竟.废话不多说,先上结论: 如果对象是原生类型,加大括号会保证生成对象被初始化(一般是0) 如果对象非原生类型,加大括号或者不加,效果是一样的,都会执行该类的默 ...
- 优先队列 逆向思维 Gym 101128C
题目链接:http://codeforces.com/gym/101128/my 具体题目大意可以看这个人的:http://blog.csdn.net/v5zsq/article/details/61 ...