工具类库系列(六)-TimeTool

时间:2022-03-05 06:17:39

第六个工具类,TimeTool


用于秒精度的时间获取:距离一个参考时间所经历的秒数,用于时间戳

以及计算两个时间戳,按照特定时间分割点分割后的差值


比如:一般游戏项目中通常都会有这样的需求:

某个任务,1周之内只能接取5次,周6早上6点算下一周开始,次数清零,

某个活动,每月5号开启下一轮

等等


所以通常就会存在需要计算玩家的本次操作(比如申请接任务),距离 上一次操作(上一次的接任务),以一个特定时间点为分界(周6早上6点),是否是同一小时/天/周/月,经过了几小时/天/周/月


首先,一般用time(NULL),取当前时间距离1970/1/1 00:00:00 所经历的总秒数, 作为时间戳


但是需要注意的是这个time(NULL)都是UTC时间


如果,需求是比如周6早上6点算下一周,这个显然说的是我们东八区的时间,相对于time(NULL)还存在一个时区转换的问题


怎么算:

假设玩家 2016/12/30(周5) 20:00:00接的任务,服务端这边记录了一个时间戳 t1

玩家 2016/12/31(周6)07:00:00再次接任务,服务端这边记录了一个时间戳 t2

t1和t2都是time(NULL)得到的UTC时间

而1970/1/1是周四,1970/1/5是周一

为了计算周数,将t1,和t2,减去4天,得到一个秒数,这个秒数即为距离UTC时间1970/1/5 00:00:00 所经历的总秒数

需求要求是周六,

再将t1,和t2,减去(6-1)天,得到一个秒数,这个秒数即为距离UTC时间1970/1/10 00:00:00 所经历的总秒数

而东八区的早上6点,即UTC时间的前一天晚上22点

再将t1,和t2,加上2小时,得到一个秒数这个秒数即为距离UTC时间的1970/1/9 22:00:00(东八区时间1970/1/10 06:00:00 周六)所经历的总秒数

以这个时间做为第一周的开始

将t1,和t2,分别除以7天,则得到距离UTC时间的1970/1/9 22:00:00所经历的总周数

如果这两个周数相等,则这两个时间,以东八区的周6早上6点为界,属于同一周


是否属于同一天类似,但是就不用偏移到周一了,就用1月1号作为参考时间,转换一下时区,然后除以24小时就好了


是否属于同一小时,也类似,而且这个不存在时区问题,更简单,直接算


是否属于同一个月,这个不能用上面的方法来算了,因为每个月的天数是不一样的

只能将时间戳,用localtime(),转换成本地时间的年月日时分秒

比如每个月的5号早上6点,

则将时间戳,减去5天6小时,以每个月的5号早上6点做为一个月的开始

减完之后,转成本地时间,得到年月,

然后年数×12+月数,算出来的就是距离东八区的1970/1/5 06:00:00 的总月数

月数相等则为同一周

但是这个依然存在一个问题,就是分割用的时间,不能大于28号,因为2月只有28天

如果用比如30号做为分割的话,3月的1号,2号就会被算到2月去


示例代码:

以2分2秒为界

以2时2分2秒为界

以周2,2时2分2秒为界

以2号,2时2分2秒为界

分别判断是否属于同一个小时/天/周/月

