微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

计算从纪元经过的天数 - 基于公式 使用mktime()计算闰日演示mktime()

如何解决计算从纪元经过的天数 - 基于公式 使用mktime()计算闰日演示mktime()

我正在尝试计算从给定的 GMT 时间过去的天数。

好吧,我可以使用迭代计算方法(找到正常年数 和闰年)

函数 get_number_of_leap_years_from_base_year 迭代从 1970 年到给定日期的所有年份,并检查每年是否有飞跃,最后添加所有天数。

是否有其他方法(公式)可以计算经过的正常年份和闰年数。

/* so-prg-2: Calculating number normal & leap years passed */

#include <stdio.h>
#include <string.h>
#include <time.h>

#define BASE_YEAR 1970

void print_time_readable_format(struct tm tm);
int convert_gmt_date_time_to_tm_format(char* gmt_time_fmt);
int get_number_of_leap_years_from_base_year(int start_year,int end_year);
int calculate_days_elapsed_from_epoch(struct tm tm);

int main()
{
    int days = 0;
    char gmt_time_fmt[] = "Dec 28 18:40:01 2020 GMT";
    //char gmt_time_fmt[] = "Jan 20 19:00:01 2019 GMT";
    //char gmt_time_fmt[] = "Dec 27 14:52:30 2020 GMT";
    //char gmt_time_fmt[] = "Jan 01 00:00:01 1970 GMT";
    days = convert_gmt_date_time_to_tm_format(gmt_time_fmt);

    printf("GMT = %s and days are %d\n",gmt_time_fmt,days);
    return 0;
}

int convert_gmt_date_time_to_tm_format(char* gmt_time_fmt)
{
    struct tm tm;
    char tm_time_fmt[255];

    //set tm struture to 0
    memset(&tm,sizeof(struct tm));
    // convert gmt_time_fmt to format required by 'tm' structure
    strptime(gmt_time_fmt,"%B %d %H:%M:%s %Y GMT",&tm);

    strftime(tm_time_fmt,sizeof(tm_time_fmt),"%s",&tm);
    printf("tm_time_fmt = %s\n",tm_time_fmt);

    print_time_readable_format(tm);
    return calculate_days_elapsed_from_epoch(tm);
}

int calculate_days_elapsed_from_epoch(struct tm tm)
{
    int days_by_month [2][12] = {
        /* normal years */
        { 0,31,59,90,120,151,181,212,243,273,304,334},/* leap years */
        { 0,60,91,121,152,182,213,244,274,305,335}
    };

    int current_year = tm.tm_year+1900;
    int total_years_passed = current_year - BASE_YEAR;
    /* -1,to skip the current year */
    int nleap_years_passed = get_number_of_leap_years_from_base_year(BASE_YEAR,current_year-1); 
    int normal_years = total_years_passed - nleap_years_passed;
    int total_days_passed = (normal_years*365 + nleap_years_passed*366 );
    
    printf(" **Years total_days_passed =%d\n",total_days_passed);
    
    total_days_passed += days_by_month[(current_year%4 == 0) - (current_year%100 == 0) + (current_year%400 == 0)][tm.tm_mon];
    total_days_passed += tm.tm_mday - 1; /* to skip the current day */
    
    printf(" **total_days_passed =%d\n",total_days_passed);
    return total_days_passed;
}

int get_number_of_leap_years_from_base_year(int start_year,int end_year)
{
    int leap_year_count = 0;
    int year = start_year;
    
    while( year <= end_year)
    {
        if( (year%4 == 0) - (year%100 == 0) + (year%400 == 0) )
            leap_year_count++;
        year++;
    }
    printf("leap_year_count = %d\n",leap_year_count);
    return leap_year_count;
}

void print_time_readable_format(struct tm tm)
{
    printf("tm.tm_year = %d ",tm.tm_year);
    printf("tm.tm_mon = %d ",tm.tm_mon);
    printf("tm.tm_mday = %d ",tm.tm_mday);
    printf("tm.tm_hour = %d ",tm.tm_hour); 
    printf("tm.tm_min = %d ",tm.tm_min );
    printf("tm.tm_sec = %d\n",tm.tm_sec );
}

