0
点赞
收藏
分享

微信扫一扫

C/C++ 实现时间戳和时间结构体的相互转换、格林威治与本地时间的转换


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;
uint32_t timestamp = 0;
uint32_t yday = t->mday - 1;
uint32_t year = t->year - 1970;
uint8_t days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

if ((t->year % 400 == 0) || ((t->year % 4 == 0) && (t->year % 100 != 0))) /* 判断是否是闰年 */
{
days[1] = 29;
}

for(i = 0; i < t->mon - 1; i++)
{
yday += days[i];
}

/* 加秒 */
timestamp += t->sec;

/* 加分钟 */
timestamp += (t->min * 60);

/* 加小时 */
timestamp += (t->hour * 60 * 60);

/* 加本年度已过的天数(转小时数) */
timestamp += (yday * 24 * 60 * 60);

/* 加从1970年已经过的年数(转小时数) */
timestamp += (((year >> 2) * CYCLE_HOURS) * 60 * 60);

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;
}

运行结果:

C/C++ 实现时间戳和时间结构体的相互转换、格林威治与本地时间的转换_2d

ends…


举报

相关推荐

0 条评论