int main(int argc, char* argv[])
{
#define SetTm(number, year, mon, day, hour, min, sec) \
tm##number.tm_year = year - 1900; \
tm##number.tm_mon = mon - 1; \
tm##number.tm_mday = day; \
tm##number.tm_hour = hour; \
tm##number.tm_min = min; \
tm##number.tm_sec = sec; \
t##number = mktime(&tm##number);

#define PrintTM \
std::cout << \
(tm1.tm_year + 1900) << "/" << (tm1.tm_mon + 1) << "/" << tm1.tm_mday << " " << tm1.tm_hour << ":" << tm1.tm_min << ":" << tm1.tm_sec << \
" -> " << \
(tm2.tm_year + 1900) << "/" << (tm2.tm_mon + 1) << "/" << tm2.tm_mday << " " << tm2.tm_hour << ":" << tm2.tm_min << ":" << tm2.tm_sec << \
std::endl;

#define PrintHour \
PrintTM \
std::cout << \
"IsDifferentHour " << TimeTool::IsDifferentHour(t1, t2, 2, 2) << \
", " << \
"GetDeltaHour " << TimeTool::GetDeltaHour(t1, t2, 2, 2) << \
std::endl;

#define PrintDay \
PrintTM \
std::cout << \
"IsDifferentDay " << TimeTool::IsDifferentDay(t1, t2, 2, 2, 2) << \
", " << \
"GetDeltaDay " << TimeTool::GetDeltaDay(t1, t2, 2, 2, 2) << \
std::endl;

#define PrintWeek \
PrintTM \
std::cout << \
"IsDifferentWeek " << TimeTool::IsDifferentWeek(t1, t2, 2, 2, 2, 2) << \
", "<< \
"GetDeltaWeek " << TimeTool::GetDeltaWeek(t1, t2, 2, 2, 2, 2) << \
std::endl;

#define PrintMonth \
PrintTM \
std::cout << \
"IsDifferentMonth " << TimeTool::IsDifferentMonth(t1, t2, 2, 2, 2, 2) << \
", " << \
"GetDeltaMonth " << TimeTool::GetDeltaMonth(t1, t2, 2, 2, 2, 2) << \
std::endl;

tm tm1;
tm tm2;

time_t t1;
time_t t2;

SetTm(1, 2017, 1, 1, 23, 59, 59);
SetTm(2, 2017, 1, 2, 0, 0, 0);
PrintHour;
SetTm(1, 2017, 1, 1, 23, 59, 59);
SetTm(2, 2017, 1, 2, 0, 2, 1);
PrintHour;
SetTm(1, 2017, 1, 1, 23, 59, 59);
SetTm(2, 2017, 1, 2, 0, 2, 2);
PrintHour;
SetTm(1, 2017, 1, 1, 23, 59, 59);
SetTm(2, 2017, 1, 2, 1, 2, 2);
PrintHour;

std::cout << std::endl;

SetTm(1, 2017, 1, 1, 23, 59, 59);
SetTm(2, 2017, 1, 2, 0, 0, 0);
PrintDay;
SetTm(1, 2017, 1, 1, 23, 59, 59);
SetTm(2, 2017, 1, 2, 2, 2, 1);
PrintDay;
SetTm(1, 2017, 1, 1, 23, 59, 59);
SetTm(2, 2017, 1, 2, 2, 2, 2);
PrintDay;
SetTm(1, 2017, 1, 1, 23, 59, 59);
SetTm(2, 2017, 1, 3, 2, 2, 2);
PrintDay;

std::cout << std::endl;

SetTm(1, 2017, 1, 1, 23, 59, 59);
SetTm(2, 2017, 1, 3, 0, 0, 0);
PrintWeek;
SetTm(1, 2017, 1, 1, 23, 59, 59);
SetTm(2, 2017, 1, 3, 2, 2, 1);
PrintWeek;
SetTm(1, 2017, 1, 1, 23, 59, 59);
SetTm(2, 2017, 1, 3, 2, 2, 2);
PrintWeek;
SetTm(1, 2017, 1, 1, 23, 59, 59);
SetTm(2, 2017, 1, 10, 2, 2, 2);
PrintWeek;

std::cout << std::endl;

SetTm(1, 2017, 1, 1, 23, 59, 59);
SetTm(2, 2017, 1, 2, 0, 0, 0);
PrintMonth;
SetTm(1, 2017, 1, 1, 23, 59, 59);
SetTm(2, 2017, 1, 2, 2, 2, 1);
PrintMonth;
SetTm(1, 2017, 1, 1, 23, 59, 59);
SetTm(2, 2017, 1, 2, 2, 2, 2);
PrintMonth;
SetTm(1, 2017, 1, 1, 23, 59, 59);
SetTm(2, 2017, 2, 1, 2, 2, 2);
PrintMonth;
SetTm(1, 2017, 1, 1, 23, 59, 59);
SetTm(2, 2017, 2, 2, 2, 2, 2);
PrintMonth;

return 0;
}