解决方法

使用mktime()

由于您的代码可以同时使用标准 C strftime() 和 POSIX strptime(),因此也没有理由不使用标准 C mktime()

它给你一个 time_t 值,它是自大纪元以来的秒数。

int calculate_days_elapsed_from_epoch(struct tm tm)
{
    time_t t = mktime(&tm);
    return t / 86400;   // 24 * 60 * 60 = 86400
}

但如果目标是计算自 The Epoch 以来的秒数,您可以立即从 mktime() 获得答案。

请注意,mktime() 被传递了一个 struct tm 指针,它接受“超出范围”的值并对结果进行规范化。另请参阅“演示 mktime()”部分中的示例代码。

计算闰日

我有一个函数 jl_dmy_conversion() 潜伏在我的库中,它将年、月、日的组合转换为自 1899-12-31 以来的天数(所以在这个系统中,第 1 天是 1900-01- 01)。但它包括闰日数的计算。此代码位于包的内部,其中参数已在日期范围 0001-01-01 .. 9999-12-31 内验证为有效,因此它没有做太多保护自己免受无效数据的影响。还有另一个函数调用 this 来进行数据验证。此处显示的一些信息来自标题,大部分来自包含实现的源文件。

typedef int Date;

enum { DATE_NULL = -2147483648 };   /* Informix NULL DATE */

#define LEAPYEAR(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))

#define PRId_Date "d"

/*
** In 400 years,there are 97 leap years (because the three years
** divisible by 100 but not by 400 are not leap years).  This also
** happens to be exactly 20871 weeks.
*/
#define DAYS_IN_400_YEARS   (400*365+97)
#define DAYS_IN_2000_YEARS  (5*DAYS_IN_400_YEARS)

enum
{
    DAYS_IN_JANUARY   = 31,DAYS_IN_FEBRUARY  = 28,DAYS_IN_MARCH     = 31,DAYS_IN_APRIL     = 30,DAYS_IN_MAY       = 31,DAYS_IN_JUNE      = 30,DAYS_IN_JULY      = 31,DAYS_IN_AUGUST    = 31,DAYS_IN_SEPTEMBER = 30,DAYS_IN_OCTOBER   = 31,DAYS_IN_NOVEMBER  = 30,DAYS_IN_DECEMBER  = 31
};

static const int days_in_month[][2] =
{
    { 0,0                  },{ DAYS_IN_JANUARY,DAYS_IN_JANUARY    },{ DAYS_IN_FEBRUARY,DAYS_IN_FEBRUARY+1 },{ DAYS_IN_MARCH,DAYS_IN_MARCH      },{ DAYS_IN_APRIL,DAYS_IN_APRIL      },{ DAYS_IN_MAY,DAYS_IN_MAY        },{ DAYS_IN_JUNE,DAYS_IN_JUNE       },{ DAYS_IN_JULY,DAYS_IN_JULY       },{ DAYS_IN_AUGUST,DAYS_IN_AUGUST     },{ DAYS_IN_SEPTEMBER,DAYS_IN_SEPTEMBER  },{ DAYS_IN_OCTOBER,DAYS_IN_OCTOBER    },{ DAYS_IN_NOVEMBER,DAYS_IN_NOVEMBER   },{ DAYS_IN_DECEMBER,DAYS_IN_DECEMBER   }
};

