C/C++ 实现时间戳和时间结构体的相互转换、格林威治与本地时间的转换
时间是具有周期性的,每间隔四年为一个闰年,时间戳是以1970/1/1 00:00:00开始到当前时间的秒数。
查看日历你会发现:
- 1970年为平年
- 1971年为平年
- 1972年为闰年
- 1973年为平年
四年加起来一共365*3+366=1461天。
这就是时间周期,后面写程序会用到。
时间结构与时间戳互转函数实现Demo如下:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
/*
* 一个自然周期为4年,包含3个平年一个闰年
*/
#define CYCLE_DAYS (1461U) /* (365 * 4 + 1) */
#define CYCLE_HOURS (35064U) /* (365 * 4 + 1) * 24 */
#define CYCLE_MINUTES (2103840U) /* (365 * 4 + 1) * 24 * 60 */
#define CYCLE_SECPNDS (126230400U) /* (365 * 4 + 1) * 24 * 60 * 60 */
#define TIMEZONE (28800UL) /* 8个小时的秒数。东八区快8个小时,东正西负 */
typedef struct
{
int year; /* 年,四位数年份 */
int mon; /* 月,范围1~12 */
int mday; /* 日,范围1~31 */
int hour; /* 时,范围1~24 */
int min; /* 分,范围1~59 */
int sec; /* 秒,范围1~59 */
int wday; /* 周几,范围0~6,0表示星期天 */
int yday; /* 一年中的第几天,范围1~366 */
}ctime_t;
/**
* 根据传入的日期计算星期几,返回值范围0~6,0表示星期天
*/
uint8_t WeekDayNum(uint32_t nYear, uint8_t nMonth, uint8_t nDay)
{
uint32_t weekday = 0U;
if (nMonth < 3U)
{
/*D = { [(23 x month)/9] + day + 4 + year + [(year-1)/4] - [(year-1)/100] + [(year-1)/400] } mod 7*/
weekday = (((23U * nMonth) / 9U) + nDay + 4U + nYear + ((nYear - 1U) / 4U) - ((nYear - 1U) / 100U) + ((nYear - 1U) / 400U)) % 7U;
}
else
{
/*D = { [(23 x month)/9] + day + 4 + year + [year/4] - [year/100] + [year/400] - 2 } mod 7*/
weekday = (((23U * nMonth) / 9U) + nDay + 4U + nYear + (nYear / 4U) - (nYear / 100U) + (nYear / 400U) - 2U) % 7U;
}
return (uint8_t)weekday;
}
/**
* 将时间戳转成ctime_t结构
*/
void timepack(uint32_t timestamp, ctime_t* t)
{
uint32_t year,mon,mday,hour,min,sec,yday,wday,dayOfCycle,i,cycle,day=0;
uint8_t days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
sec = timestamp % 60; /* 秒 */
timestamp /= 60;
min = timestamp % 60; /* 分 */
timestamp /= 60;
hour = timestamp % 24; /* 时 */
timestamp /= 24;
cycle = timestamp / CYCLE_DAYS; /* 自1970年到现在已经过去的周期数(一个周期为4年) */
dayOfCycle = timestamp % CYCLE_DAYS; /* 本周期内已过的天数 */
if(dayOfCycle < 365) /* 周期内的第一年为平年 */
{
year = 1970 + cycle * 4; /* 年 */
yday = dayOfCycle + 1; /* 本年度的第几天 */
}else if(dayOfCycle >= 365 && dayOfCycle < 730) /* 周期内的第二年为平年 */
{
year = 1970 + cycle * 4 + 1;
yday = dayOfCycle - 365 + 1;
}else if(dayOfCycle >= 760 && dayOfCycle < 1096) /* 周期内的第三年为闰年 */
{
year = 1970 + cycle * 4 + 2;
yday = dayOfCycle - 760 + 1;
}else /* 周期内的第四年为平年 */
{
year = 1970 + cycle * 4 + 3;
yday = dayOfCycle - 1096 + 1;
}
if ((year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0))) /* 判断是否是闰年 */
days[1] = 29;
for(i = 0; i < 12; i++)
{
day += days[i];
if(day >= yday)
{
mon = i + 1; /* 月 */
mday = days[i] - (day - yday); /* 日 */
break;
}
}
wday = WeekDayNum(year, mon, mday); /* 周几 */
if(t != NULL)
{
t->year = year;
t->mon = mon;
t->mday = mday;
t->hour = hour;
t->min = min;
t->sec = sec;
t->wday = wday;
t->yday = yday;
}
}
/**
* 将ctime_t结构还原成时间戳
*/
uint32_t timeunpack(const ctime_t *t)
{
uint32_t i = 0, days = 0, timestamp = 0;
uint8_t mdays[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
for(i = 1970; i < t->year; i++)
{
if((i % 400 == 0) || ((i % 4 == 0) && (i % 100 != 0))) /* 判断是否是闰年 */
days += 366;
else
days += 365;
}
if((t->year % 400 == 0) || ((t->year % 4 == 0) && (t->year % 100 != 0))) /* 判断是否是闰年 */
mdays[1] = 29;
for(i = 0; i < t->mon - 1; i++)
days += mdays[i];
days += (t->mday - 1);
timestamp = days * 24 * 60 * 60;
timestamp += t->hour * 60 * 60;
timestamp += t->min * 60;
timestamp += t->sec;
return timestamp;
}
int main(void)
{
ctime_t t;
time_t timestamp;
static const char* wday[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
/* 获取时间戳,time函数返回的是自<1970/01/01 00:00:00>的秒数 */
timestamp = time(NULL);
printf("timestamp = %ld \n", timestamp);
fflush(stdout);
/* 北京时间为东八区,比UTC时间快了正好8小时,所以需要加上8个小时的秒数 */
timestamp += TIMEZONE;
/* 时间戳转成时间结构 */
timepack(timestamp, &t);
printf("%04d/%02d/%02d %02d:%02d:%02d %s \n",
t.year,
t.mon,
t.mday,
t.hour,
t.min,
t.sec,
wday[WeekDayNum(t.year, t.mon, t.mday)]);
fflush(stdout);
/* 测试将时间结构转回时间戳 */
timestamp = timeunpack(&t) - TIMEZONE;
printf("timestamp = %ld \n", timestamp);
fflush(stdout);
return 0;
}
运行结果:
ends…