发布时间:2022-09-22 08:01:04来源:网络整理浏览:42
曾几何时,我总是担心日期的变化。我厌倦了闰月的计算,也厌倦了月跨度和年跨度的判断,比如:
你今天收到奖励了吗?距离上次手术已经过去一天了吗?还是两天?还是七日?你有多少天没有做某事?
有很多不同的东西,但总是需要获取当前日期并根据闰月和跨月来判断边界。如果三天的开始不是从零开始,那就更麻烦了。有简单的方法吗?想了半天,找到了一个简单的方法。在多个项目中使用确实方便简单,但是我也发现很多人还卡在老方法中,所以分享一下。
(以下代码示例是基于C语言的epoch,相关技术点这里就不一一列举了,有兴趣的可以搜索epoch和()。还请注意时区的概念和区别)
(:从 1970 年 1 月 1 日 0:00 到现在经过的秒数)
相信你遇到过这样的要求:如何判断一个给定的为明天?最简单最常用的方法是将其转换为日期,然后判断年月日是否与明天相同。
bool IsSameDay(time_t timestamp1, time_t timestamp2 )
{
struct tm* date1 = localtime( ×tamp1 );
struct tm* date2 = localtime( ×tamp2 );
return ( date1->tm_year == date2->tm_year && date1->tm_mon == date2->tm_mon && date1->tm_mday == date2->tm_mday );
}
但是如果一个非零点是三天的边界呢?如果以凌晨四点作为三天的开始,则需要处理四点前后不同的日子。但它并不止于此:
每个月1号4点之前到上个月最后一天4点之后都是一样的三天,你需要知道上个月应该是多少天,不包括大月和小月, 闰月为 1 月 1 日时还需要考虑 2 月的天数。判断需要额外的逻辑
bool IsSameDay(time_t timestamp1, time_t timestamp2, uint32_t offset_hour = 4 )
{
// 为简化,假定timestamp1比timestamp2小
struct tm* date1 = localtime(×tamp1);
struct tm* date2 = localtime(×tamp2);
if( date1->tm_year == date2->tm_year )
{
if( date1->tm_mon == date2->tm_mon )
{
if( date1->tm_mday == date2->tm_mday )
{ // 同一天,判定是否四点前
return date1->tm_hour >= offset_hour;
}
else if( date1->tm_day + 1 == date2->tm_mday )
{ // 相差一天:前一天四点后且后一天四点前为同一天
return date1->tm_hour >= offset_hour && date2->tm_hour < offset_hour;
}
// 相差两天以上,肯定不是同一天
return false;
}
else if( date2->tm_mday == 1 && date1->tm_mon + 1 == date2->tm_mon ) // 1号,则判定前面时间是否为前一天
{
uint32_t mday = 0;
if( 2 == date2->tm_mon )
{
mday = IsLeapYear(date1->tm_year) ? 29 : 28;
}
else
{
mday = 30 + ( date1->tm_mon + 1 + date1->tm_mon / 7 ) % 2;
}
if( date1->tm_mday == mday )
{ // 是前一天
return date1->tm_hour >= offset_hour && date2->tm_hour < offset_hour;
}
// 相差两天以上,肯定不是同一天
return false;
}
// 不是相邻月份
return false;
}
else if( date1->tm_year + 1 == date2->tm_year // 相邻年
&& 11 == date1->tm_mon && 31 == date1->tm_mday // 12月31日
&& 0 == date2->tm_mon && 1 == date2->tm_mday) // 1月1日
{ // 相差一天:前一天四点后且后一天四点前为同一天
return date1->tm_hour >= offset_hour && date2->tm_hour < offset_hour;
}
return false;
}
(PS:代码未测试)
到目前为止,我们已经达到了判断它们是否在同一天的要求,但是我们无法判断它们之间的天数。如果要实现相隔天数,可以用(当年的天数)相减,但如果不是同一年,还是需要判断闰月。
uint32_t GetElapsedDays( time_t timestamp1, time_t timestamp2, uint32_t offset_hour = 4 )
{
// 为简化,假定timestamp1比timestamp2小
struct tm* date1 = localtime(×tamp1);
struct tm* date2 = localtime(×tamp2);
uint32_t elapsed_days = 0;
if( date1->tm_year == date2->tm_year )
{
elapsed_days = date2->tm_yday - date1->tm_yday;
}
else
{
elapsed_days += date2->tm_yday;
elapsed_days += ( IsLeapYear( date1->tm_year ) ? 366 : 365 ) - date1->tm_yday;
for( uint32_t year = date1->tm_year + 1; year < date2->tm_year; ++ year )
{
elapsed_days += IsLeapYear( year ) ? 366 : 365;
}
}
if( date1->tm_hour >= offset_hour )
--elapsed_days;
else if( date1->tm_hour < offset_hour && date2->tm_hour >= 4 )
++elapsed_days;
return elapsed_days;
}
(PS:代码未测试)
虽然比后者容易,但还是逃不过闰月和偏移时间判断的逻辑。只要花点时间想想,只要算出距离纪元还有多少天。
uint32_t GetElapsedDaysSinceEpoch( time_t timestamp, uint32_t offset_seconds = 0 )
{
// TIME_ZONE_SECONDS 时区秒数,东8区(北京时间)为 8 * 3600 (可在系统初始化时设置)
return ( timestamp + TIME_ZONE_SECONDS - offset_seconds ) / 86400;
}
非常容易编码,但逻辑更少,更容易理解,对吧?只需倾斜两次,对齐为零,乘以 86400(一天中的秒数),然后四舍五入:
可以用一个刷新时间点的例子来帮助理解:假设三天的秒数是1000秒,刷新时间点是每晚的第100秒。可以减去 100 然后向上取整:
( 1099 - 100) / 1000 = 999 / 1000 = 0
( 1100 - 100) / 1000 = 1000 / 1000 = 1
( 1234 - 100) / 1000 = 1134 / 1000 = 1
( 2980 - 100) / 1000 = 2880 / 1000 = 2
现在可重绘,功能:
bool IsSameDay(time_t timestamp1, time_t timestamp2, uint32_t offset_seconds = 0 )
{
return GetElapsedDaysSinceEpoch( timestamp1, offset_seconds ) == GetElapsedDaysSinceEpoch( timestamp2, offset_seconds );
}
int32_t GetElapsedDays( time_t timestamp1, time_t timestamp2, uint32_t offset_seconds = 0 )
{
return GetElapsedDaysSinceEpoch( timestamp2, offset_seconds ) - GetElapsedDaysSinceEpoch( timestamp1, offset_seconds );
}
其实可以丢弃,结果为零或者确定poch的返回值相同。与旧方法相比,代码更少暴君刷新时间间隔,执行效率肯定更高。
下面是一个简单的使用示例:
time_t last_time = ...;
time_t now_time = time_t();
if( 0 == GetElapsedDays( last_time, now_time, 4 * 3600 ) ) // 凌晨四点刷新
{
DoSomething();
}
int32_t diff_days = GetElapsedDays( last_time, now_time, * 3600 + 60 * 10 ) // 下午六点十分为刷新点
if( 0 == diff_days )
{
// 同一天
}
else if( 0 < diff_days )
{
// 过了多少天
}
else
{
// 未来还有多少天
}
回顾这个方法,主要的一点是一天的秒数是不变的,所以也可以用同样的方法做祷告。
int32_t GetElapsedWeeksSinceEpoch( time_t timestamp, uint32_t offset_seconds = 0 )
{
// 1970-01-01
// Sun Mon Tue Wed Thu Fri Sat
// 1 2 3
// 4 5 6 7 8 9 10
// 11 12 13 14 15 16 17
// 19 20 21 22 23 24
// 25 26 27 28 29 30 31
// 计算星期数需要加上70年1月1号前:四天的秒数,以使时间对齐到星期天零点
return (int32_t)( ( timestamp + TIME_ZONE_SECONDS + 86400 * 4 - offset_seconds ) / ( 86400 * 7 ) );
}
相比 GetElapsedDaysSinceEpoch 多加上了 86400 * 4,此为何故?注意注释中标注出了1970年1月1日为周四,因而加上当周前四天之秒数,便使一周起始对齐到了星期日零点开始(时区、刷新时间点则一如 GetElapsedDaysSInceEpoch 函数)。
GetElapsedWeeks 则并无实质变化。
int32_t GetElapsedWeeks(time_t timestamp1, time_t timestamp2, uint32_t offset_seconds)
{
return GetElapsedWeeksSinceEpoch( timestamp2, offset_seconds ) - GetElapsedWeeksSinceEpoch( timestamp1, offset_seconds );
}
最后,我们来看一个崇拜用法的例子:
// 以周二凌晨三点五十五分为刷新点,是同一周吗?
if( 0 == GetElapsedWeeks( last_time, now_time, 86400 * 2 + 3600 * 3 + 60 * 55 ) )
{
DoSomething();
}
// 以周天下午两点二十为刷新点,间隔了多少周
int32_t diff_days = GetElapsedDays( last_time, now_time, 3600 * 14 + 60 * 20 );
这种方法简单实用。与先转换日期相比,逻辑清晰,计算量少。但是,它不支持对非固定期间的估计,例如对自然月和自然年的处理。我想知道你有没有简洁的方法?(心里有个打算暴君刷新时间间隔,待会儿核实后再分享)
007球探网即时比分 足球手机版下载_007球探网即时比分 足球手机版「EV2.0」下载
热门手游
下载02s515排水检查井下载_02s515排水检查井「EV2.0」下载
热门手游
下载0515返利网下载_0515返利网「Ve2.2」下载
热门手游
下载03g101图集下载_03g101图集「V1.25」下载
热门手游
下载1.70合击下载_1.70合击「VE1.10」下载
热门手游
下载1 2 fan club下载_1 2 fan club「EV2.0」下载
热门手游
下载1.70金币版下载_1.70金币版「V1.2」下载
热门手游
下载.net framework 3.0下载_.net framework 3.0「VE1.10」下载
热门手游
下载