结果截图

工具类库系列(六)-TimeTool



最后完整代码:

TimeTool.h

#ifndef __TimeTool_h__
#define __TimeTool_h__

#include <time.h>

namespace common{
namespace tool{

class TimeTool
{
public:
// 返回距离UTC时间1970/1/1 00:00:00的总秒数
static time_t GetCurrSecond();


// 返回本地日历日期,用于取年月日时分秒
// t为GetCurrSecond获取到的UTC时间
static tm GetDateTime(time_t t);
// 返回当前本地日历日期,用于取年月日时分秒
static tm GetCurrDateTime();


// M分S秒为界(M分S秒之前算前一小时),返回两个秒数之间,相差几小时
// t1,t2: GetCurrSecond获取到的UTC时间
// m: 0 ~ 59
// s: 0 ~ 59
static unsigned int GetDeltaHour(time_t t1, time_t t2, unsigned int m, unsigned int s);

// 以东八区时间每天H时M分S秒为界(H时M分S秒之前算前一天),返回两个秒数之间,相差几天
// t1,t2: GetCurrSecond获取到的UTC时间
// h: 0 ~ 23,东八区时间
// m: 0 ~ 59
// s: 0 ~ 59
static unsigned int GetDeltaDay(time_t t1, time_t t2, unsigned int h, unsigned int m, unsigned int s);

// 以东八区时间每周星期W,H时M分S秒为界(星期W,H时M分S秒之前算上一周),返回两个秒数之间,相差几周
// t1,t2: GetCurrSecond获取到的UTC时间
// w: 1 ~ 7,东八区时间
// h: 0 ~ 23,东八区时间
// m: 0 ~ 59
// s: 0 ~ 59
static unsigned int GetDeltaWeek(time_t t1, time_t t2, unsigned int w, unsigned int h, unsigned int m, unsigned int s);

// 以东八区时间每月D号,H时M分S秒为界(D号,H时M分S秒之前算上一个月),返回两个秒数之间,相差几个月
// t1,t2: GetCurrSecond获取到的UTC时间
// d: 1 ~ 28,东八区时间
// h: 0 ~ 23,东八区时间
// m: 0 ~ 59
// s: 0 ~ 59
static unsigned int GetDeltaMonth(time_t t1, time_t t2, unsigned int d, unsigned int h, unsigned int m, unsigned int s);


// M分S秒为界(M分S秒之前算前一小时),判断两个秒数是否是同一小时
// t1,t2: GetCurrSecond获取到的UTC时间
// m: 0 ~ 59
// s: 0 ~ 59
static bool IsDifferentHour(time_t t1, time_t t2, unsigned int m, unsigned int s);

// 以东八区时间每天H时M分S秒为界(H时M分S秒之前算前一天),判断两个秒数是否是同一天
// t1,t2: GetCurrSecond获取到的UTC时间
// h: 0 ~ 23,东八区时间
// m: 0 ~ 59
// s: 0 ~ 59
static bool IsDifferentDay(time_t t1, time_t t2, unsigned int h, unsigned int m, unsigned int s);

// 以东八区时间每周星期W,H时M分S秒为界(星期W,H时M分S秒之前算上一周),判断两个秒数是否是同一周
// t1,t2: GetCurrSecond获取到的UTC时间
// w: 1 ~ 7,东八区时间
// h: 0 ~ 23,东八区时间
// m: 0 ~ 59
// s: 0 ~ 59
static bool IsDifferentWeek(time_t t1, time_t t2, unsigned int w, unsigned int h, unsigned int m, unsigned int s);

// 以东八区时间每月D号,H时M分S秒为界(D号,H时M分S秒之前算上一个月),判断两个秒数是否是同一月
// t1,t2: GetCurrSecond获取到的UTC时间
// d: 1 ~ 28,东八区时间
// h: 0 ~ 23,东八区时间
// m: 0 ~ 59
// s: 0 ~ 59
static bool IsDifferentMonth(time_t t1, time_t t2, unsigned int d, unsigned int h, unsigned int m, unsigned int s);


// 将 距离UTC时间1970/1/1 00:00:00的总秒数
// 换算成 距离UTC+8时间1970/1/1 h:m:s的总秒数
// t: GetCurrSecond获取到的UTC时间
// h: 0 ~ 23,东八区时间
// m: 0 ~ 59
// s: 0 ~ 59
static time_t GetHMS_Utc8(time_t t, unsigned int h, unsigned int m, unsigned int s);

// 将 距离UTC时间1970/1/1 00:00:00的总秒数
// 换算成 距离UTC+8时间1970/1/(5+w-1) h:m:s的总秒数,1970/1/5为周一
// t: GetCurrSecond获取到的UTC时间
// w: 1 ~ 7,东八区时间
// h: 0 ~ 23,东八区时间
// m: 0 ~ 59
// s: 0 ~ 59
static time_t GetWHMS_Utc8(time_t t, unsigned int w, unsigned int h, unsigned int m, unsigned int s);
};

}
}

