计算从纪元开始经过的天数 - 基于公式
Calculate number of days elapsed from epoch - formula based
我正在尝试计算从给定 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, 0, 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, 31, 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 );
}
闰年的条件总结为:
- 闰年如果在 400 之前完全可见
- 如果 100 可见但不能被 400 整除,则不是闰年
- 如果不能被 100 整除但能被 4 整除则为闰年
- 其他年份都不是闰年
所以逻辑可以表示为:
if (((year%4 == 0) && (year%100 != 0)) || (year%400 == 0))
{
leap_year_count++;
}
year++;
但不确定重构现有逻辑是否会增加任何速度优势。
使用mktime()
因为您的代码允许同时使用标准 C strftime()
and POSIX strptime()
, there's no reason not to use Standard 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
}
但如果目标是计算自大纪元以来的秒数,您可以立即从 mktime()
中得到答案。
请注意,mktime()
传递了一个 struct tm
指针,它接受 'out of range' 的值并对结果进行规范化。另请参阅 'Demonstrating mktime()
'.
部分中的示例代码
计算闰日
我有一个函数 jl_dmy_conversion()
潜伏在我的库中,它将年、月、日的组合转换为自 1899-12-31 以来的天数(所以在这个系统中,第 1 天是 1900- 01-01).但它包括对闰日数的计算。此代码是包的内部代码,其中参数已在日期范围 0001-01-01 .. 9999-12-31 内被验证为有效,因此它并不能保护自己免受无效数据的影响。还有另一个函数调用它来进行数据验证。此处显示的一些信息来自 header,大部分来自包含实现的源文件。
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?. The DB_TRACE
macro is available in my SOQ (Stack Overflow Questions) repository on GitHub as files debug.c
and debug.h
in the src/libsoq sub-directory 中显示的代码派生而来的。
格式给出了一行显示计算步骤。
上面的代码编译时包含 debug.h
header 和 <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, mm, dd, dt);
return 0;
}
示范[=15=]
如上所述,mktime()
被传递了一个 struct tm
指针,据记载它接受 'out of range' 的值并对结果进行规范化——修改它传递的结构.它还设置 tm_wday
和 tm_yday
字段——它忽略它们作为输入。
如果您有 2020-12-28 的 struct tm
值 08:20:26 并且您想知道 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;
}
当运行(在US/Mountain标准时区——UTC-7)时,它产生:
1609168826: 2020-12-28 08:20:26 Monday (362)
1609754638: 2021-01-04 03:03:58 Monday (3)
我正在尝试计算从给定 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, 0, 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, 31, 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 );
}
闰年的条件总结为:
- 闰年如果在 400 之前完全可见
- 如果 100 可见但不能被 400 整除,则不是闰年
- 如果不能被 100 整除但能被 4 整除则为闰年
- 其他年份都不是闰年
所以逻辑可以表示为:
if (((year%4 == 0) && (year%100 != 0)) || (year%400 == 0))
{
leap_year_count++;
}
year++;
但不确定重构现有逻辑是否会增加任何速度优势。
使用mktime()
因为您的代码允许同时使用标准 C strftime()
and POSIX strptime()
, there's no reason not to use Standard 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
}
但如果目标是计算自大纪元以来的秒数,您可以立即从 mktime()
中得到答案。
请注意,mktime()
传递了一个 struct tm
指针,它接受 'out of range' 的值并对结果进行规范化。另请参阅 'Demonstrating mktime()
'.
计算闰日
我有一个函数 jl_dmy_conversion()
潜伏在我的库中,它将年、月、日的组合转换为自 1899-12-31 以来的天数(所以在这个系统中,第 1 天是 1900- 01-01).但它包括对闰日数的计算。此代码是包的内部代码,其中参数已在日期范围 0001-01-01 .. 9999-12-31 内被验证为有效,因此它并不能保护自己免受无效数据的影响。还有另一个函数调用它来进行数据验证。此处显示的一些信息来自 header,大部分来自包含实现的源文件。
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?. The DB_TRACE
macro is available in my SOQ (Stack Overflow Questions) repository on GitHub as files debug.c
and debug.h
in the src/libsoq sub-directory 中显示的代码派生而来的。
格式给出了一行显示计算步骤。
上面的代码编译时包含 debug.h
header 和 <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, mm, dd, dt);
return 0;
}
示范[=15=]
如上所述,mktime()
被传递了一个 struct tm
指针,据记载它接受 'out of range' 的值并对结果进行规范化——修改它传递的结构.它还设置 tm_wday
和 tm_yday
字段——它忽略它们作为输入。
如果您有 2020-12-28 的 struct tm
值 08:20:26 并且您想知道 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;
}
当运行(在US/Mountain标准时区——UTC-7)时,它产生:
1609168826: 2020-12-28 08:20:26 Monday (362)
1609754638: 2021-01-04 03:03:58 Monday (3)