方法一:在网络上很多人用这种4年为一周期,有点刻意而为之(另:好多人贴的代码闰年都理解错了!!!),而且只适用2038之前,有点死板
方法二:方法虽然有点笨,但是很准确啦,就是从1970年试了,逼近法。无外乎时间比第一种理论上多了那么一点,但这种时间差距应该很小,可以忽略
大家可以多多测试一下,奇数年,偶数年,平年,闰年,闰月等等,亲测准确。
方法一:代码示例
此代码之前的错误版本还在网上大面积存留。请谨慎!!!!
我就是用的那个错误的,测试发现问题后找到如下正确版本
/****************************************************************** //版本记录: // v1.初始化版本。 --19.05.23 // v2.1.经网友报错,改正一处逻辑错误。 --20.11.08 ******************************************************************/ #include <stdio.h> #include <string.h> #include "stdint.h" #define FOURYEARDAY (365+365+365+366) //4年一个周期内的总天数(1970~2038不存在2100这类年份,故暂不优化) #define TIMEZONE (8) //北京时区调整 typedef struct rtc_time_struct { uint16_t ui8Year; // 1970~2038 uint8_t ui8Month; // 1~12 uint8_t ui8DayOfMonth; // 1~31 uint8_t ui8Week; uint8_t ui8Hour; // 0~23 uint8_t ui8Minute; // 0~59 uint8_t ui8Second; // 0~59 }rtc_time_t; static uint8_t month_day[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; //平年 static uint8_t Leap_month_day[12]={31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; //闰年 //const uint16_t dayPerYear[4] = {365, 365, 365, 366}; // 判断是否是闰年 // year: 需要判断的年 // return:1:闰年 // 0: 平年 uint8_t isLeapYear(uint16_t year) { uint8_t res=0; if(year%4 == 0) // 能够被4整除 { if((year%100 == 0) && (year%400 != 0)) //能够被100整除,但是不能够被400整除 { res = 0; } else { res =1; } } return res; } // 将Unix时间戳转换为北京时间 // unixTime: 需要判断的Unix时间戳 // *tempBeijing:返回的北京时间 // return:none // note:没对输入参数正确性做判断 void covUnixTimeStp2Beijing(uint32_t unixTime, rtc_time_t *tempBeijing) { uint32_t totleDayNum=0, totleSecNum=0; uint16_t remainDayofYear=0, tempDay=0, tempYear=0; uint8_t *pr=NULL; totleDayNum = unixTime/(24*60*60); //总天数(注意加括号) totleSecNum = unixTime%(24*60*60); //当天剩余的秒速 memset(tempBeijing, 0x00, sizeof(rtc_time_t)); // 1.先计算时间 HH:MM:SS tempBeijing->ui8Hour = totleSecNum/3600; tempBeijing->ui8Minute = (totleSecNum%3600)/60; //error:变量搞错 tempBeijing->ui8Second = (totleSecNum%3600)%60; // 2.对时间进行时区调整(注意:这里可能造成日期 +1) tempBeijing->ui8Hour +=TIMEZONE; if(tempBeijing->ui8Hour>23){ //printf("modify day..\n"); tempBeijing->ui8Hour -= 24; remainDayofYear++; // 日期+1 } // 3.计算哪一年 tempBeijing->ui8Year = 1970 + (totleDayNum / FOURYEARDAY) * 4; // 4年为一个周期 remainDayofYear += totleDayNum % FOURYEARDAY; //printf("year:%d, day:%d.\n", tempBeijing->ui8Year, remainDayofYear); tempYear = isLeapYear(tempBeijing->ui8Year)?366:365; while(remainDayofYear >= tempYear) // 计算4年整数倍外的年。 { tempBeijing->ui8Year++; remainDayofYear -= tempYear; tempYear = isLeapYear(tempBeijing->ui8Year)?366:365; } // 4.计算哪一月的哪一天 pr = isLeapYear(tempBeijing->ui8Year)?Leap_month_day:month_day; remainDayofYear++; // 这里开始计算具体日期。remainDayofYear为 0 时其实是 1 号,所以这里要 +1 while(remainDayofYear > *(pr+tempBeijing->ui8Month)) { remainDayofYear -= *(pr+tempBeijing->ui8Month); tempBeijing->ui8Month++; } //printf("year:%d, day:%d.\n", tempBeijing->ui8Year, remainDayofYear); tempBeijing->ui8Month++; //month tempBeijing->ui8DayOfMonth = remainDayofYear; //day //printf("year:%d, day:%d.\n", tempBeijing->ui8Year, tempBeijing->ui8DayOfMonth); } // 将北京时间转换为Unix时间戳 // year: 需要判断的年 // return:Unix时间戳(从1970/1/1 00:00:00 到现在的秒数) // note:没对输入参数正确性做判断 uint32_t covBeijing2UnixTimeStp(rtc_time_t *beijingTime) { uint32_t daynum=0, SecNum=0; //保存北京时间到起始时间的天数 uint16_t tempYear=1970, tempMonth=0; //1.年的天数 while(tempYear < beijingTime->ui8Year) { if(isLeapYear(tempYear)){ daynum += 366; } else{ daynum += 365; } tempYear++; } //2.月的天数 while(tempMonth < beijingTime->ui8Month-1) { if(isLeapYear(beijingTime->ui8Year)){ //闰年 daynum += Leap_month_day[tempMonth]; } else{ daynum += month_day[tempMonth]; } tempMonth++; } //3.天数 daynum += (beijingTime->ui8DayOfMonth-1); //4.时分秒 SecNum = daynum*24*60*60; //s SecNum += beijingTime->ui8Hour*60*60; SecNum += beijingTime->ui8Minute*60; SecNum += beijingTime->ui8Second; //5.时区调整 SecNum -= TIMEZONE*60*60; return SecNum; } //测试主函数 int main() { rtc_time_t testTime; uint32_t UnixTimsStamp=0; covUnixTimeStp2Beijing(1613806557, &testTime); printf("%u convert is: %d/%02d/%02d-%02d:%02d:%02d\n", UnixTimsStamp, testTime.ui8Year, testTime.ui8Month, testTime.ui8DayOfMonth, \ testTime.ui8Hour, testTime.ui8Minute, testTime.ui8Second); }
方法二:
/************************************************************************************* ************************************************************************************** ************************ unix timestamp to time string [begin] *********************** ************************************************************************************** ***************************************************************************************/ #include <stdio.h> #define UTC_BASE_YEAR 1970 #define MONTH_PER_YEAR 12 #define DAY_PER_YEAR 365 #define SEC_PER_DAY 86400 #define SEC_PER_HOUR 3600 #define SEC_PER_MIN 60 typedef struct{ int Year; char Month; char DayOfMonth; char Weekday; char Hour; char Minute; char Second; }nwy_time_type_t; /* 每个月的天数 */ const unsigned char g_day_per_mon[MONTH_PER_YEAR] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; /* * 功能: * 判断是否是闰年 * 参数: * year:需要判断的年份数 * * 返回值: * 闰年返回1,否则返回0 */ static unsigned char applib_dt_is_leap_year(unsigned short year) { if ((year % 400) == 0) { return 1; } else if ((year % 100) == 0) { return 0; } else if ((year % 4) == 0) { return 1; } else { return 0; } } /* * 功能: * 得到每个月有多少天 * 参数: * month:需要得到天数的月份数 * year:该月所对应的年份数 * * 返回值: * 该月有多少天 * */ static unsigned char applib_dt_last_day_of_mon(unsigned char month, unsigned short year) { if ((month == 0) || (month > 12)) { return g_day_per_mon[1] + applib_dt_is_leap_year(year); } if (month != 2) { return g_day_per_mon[month - 1]; } else { return g_day_per_mon[1] + applib_dt_is_leap_year(year); } } static void nwy_cov_unixtimestp_2_utc(int ts, int timezone, nwy_time_type_t *tempUTC) { int year = 0; int month = 0; int day = 0; int hour = 0; int minute = 0; int second = 0; ts += timezone*60*60;//时区调整 //将时间戳值转化成天数。通过天数可以比较方便地算出年、月、日。 int days = ts / SEC_PER_DAY; //1.计算哪一年 int yearTmp = 0; int dayTmp = 0; //使用夹逼法计算 days 天中包含的年数。 for (yearTmp = UTC_BASE_YEAR; days > 0; yearTmp++) { dayTmp = (DAY_PER_YEAR + applib_dt_is_leap_year(yearTmp)); //这一年有多少天? if (days >= dayTmp) //条件成立,则 yearTmp 即是这个时间戳值所代表的年数。 { days -= dayTmp; } else { break; } } year = yearTmp; //2.计算哪一月的哪一天 int monthTmp = 0; for (monthTmp = 1; monthTmp < MONTH_PER_YEAR; monthTmp++) { dayTmp = applib_dt_last_day_of_mon(monthTmp, year); if (days >= dayTmp) { days -= dayTmp; } else { break; } } month = monthTmp; day = days + 1; //转化成秒。 int secs = ts % SEC_PER_DAY; //这个时间戳值的小时数。 hour = secs / SEC_PER_HOUR; //这个时间戳值的分钟数。 secs %= SEC_PER_HOUR; minute = secs / SEC_PER_MIN; //这个时间戳的秒钟数。 second = secs % SEC_PER_MIN; tempUTC->Year = year; tempUTC->Month = month; tempUTC->DayOfMonth = day; tempUTC->Hour = hour; tempUTC->Minute = minute; tempUTC->Second = second; printf("time : %d \n",ts); printf("time_to : %d-%02d-%02d,%02d:%02d:%02d \n",tempUTC->Year,tempUTC->Month,tempUTC->DayOfMonth, tempUTC->Hour, tempUTC->Minute, tempUTC->Second); } void main() { nwy_time_type_t tempUTC; nwy_cov_unixtimestp_2_utc(1582184157,8,&tempUTC); } /************************************************************************************* ************************************************************************************** ************************ unix timestamp to time string [ end ] *********************** ************************************************************************************** ***************************************************************************************/