#endif

TimeTool.cpp

#include "TimeTool.h"

namespace common{
namespace tool{

time_t TimeTool::GetCurrSecond()
{
return time(NULL);
}

tm TimeTool::GetDateTime(time_t t)
{
tm tm = *localtime(&t);
tm.tm_year += 1900; /* years since 1900 */
tm.tm_mon += 1; /* months since January - [0,11] */
return tm;
}

tm TimeTool::GetCurrDateTime()
{
time_t t = GetCurrSecond();
return GetDateTime(t);
}

unsigned int TimeTool::GetDeltaHour(time_t t1, time_t t2, unsigned int m, unsigned int s)
{
if (t1 != t2)
{
// 参数校验
if (m > 59 || s > 59)
{
m = 0;
s = 0;
}

// 更换参考时间
t1 = t1 - (m * 60 + s);
t2 = t2 - (m * 60 + s);

// 换算成小时
t1 = t1 / (60 * 60);
t2 = t2 / (60 * 60);

// 计算小时数的差值
if (t1 > t2)
{
return static_cast<unsigned int>(t1 - t2);
}
else if (t1 < t2)
{
return static_cast<unsigned int>(t2 - t1);
}
else
{
return 0;
}
}
else
{
return 0;
}
}

unsigned int TimeTool::GetDeltaDay(time_t t1, time_t t2, unsigned int h, unsigned int m, unsigned int s)
{
if (t1 != t2)
{
// 参数校验
if (h > 23 || m > 59 || s > 59)
{
h = 0;
m = 0;
s = 0;
}

// 更换参考时间
t1 = GetHMS_Utc8(t1, h, m, s);
t2 = GetHMS_Utc8(t2, h, m, s);

// 换算成天数
t1 = t1 / (24 * 60 * 60);
t2 = t2 / (24 * 60 * 60);

// 计算天数的差值
if (t1 > t2)
{
return static_cast<unsigned int>(t1 - t2);
}
else if (t1 < t2)
{
return static_cast<unsigned int>(t2 - t1);
}
else
{
return 0;
}
}
else
{
return 0;
}
}

unsigned int TimeTool::GetDeltaWeek(time_t t1, time_t t2, unsigned int w, unsigned int h, unsigned int m, unsigned int s)
{
if (t1 != t2)
{
// 参数校验
if (w < 1 || w > 7 || h > 23 || m > 59 || s > 59)
{
w = 1;
h = 0;
m = 0;
s = 0;
}

// 更换参考时间
t1 = GetWHMS_Utc8(t1, w, h, m, s);
t2 = GetWHMS_Utc8(t2, w, h, m, s);

// 换算成周数
t1 = t1 / (7 * 24 * 60 * 60);
t2 = t2 / (7 * 24 * 60 * 60);

// 计算周数的差值
if (t1 > t2)
{
return static_cast<unsigned int>(t1 - t2);
}
else if (t1 < t2)
{
return static_cast<unsigned int>(t2 - t1);
}
else
{
return 0;
}
}
else
{
return 0;
}
}

unsigned int TimeTool::GetDeltaMonth(time_t t1, time_t t2, unsigned int d, unsigned int h, unsigned int m, unsigned int s)
{
if (t1 != t2)
{
// 参数校验
if (d < 1 || d > 28 || h > 23 || m > 59 || s > 59)
{
d = 1;
h = 0;
m = 0;
s = 0;
}

// 更换参考时间,以d号h时m分s秒为参考时间
// 不考虑时区,后面就是以本地时间计算
t1 = t1 - ((d - 1) * 24 * 60 * 60 + h * 60 * 60 + m * 60 + s);
t2 = t2 - ((d - 1) * 24 * 60 * 60 + h * 60 * 60 + m * 60 + s);

// 转化为本地时间
tm tm1 = *localtime(&t1);
tm tm2 = *localtime(&t2);

// 换算成月数
t1 = tm1.tm_year * 12 + tm1.tm_mon;
t2 = tm2.tm_year * 12 + tm2.tm_mon;

if (t1 > t2)
{
return static_cast<unsigned int>(t1 - t2);
}
else if (t1 < t2)
{
return static_cast<unsigned int>(t2 - t1);
}
else
{
return 0;
}
}
else
{
return 0;
}
}

bool TimeTool::IsDifferentHour(time_t t1, time_t t2, unsigned int m, unsigned int s)
{
return 0 != GetDeltaHour(t1, t2, m, s);
}

bool TimeTool::IsDifferentDay(time_t t1, time_t t2, unsigned int h, unsigned int m, unsigned int s)
{
return 0 != GetDeltaDay(t1, t2, h, m, s);
}

bool TimeTool::IsDifferentWeek(time_t t1, time_t t2, unsigned int w, unsigned int h, unsigned int m, unsigned int s)
{
return 0 != GetDeltaWeek(t1, t2, w, h, m, s);
}

bool TimeTool::IsDifferentMonth(time_t t1, time_t t2, unsigned int d, unsigned int h, unsigned int m, unsigned int s)
{
return 0 != GetDeltaMonth(t1, t2, d, h, m, s);
}

time_t TimeTool::GetHMS_Utc8(time_t t, unsigned int h, unsigned int m, unsigned int s)
{
// 如:东八区时间的:9时30分30秒,则为UTC时间的:1时30分30秒(参考时间往后偏移,总秒数减少)
// 如:东八区时间的:7时30分30秒,则为UTC时间的前一天:23时30分30秒(参考时间往前偏移,总秒数增加)
unsigned int deltaT = h * 60 * 60 + m * 60 + s;
unsigned int delta8 = 8 * 60 * 60;
if (delta8 <= deltaT)
{
t = t - (deltaT - delta8);
}
else
{
t = t + (delta8 - deltaT);
}

return t;
}

time_t TimeTool::GetWHMS_Utc8(time_t t, unsigned int w, unsigned int h, unsigned int m, unsigned int s)
{
t = GetHMS_Utc8(t, h, m, s);

// 1970/1/1 为周4
// 1970/1/5 为周1
// 换算成距离1970/1/5 h:m:s的总秒数,即减4天
t = t - 4 * 24 * 60 * 60;

// 换算成距离星期W,即距离1970/1/(5+w-1) h:m:s的总秒数,即减(w-1)天
t = t - (w - 1) * 24 * 60 * 60;

return t;
}
}
}