所在位置:首页 > 手游攻略 > 如何给定为今日?看完这篇文章就够了

如何给定为今日?看完这篇文章就够了

发布时间: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( &timestamp1 );
    struct tm* date2 = localtime( &timestamp2 );
    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(&timestamp1);
    struct tm* date2 = localtime(&timestamp2);
    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(&timestamp1);
    struct tm* date2 = localtime(&timestamp2);
    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;
        }
    }

激战2世界boss刷新时间_24人火炮兰刷新时间_暴君刷新时间间隔

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,此为何故?注意注释中标注出了197011日为周四,因而加上当周前四天之秒数,便使一周起始对齐到了星期日零点开始(时区、刷新时间点则一如 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 );

这种方法简单实用。与先转换日期相比,逻辑清晰,计算量少。但是,它不支持对非固定期间的估计,例如对自然月和自然年的处理。我想知道你有没有简洁的方法?(心里有个打算暴君刷新时间间隔,待会儿核实后再分享)