C语言如何实现Unix时间戳与本地时间转化
前言
我们平常说时间都说的几点几分几秒,星期几,但是在计算机里面并不是直接使用我们所说的时间,而是使用Unix时间戳,这样不管是哪个平台,哪个系统,都可以根据自己对时间的定义进行转换,像Java,PHP等都提供了接口来进行转化,C库里面也有这样的函数,那具体是怎么实现的呢?要了解这个问题首先我们就必须要清楚什么是Unix时间戳,什么是我们平常使用的时间。
1. Unix时间戳
UNIX时间戳:Unix时间戳(英文为Unix epoch, Unix time, POSIX time 或 Unix timestamp)
是从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数,不考虑闰秒。
UNIX时间戳的0按照ISO 8601规范为 :1970-01-01T00:00:00Z.
一个小时表示为UNIX时间戳格式为:3600秒;一天表示为UNIX时间戳为86400秒,闰秒不计算。
在大多数的UNIX系统中UNIX时间戳存储为32位,这样会引发2038年问题或Y2038。
对应的时间和秒数如下
时间 | 秒 |
---|---|
1 分钟 | 60 秒 |
1 小时 | 3600 秒 |
1 天 | 86400 秒 |
1 周 | 604800 秒 |
1 月 (30.44 天) | 2629743 秒 |
1 年 (365.24 天) | 31556926 秒 |
2. 本地时间
平常我们最常听见的是阴历和阳历之分,阴历是按照月亮的运行规律来计算日期的,那我们所说的阳历又是什么呢?其实阳历或者说叫西历最早是叫做儒略历,是由罗马共和国独裁官儒略·恺撒(即盖乌斯·尤里乌斯·凯撒)采纳数学家兼天文学家索西琴尼的计算后,于公元前45年1月1日起执行的取代旧罗马历法的一种历法,但令人遗憾的是,当时那些颁发历书的祭司们,却不了解改历的实质。结果,可笑的是,当时罗马执掌颁布历书的祭司竟把原来历法上规定的“每隔三年置闰”误解为“每三年置一闰”。从公元前45年起,到公元前9年为止,这之间本应设置10个闰年,他们却设置了13个闰年。公元前9年,人们终于发现这一差错,这时恺撒的外甥奥古斯都执掌政权,他纠正了这个错误,才停止了“三年一闰”。奥古斯都下令改正过来,改到次年(公元前8年)才置闰年。当改正这种闰年的错误时已经多闰了3年,为了去掉着多闰的3年,奥古斯都又下令停闰3年,即以公元前5年、公元前1年、公元前4年仍为平年,以后恢复了每4年一闰的规定了。奥古斯都为了宣扬这一功劳,仿效儒略·恺撒的做法,下令把自己出生的儒略历中的8月改称为奥古斯都月(这一名称在西方沿用到今天)。8月后的大,小月份都翻转过来了,9月为30天,10月为31天,11月为30天,12月为31天,这种置月方式一直沿用至今。如此一来,一年多出了一天,于是也从二月份29天里再减去一天,二月份只剩下28天了 1582年罗马教皇格里高利对”儒略历”又进行修改,规定被4整除的年为闰年,但逢百之年只有能被400除尽才能是闰年。这就是使用至今的“格里历”。这样做是为了使历年与回归年相接近。回归年的周期是365.2425天。儒略历一年的平均长度为365.25日,比回归年(365.2425天)长11分14秒,自公元325年(该年采用儒略历作为宗教日历)积累到十六世纪末,春分日由3月21日提早到3月11日。于是罗马教皇格里高利十三世(Gregorius XⅢ)于1582年10月4日还下令将次日(即原10月5日)定为10月15日,把春分日又恢复为3月21日。这样,1582年的10月5日-14日这十天就成了“不存在”的日子,变为历史的空白。1949年9月27日,经过中国人民政治协商会议第一届全体会议通过,中华人民共和国使用国际社会多数国家通用的西历和西元作为历法和纪年。
3. 蔡勒公式
清楚了Unix时间戳和公历的意义,怎么讲秒数转化成本地时间大家心里应该有个大概了,既然本地时间知道了,那我们可不可以根据这个日期直接知道那一天是星期几呢?数学家蔡勒(Zeller)推算出了这个公式,大家称为蔡勒公式,使用这个公式随便给出一个日期,就可以计算出是星期几。
公式具体是这样的
W = [C / 4] - 2C + y + [y / 4] + [13 * (M + 1) / 5] + d - 1
或者是:w = y + [y / 4] + [c / 4] - 2c + [26(m + 1) / 10] + d - 1
公式中的符号含义如下:
w:星期; w对7取模得:0-星期日,1-星期一,2-星期二,3-星期三,4-星期四,5-星期五,6-星期六
c:世纪-1(前两位数)
y:年(后两位数)
m:月(m大于等于3,小于等于14,即在蔡勒公式中,某年的1、2月要看作上一年的13、14月来计算,比如2003年1月1日要看作2002年的13月1日来计算)
d:日 [ ]代表取整,即只要整数部分。
下面以中华人民共和国成立100周年纪念日那天(2049年10月1日)来计算是星期几,过程如下:
w = y + [y / 4] + [c / 4] - 2c + [26(m + 1) / 10] + d - 1
= 49 + [49 / 4] + [20 / 4] - 2 × 20 + [26 × (10 + 1) / 10] + 1 - 1
= 49 + [12.25] + 5 - 40 + [28.6]
= 49 + 12 + 5 - 40 + 28
= 54 (除以7余5)
即2049年10月1日(100周年国庆)是星期五。
再比如计算2006年4月4日,过程如下:
w = y + [y / 4] + [c / 4] - 2c + [26(m + 1) / 10] + d - 1
= 6 + [6 / 4] + [20 / 4] - 2 * 20 + [26 * (4 + 1) / 10] + 4 - 1
= -12 (除以7余5,注意对负数的取模运算!实际上应该是星期二而不是星期五)
不过要注意的是,蔡勒公式只适合于1582年(明朝万历十年)10月15日之后的情形。
4. 具体实现
清楚了上面这些内容,我们来看一下用C怎么实现Unix时间戳和本地时间的相互转化,并根据指定的时间算出对应的日期
#include <stdio.h> #include <stdbool.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 /* 每个月的天数 */ const unsigned char g_day_per_mon[MONTH_PER_YEAR] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; /* 自定义的时间结构体 */ typedef struct { unsigned short nYear; unsigned char nMonth; unsigned char nDay; unsigned char nHour; unsigned char nMin; unsigned char nSec; unsigned char DayIndex; /* 0 = Sunday */ } mytime_struct; /* * 功能: * 判断是否是闰年 * 参数: * year:需要判断的年份数 * * 返回值: * 闰年返回1,否则返回0 */ unsigned char applib_dt_is_leap_year(unsigned short year) { /*----------------------------------------------------------------*/ /* Local Variables */ /*----------------------------------------------------------------*/ /*----------------------------------------------------------------*/ /* Code Body */ /*----------------------------------------------------------------*/ 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:该月所对应的年份数 * * 返回值: * 该月有多少天 * */ unsigned char applib_dt_last_day_of_mon(unsigned char month, unsigned short year) { /*----------------------------------------------------------------*/ /* Local Variables */ /*----------------------------------------------------------------*/ /*----------------------------------------------------------------*/ /* Code Body */ /*----------------------------------------------------------------*/ 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); } } /* * 功能: * 根据给定的日期得到对应的星期 * 参数: * year:给定的年份 * month:给定的月份 * day:给定的天数 * * 返回值: * 对应的星期数,0 - 星期天 ... 6 - 星期六 */ unsigned char applib_dt_dayindex(unsigned short year, unsigned char month, unsigned char day) { char century_code, year_code, month_code, day_code; int week = 0; century_code = year_code = month_code = day_code = 0; if (month == 1 || month == 2) { century_code = (year - 1) / 100; year_code = (year - 1) % 100; month_code = month + 12; day_code = day; } else { century_code = year / 100; year_code = year % 100; month_code = month; day_code = day; } /* 根据蔡勒公式计算星期 */ week = year_code + year_code / 4 + century_code / 4 - 2 * century_code + 26 * ( month_code + 1 ) / 10 + day_code - 1; week = week > 0 ? (week % 7) : ((week % 7) + 7); return week; } /* * 功能: * 根据UTC时间戳得到对应的日期 * 参数: * utc_sec:给定的UTC时间戳 * result:计算出的结果 * daylightSaving:是否是夏令时 * * 返回值: * 无 */ void utc_sec_2_mytime(unsigned int utc_sec, mytime_struct *result, bool daylightSaving) { /*----------------------------------------------------------------*/ /* Local Variables */ /*----------------------------------------------------------------*/ int sec, day; unsigned short y; unsigned char m; unsigned short d; unsigned char dst; /*----------------------------------------------------------------*/ /* Code Body */ /*----------------------------------------------------------------*/ if (daylightSaving) { utc_sec += SEC_PER_HOUR; } /* hour, min, sec */ /* hour */ sec = utc_sec % SEC_PER_DAY; result->nHour = sec / SEC_PER_HOUR; /* min */ sec %= SEC_PER_HOUR; result->nMin = sec / SEC_PER_MIN; /* sec */ result->nSec = sec % SEC_PER_MIN; /* year, month, day */ /* year */ /* year */ day = utc_sec / SEC_PER_DAY; for (y = UTC_BASE_YEAR; day > 0; y++) { d = (DAY_PER_YEAR + applib_dt_is_leap_year(y)); if (day >= d) { day -= d; } else { break; } } result->nYear = y; for (m = 1; m < MONTH_PER_YEAR; m++) { d = applib_dt_last_day_of_mon(m, y); if (day >= d) { day -= d; } else { break; } } result->nMonth = m; result->nDay = (unsigned char) (day + 1); /* 根据给定的日期得到对应的星期 */ result->DayIndex = applib_dt_dayindex(result->nYear, result->nMonth, result->nDay); } /* * 功能: * 根据时间计算出UTC时间戳 * 参数: * currTime:给定的时间 * daylightSaving:是否是夏令时 * * 返回值: * UTC时间戳 */ unsigned int mytime_2_utc_sec(mytime_struct *currTime, bool daylightSaving) { /*----------------------------------------------------------------*/ /* Local Variables */ /*----------------------------------------------------------------*/ unsigned short i; unsigned int no_of_days = 0; int utc_time; unsigned char dst; /*----------------------------------------------------------------*/ /* Code Body */ /*----------------------------------------------------------------*/ if (currTime->nYear < UTC_BASE_YEAR) { return 0; } /* year */ for (i = UTC_BASE_YEAR; i < currTime->nYear; i++) { no_of_days += (DAY_PER_YEAR + applib_dt_is_leap_year(i)); } /* month */ for (i = 1; i < currTime->nMonth; i++) { no_of_days += applib_dt_last_day_of_mon((unsigned char) i, currTime->nYear); } /* day */ no_of_days += (currTime->nDay - 1); /* sec */ utc_time = (unsigned int) no_of_days * SEC_PER_DAY + (unsigned int) (currTime->nHour * SEC_PER_HOUR + currTime->nMin * SEC_PER_MIN + currTime->nSec); if (dst && daylightSaving) { utc_time -= SEC_PER_HOUR; } return utc_time; } int main(int argc, char *argv[]) { mytime_struct my_time; unsigned int sec; char *DayIndex[] = {"Sun.", "Mon.", "Tues.", "Wed.", "Thur.", "Fri.", "Sat."}; /* 这里根据UTC时间戳计算出来的时间是零时区的时间,所以如果要转化成北京时间就需要多加8小时 */ utc_sec_2_mytime(1484537668 + 8 * SEC_PER_HOUR, &my_time, false); printf("%d-%d-%d %d:%d:%d %s\n", my_time.nYear, my_time.nMonth, my_time.nDay, my_time.nHour, my_time.nMin, my_time.nSec, DayIndex[my_time.DayIndex]); sec = mytime_2_utc_sec(&my_time, false); printf("sec = %d\n", sec); return 0; }
基本上的内容就这些了,平常最好能够直接使用系统提供的接口,如果不能使用的话就自己实现,根据上面的代码修改成自己需要的,如果有不对的地方,希望能够批评指正
参考文献
https://www.jb51.net/article/206868.htm
总结
到此这篇关于C语言如何实现Unix时间戳与本地时间转化的文章就介绍到这了,更多相关C语言Unix时间戳内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!
相关文章
- 这篇文章主要为大家详细介绍了C语言实现放烟花的程序,有音乐播放,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-02-23
- 本篇文章主要介绍C语言中char的知识,并附有代码实例,以便大家在学习的时候更好的理解,有需要的可以看一下...2020-04-25
- 这篇文章主要介绍了详解如何将c语言文件打包成exe可执行程序,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-25
- 这篇文章主要介绍了PostgreSQL TIMESTAMP类型 时间戳操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-12-26
- 这篇文章主要介绍了解决python 两个时间戳相减出现结果错误的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-03-12
- free函数是释放之前某一次malloc函数申请的空间,而且只是释放空间,并不改变指针的值。下面我们就来详细探讨下...2020-04-25
- 这篇文章主要介绍了C语言中计算正弦的相关函数总结,包括正弦和双曲线正弦以及反正弦的函数,需要的朋友可以参考下...2020-04-25
详解C语言中的rename()函数和remove()函数的使用方法
这篇文章主要介绍了详解C语言中的rename()函数和remove()函数的使用方法,是C语言入门学习中的基础知识,需要的朋友可以参考下...2020-04-25- 这篇文章主要介绍了C语言中求和、计算平均值、方差和标准差的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-12-10
- 本篇文章主要讲解C语言 基本语法,这里提供简单的示例和代码来详细讲解C语言的基本语法,开始学习C语言的朋友可以看一下,希望能够给你带来帮助...2021-09-18
- 分享一个利用php根据日期或时间戳获取相应的干支纪年,生肖和星座信息的函数方法,具体函数代码以及使用方法如下: /** 判断干支、生肖和星座 */ function birthext($birth){ if(strstr($birth,'-')===false&&strlen($bi...2015-10-21
php 获取今日、昨日、上周、本月的起始时间戳和结束时间戳的方法
php获取今日开始时间戳和结束时间戳$beginToday=mktime(0,0,0,date('m'),date('d'),date('Y'));$endToday=mktime(0,0,0,date('m'),date('d')+1,date('Y'))-1;//php获取昨日起始时间戳和结束时间...2013-10-04- 这篇文章主要为大家详细介绍了Unity时间戳的使用方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-06-25
- 这篇文章主要介绍了C语言中send()函数和sendto()函数的使用方法,是C语言入门学习中的基础知识,需要的朋友可以参考下...2020-04-25
- 今天小编就为大家分享一篇C语言实现从文件读入一个3*3数组,并计算每行的平均值,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-04-25
- 这篇文章主要介绍了使用C语言操作文件的基本函数整理,包括创建和打开以及关闭文件的操作方法,需要的朋友可以参考下...2020-04-25
- 这篇文章主要介绍了C语言中memcpy 函数的用法详解的相关资料,需要的朋友可以参考下...2020-04-25
- 这篇文章主要介绍了C语言中查找字符在字符串中出现的位置的方法,分别是strchr()函数和strrchr()函数的使用,需要的朋友可以参考下...2020-04-25
- 很多同学在学习c语言的时候是不是会碰到a++和++a都有甚么作用啊。今天我们就来探讨下...2020-04-25
- 这篇文章主要介绍了讲Perl中的本地时间与UNIX时间戳间相互转换的方法,主要用到了Perl中的Date::Parse模块,需要的朋友可以参考下...2020-06-29