/* Return date as number of days since 31st December 1899 - no range check */
static Date jl_dmy_conversion(int d,int m,int y)
{
    int             leap;
    int             i;
    Date            daynum;

    /* No need to assert here - calling functions have checked basics */
    DB_TRACE(1,"[[-- jl_dmy_conversion (d = %2d,m = %2d,y = %4d) ",d,m,y);

    leap = LEAPYEAR(y);
    if (d > days_in_month[m][leap])
    {
        DB_TRACE(1,"<<-- NULL (invalid day of month)\n");
        return(DATE_NULL);
    }

    /* Number of days so far this month */
    daynum = d;

    /* Days so far this year prior to this month */
    for (i = 1; i < m; i++)
        daynum += days_in_month[i][leap];
    DB_TRACE(4,"YDAY = %3ld ",daynum);

    /*
    ** Now compute number of days to 1st of January of this year.  Add
    ** 2000 years (5 periods of 400 years) to ensure that numbers
    ** resulting from subtraction are positive,even when dates back to
    ** 0001-01-01 are allowed,and then remove the number of days found
    ** in 2000 years.  This assumes int is 32-bit or longer.
    **
    ** NB: Things begin to go haywire when (y - 1901) yields -4,which
    ** is when y == 1897.  Things get worse before 1601.  The result is
    ** usually,but not always,off by one.  Adding 2000 years and then
    ** subtracting the appropriate number of days sidesteps the
    ** problems.
    */
    y += 2000;
    daynum += 365 * (y - 1900); /* Ignoring leap years */
    DB_TRACE(4,"INC1 = %7d ",365 * (y - 1900));
    daynum += (y - 1901) / 4;   /* Allowing for leap years */
    DB_TRACE(4,"INC2 = %4d ",(y - 1901) / 4);
    daynum -= (y - 1901) / 100; /* Allowing for non-leap years */
    DB_TRACE(4,"INC3 = %3d ",-(y - 1901) / 100);
    daynum += (y - 1601) / 400; /* Allowing for leap years */
    DB_TRACE(4,"INC4 = %2d ",(y - 1601) / 400);
    daynum -= DAYS_IN_2000_YEARS;
    DB_TRACE(1," (r = %7" PRId_Date ") --]]\n",daynum);

    return(daynum);
}

DB_TRACE 宏源自 #define a macro for debug printing in C? 中显示的代码。 DB_TRACE 宏在我在 GitHub 上的 SOQ(堆栈溢出问题)存储库中作为 src/libsoq 子目录中的文件 debug.cdebug.h 提供。 格式给出了一行显示计算步骤。

上面的代码编译时包含 debug.h 标头和 <stdio.h>,以及最小的 main(),加上与 debug.c 中的代码的链接:


int main(void)
{
    int dd = 28;
    int mm = 12;
    int yyyy = 2020;
    Date dt = jl_dmy_conversion(dd,mm,yyyy);
    printf("%.4d-%.2d-%.2d = %d\n",yyyy,dd,dt);
    return 0;
}

演示mktime()

如上所述,mktime() 被传递了一个 struct tm 指针,据记载,它接受“超出范围”的值并规范化结果——修改它传递的结构。它还设置 tm_wdaytm_yday 字段 - 将它们作为输入忽略。

如果您有 2020-12-28 08:20:26 的 struct tm 值,并且您想知道 6 天 18 小时 43 分 32 秒后的 time_t 值,你可以使用这样的代码:

#include <stdio.h>
#include <time.h>

static void print_time(time_t t,const struct tm *tm)
{
    char buffer[32];
    strftime(buffer,sizeof(buffer),"%Y-%m-%d %H:%M:%S %A",tm);
    printf("%lld: %s (%d)\n",(long long)t,buffer,tm->tm_yday);
}

int main(void)
{
   struct tm tm = { .tm_year = 2020 - 1900,.tm_mon = 12 - 1,.tm_mday = 28,.tm_hour = 8,.tm_min = 20,.tm_sec = 26 };
   time_t t0 = mktime(&tm);
   print_time(t0,&tm);
   tm.tm_mday += 6;
   tm.tm_hour += 18;
   tm.tm_min += 43;
   tm.tm_sec += 32;
   time_t t1 = mktime(&tm);
   print_time(t1,&tm);
   return 0;
}

运行时(在美国/山区标准时区 — UTC-7),它产生:

1609168826: 2020-12-28 08:20:26 Monday (362)
1609754638: 2021-01-04 03:03:58 Monday (3)
,

闰年的条件总结如下:

  • 闰年如果在 400 之前完全可见
  • 如果能被 100 整除,但不能被 400 整除,则不是闰年
  • 闰年如果不能被 100 整除但可以被 4 整除
  • 所有其他年份都不是闰年

所以逻辑可以表示为:

if (((year%4 == 0) && (year%100 != 0)) || (year%400 == 0))
{
    leap_year_count++;
}
year++;

但不确定重构现有逻辑是否会增加任何速度优势。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。