最近在学习用esp-idf给ESP32写代码。打算写一个NTP授时功能的时候发现要自己解析时间戳。在网上搜了一下,发现大佬们都是按照每四年一闰年的方法来判断是否是闰年。但是这个方法并不准确。
四年一闰,百年不闰,四百年再闰。例如:2000年是闰年,2100年则是平年。——摘自百度百科
考虑到这个问题,所以自己重新写了一个算法(就是不知道有没有大佬也写个这个算法hhh),通过循环来判断是否为闰年。虽然在速度上稍微慢一点但至少会更严谨一些。
#include <stdio.h>
#include <time.h>
#include <stdbool.h>
#include <windows.h>
const long DAY_SEC = 86400;//一天中的秒数
#define my_timezone 8
#define start_year 2021
#define isLeap false
#define start_day 5
const long TO2021_SEC = 1609459200;//1970-1-1到2021-1-1的秒数
int nor_month[] = {31,28,31,30,31,30,31,31,30,31,30,31};
int leap_month[] = {31,29,31,30,31,30,31,31,30,31,30,31};
struct {
int year;
int month;
int date;
int hour;
int min;
int sec;
int day;
bool isLeapYear;
}my_time;
void getTime(unsigned long timestamp);
bool isLeapYear(int year);
int main() {
while(1){
unsigned long timestamp = time(NULL);
//scanf("%ul",×tamp);
printf("时间戳是:%u\n",timestamp);
getTime(timestamp);
printf("%d-%d-%d %02d:%02d:%02d %d %d \n",
my_time.year,
my_time.month,
my_time.date,
my_time.hour,
my_time.min,
my_time.sec,
my_time.isLeapYear,
my_time.day);
Sleep(1000);
}
}
bool isLeapYear(int year)
{
if(year%400 == 0)
return true;
else if(year%4 == 0 && year%100 !=0)
return true;
return false;
}
void getTime(unsigned long timestamp)
{
timestamp += my_timezone * 3600;//时区偏移
timestamp -= TO2021_SEC;//预处理
//时分秒
my_time.sec = timestamp % 60;//算出没凑够一分钟的秒数
my_time.min = timestamp % 3600 / 60;//算出没凑够一小时的秒数再除以一分钟的秒数
my_time.hour = timestamp % DAY_SEC / 3600;//算出没凑够一天的秒数再除以一小时的秒数
//
my_time.year = start_year;
my_time.isLeapYear = isLeap;
my_time.month = 1;//一开始是1月
//
long nDays = timestamp / DAY_SEC ;
int tem = nDays % 7;//没凑够一周的天数
my_time.day = start_day + tem;//当时是星期五
if(my_time.day > 7)
my_time.day -= 7;
//
while(nDays > 365)//经过多少年就减去对应的天数
{
my_time.year++;
if(isLeapYear(my_time.year))
{
nDays -= 366;
my_time.isLeapYear = true;
}
else
{
nDays -= 365;
my_time.isLeapYear = false;
}
}
int *month;//根据闰年选择是有29的还是普通月份
if(my_time.isLeapYear)
{
month = leap_month;
}
else
{
month = nor_month;
}
for(int i = 0;i < 12; i++)
{
if(nDays > month[i])//如果剩余的天数比该月份的上限天数要大
{
my_time.month++;//说明至少月份是下一个月的
nDays -= month[i];
}
else//否则就是该月份的
{
break;
}
}
my_time.date = nDays + 1;//剩余的天数就是经过多少天,一开始是1要加上
}
为了减少循环的次数我直接从2021-1-1开始计算,所以直接减去了从1970-1-1到2021-1-1的秒数。所以说循环次可能也不会很多,如果感觉年份靠后了的话也可以改一开始的几个定义,把时间往后推。
代码在Clion上运行过是没有问题的,但就是不知道有没有特定的时间点会出错。如果代码有BUG 的话欢迎各位大佬指出。如果觉得我代码不规范的也可以吐槽一下我不听