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 = 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…

猜你喜欢

转载自blog.csdn.net/qq153471503/article/details/128576442