C语言实现的 mktime()接口

         最近使用stm32F40x做一个项目,遇到比较蛋疼的一件事,同一份代码,在两个不同的环境下(Linux,windows下的keil5)编译出来的固件,烧录到板上,系统时间会自动改变,使用Linux平台编译的固件,系统时间总是比使用keil5编译出来的固件少1天,折腾了很久,最后发现,从RTC寄存器里面读出来的时间,两者是一致的,在调mktime()生成时间戳后就不一致了,那么问题就是出现在这个mktime()上面,因为都是调底层C库里面的mktime(),没法看到它们的实现方式,而且Linux版本的固件已经有产品在用户手上了,没办法,所以只能在keil版本上修改,以便与Linux版本保持一致,于是在网上找了好几种实现方式,但还是没能达到预期的效果,于是就自己重新实现了mktime();

1、首先定义tm结构体:(其实这个结构体我直接使用系统里面的定义)

struct tm {
	int tm_sec;			    /* Seconds.	[0-60] (1 leap second) */
	int tm_min;			    /* Minutes.	[0-59] */
	int tm_hour;		    /* Hours.	[0-23] */
	int tm_mday;		    /* Day.		[1-31] */
	int tm_mon;			    /* Month.	[0-11] */
	int tm_year;			/* Year - 1900. */
	int tm_wday;			/* Day of week.	[0-6] */
	int tm_yday;			/* Days in year.[0-365]	*/
	int tm_isdst;			/* DST.		[-1/0/1]*/

	long int tm_gmtoff;		/* Seconds east of UTC.  */
	const char *tm_zone;	/* Timezone abbreviation.  */
};

2、重新定义time_t ;

typedef unsigned long time_t;

3、声明一个当前月中所经过的天数数组

const short __mday[13] =
 {  0,
    (31),
    (31+28),
    (31+28+31),
    (31+28+31+30),
    (31+28+31+30+31),
    (31+28+31+30+31+30),
    (31+28+31+30+31+30+31),
    (31+28+31+30+31+30+31+31),
    (31+28+31+30+31+30+31+31+30),
    (31+28+31+30+31+30+31+31+30+31),
    (31+28+31+30+31+30+31+31+30+31+30),
    (31+28+31+30+31+30+31+31+30+31+30+31),
};

4、写一个判断是否是闰年的接口

int __isleap(int year)
{
    return (!(year % 4) && ((year % 100) || !(year % 400)));
}

5、实现mymktime()接口

time_t mymktime(struct tm *const t) 
{
	time_t  day;
	time_t  i;
	time_t years = t->tm_year - 70;

	if (t->tm_sec > 60) {
		t->tm_min += t->tm_sec/60;
		t->tm_sec %= 60; 
	}
	
	if (t->tm_min > 60) { 
		t->tm_hour += t->tm_min / 60;
		t->tm_min %= 60; 
	}
	
	if (t->tm_hour>24) { 
		t->tm_mday += t->tm_hour / 24; 
		t->tm_hour %= 24; 
	}
	
	if (t->tm_mon > 12) { 
		t->tm_year += t->tm_mon / 12; 
		t->tm_mon %= 12; 
	}
	
	while (t->tm_mday > __mday[1+t->tm_mon]) {
		if (t->tm_mon == 1 && __isleap(t->tm_year + 1900)) {
		  	--t->tm_mday;
		}
		t->tm_mday -= __mday[t->tm_mon];
		++t->tm_mon;
		
		if (t->tm_mon>11) {
			t->tm_mon=0; 
			++t->tm_year; 
		}
	}

	if (t->tm_year < 70)
		return (time_t) -1;

	/* 1970年以来的天数等于365 *年数+ 1970年以来的闰年数 */
	day  = years * 365 + (years + 1) / 4;

	/* 2100年以后,计算闰年的方式不一样了,每400年减去3个闰年,大多数mktime实现不支持2059年后的日期,所以可以把这个省略掉 */
	if ((int)(years -= 131) >= 0) {
		years /= 100;
		day -= (years >> 2) * 3 + 1;
	
		if ((years &= 3) == 3) 
			years--;
		
		day -= years;
	}

	day += t->tm_yday = __mday [t->tm_mon] + t->tm_mday-1 + ( __isleap (t->tm_year + 1900)  &  (t->tm_mon > 1) );

	/* 现在是自1970年1月1日以来的天数 */
	i = 7;
	t->tm_wday = (day + 4) % i;      /* 星期天=0, 星期一=1, ..., 星期六=6 */

	i = 24;
	day *= i;
	i = 60;
	return ((day + t->tm_hour) * i + t->tm_min) * i + t->tm_sec;
}

6、测试

void show_system_time(void)
{
	struct tm dt;
	utime_t utime;
	RTC_TimeTypeDef RTC_TimeStructure;	
	RTC_DateTypeDef RTC_DateStructure;		
		
	RTC_GetTime(RTC_Format_BIN, &RTC_TimeStructure);
	RTC_GetDate(RTC_Format_BIN, &RTC_DateStructure);

	dt.tm_sec = RTC_TimeStructure.RTC_Seconds;
	dt.tm_min = RTC_TimeStructure.RTC_Minutes;
	dt.tm_hour = RTC_TimeStructure.RTC_Hours;
	dt.tm_mon = RTC_DateStructure.RTC_Month -1;
	dt.tm_mday = RTC_DateStructure.RTC_Date;
	dt.tm_year = RTC_DateStructure.RTC_Year + 100;

	utime = (utime_t)mymktime(&dt);
	printf("utime: %d \r\n",utime);
	
	return ;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
`mktime` 函数是 C 标准库中的函数,其实现比较复杂,需要考虑多个因素,比如闰年、夏令时、时区等。以下是一个简化版的 `mktime` 函数实现,仅考虑了不跨越夏令时和非闰年的情况: ```c #include <time.h> #define SECS_PER_DAY 86400 #define SECS_PER_HOUR 3600 #define SECS_PER_MINUTE 60 time_t my_mktime(struct tm *timeptr) { int year, month, day, hour, minute, second; int days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // 获取年、月、日、时、分、秒 year = timeptr->tm_year + 1900; month = timeptr->tm_mon + 1; day = timeptr->tm_mday; hour = timeptr->tm_hour; minute = timeptr->tm_min; second = timeptr->tm_sec; // 计算天数 int days = (year - 1970) * 365; for (int i = 1970; i < year; i++) { if ((i % 4 == 0 && i % 100 != 0) || i % 400 == 0) { days++; } } for (int i = 1; i < month; i++) { days += days_in_month[i - 1]; } if (month > 2 && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)) { days++; } days += day - 1; // 计算秒数 time_t seconds = days * SECS_PER_DAY + hour * SECS_PER_HOUR + minute * SECS_PER_MINUTE + second; return seconds; } ``` 以上代码中,我们使用了一个 `days_in_month` 数组来保存每个月的天数,然后根据年、月、日计算出总共的天数,再将其转换为秒数即可。需要注意的是,由于 `struct tm` 结构体中的年份是从 1900 年开始计算的,因此需要将其加上 1900。此外,由于计算过程中没有考虑夏令时、时区等因素,因此在实际使用时可能会存在一定的误差。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

y20082478

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值