如何解决计算从纪元经过的天数 - 基于公式 使用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.c
和 debug.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_wday
和 tm_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 举报,一经查实,本站将立刻删除。