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时间戳内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C语言实现字符转unix时间戳的简单实例

    C语言实现字符转unix时间戳,需要先转成tm类型,再得到它的Unix时间戳.附上实现代码: #include <stdio.h> #include <time.h> int strtotime(char datetime[]) { struct tm tm_time; int unixtime; strptime(datetime, "%Y-%m-%d %H:%M:%S", &tm_time); unixtime = mktime(&tm_ti

  • C语言实现时间戳转日期的算法(推荐)

    1.算法 时间是有周期规律的,4年一个周期(平年.平年.平年.闰年)共计1461天.Windows上C库函数time(NULL)返回的是从1970年1月1日以来的毫秒数,我们最后算出来的年数一定要加上这个基数1970.总的天数除以1461就可以知道经历了多少个周期:总的天数对1461取余数就可以知道剩余的不足一个周期的天数,对这个余数进行判断也就可以得到月份和日了. 当然了,C语言库函数:localtime就可以获得一个时间戳对应的具体日期了,这里 主要说的是实现的一种算法. 2.C语言代码实现

  • C语言中时间戳转换成时间字符串的方法

    在PE格式里有个字段是文件的创建时间戳,我想把转成字符串,这样看的更直观. TCHAR buffer[50] = {0}; struct tm Tm = {0}; time_t time = (time_t)NtHeader->FileHeader.TimeDateStamp;//时间戳 gmtime_s(&Tm, &time); printf(buffer, TEXT("%d年%d月%d日 %d:%d:%d"), Tm.tm_year+1900, Tm.tm_m

  • 浅谈时间戳与日期时间互转C语言

    浅谈时间戳与日期时间互转C语言 /* * ctime.h * * Created on: May 19, 2016 * */ #ifndef CTIME_H_ #define CTIME_H_ #include "common/micro_type.h" #define OFFSET_SECOND 946684800 /* ��1970/1/1/0/0/0��2000/1/1/0/0/0֮��������� */ //#define OFFSET_SECOND 0 /* ��2000/

  • C语言如何实现Unix时间戳与本地时间转化

    前言 我们平常说时间都说的几点几分几秒,星期几,但是在计算机里面并不是直接使用我们所说的时间,而是使用Unix时间戳,这样不管是哪个平台,哪个系统,都可以根据自己对时间的定义进行转换,像Java,PHP等都提供了接口来进行转化,C库里面也有这样的函数,那具体是怎么实现的呢?要了解这个问题首先我们就必须要清楚什么是Unix时间戳,什么是我们平常使用的时间. 1. Unix时间戳 UNIX时间戳:Unix时间戳(英文为Unix epoch, Unix time, POSIX time 或 Unix

  • JS根据Unix时间戳显示发布时间是多久前【项目实测】

    后台接口给的时间数据为Unix时间戳,我们的需求是显示类似微信朋友圈显示发布时间为距离当前时间多久之前,"xx分钟之前","xx小时之前","xx个月之前". 类似这样的时间显示效果: 转换函数: /** * Unix时间戳转换为当前时间多久之前 * @param timespan int Unix时间戳 * @return timeSpanStr string 转换之后的前台需要的字符串 */ function Ftime (timespan)

  • PHP的时间戳与具体时间转化的简单实现

    三个内置函数: time() //获取UNIX系统时间戳 mktime(hour,minute,second,month,day,year) //将指定时间转化为时间戳 date(时间格式,时间戳) //将时间戳转化为方便阅读的时间 time -> date: $now = time(); echo "时间戳是 " .$now; echo "创建日期是 " . date("Y-m-d h:i:s", $now); 输出: 时间戳是 1404

  • vue.js将unix时间戳转换为自定义时间格式

    本方法通过vue.js filter实现将unix时间戳转换为自定义标准时间格式 <!-- js代码 --> $().ready(function() { <!-- 自定义filter名称为'time' --> Vue.filter('time', <!-- value 格式为13位unix时间戳 --> <!-- 10位unix时间戳可通过value*1000转换为13位格式 --> function(value) { var date = new Dat

  • UTC时间、GMT时间、本地时间、Unix时间戳的具体使用

    目录 1.UTC时间 与 GMT时间 2.UTC时间 与 本地时 3.UTC 与 Unix时间戳 1.UTC时间 与 GMT时间 我们可以认为格林威治时间就是时间协调时间(GMT=UTC),格林威治时间和UTC时间均用秒数来计算的. 2.UTC时间 与 本地时 UTC + 时区差 = 本地时间 时区差东为正,西为负.在此,把东八区时区差记为 +0800, UTC + (+0800) = 本地(北京)时间 (1) 那么,UTC = 本地时间(北京时间))- 0800 (2) 3.UTC 与 Uni

  • JS操作时间 - UNIX时间戳的简单介绍(必看篇)

    准备知识 GMT(Greenwich Mean Time) - 格林尼治时间.UTC(Universal Time Code) - 国际协调时间.PST(Pacific Standard Time,太平洋标准时间). UTC出现的比GMT时间晚,可以认为UTC时间更加精确,不过它们之间只相差几秒钟. UTC开始时间为:1970-01-01T00:00:00.000Z(这种时间设置可以一直精确到毫秒,字母T和Z仅仅是一种格式) UNIX时间戳指的是从1970-01-01T00:00:00.000Z

  • C#中系统时间和UNIX时间戳互相转换

    在项目开发过程中,有时会遇到不同程序之间相互调用数据,数据中不免会包含时间,比如ASP.NET调用PHP,牵扯到时间就要做一下处理,PHP程序中一般存取的都是UNIX时间,不像ASP.NET存储的是年月日时分秒,这样就需要做一下转换,代码如下: 一.将系统时间转换成UNIX时间戳 复制代码 代码如下: DateTime dtStart = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970,1,1)); DateTime dtNow =

  • 讲Perl中的本地时间与UNIX时间戳间相互转换的方法

    当你的Perl脚本需要解决时间信息,这里有两种方法来表示和处理日期和时间.一种方法是易读的时间表示(例,"Sat Mar 14 10:14:05 EDT 2015"),另外一种是使用UNIX时间戳(也叫"新纪元时间"),这是从1970年1月1日到今所经过的时间秒数.每一种方法都有它自己的优劣势,取决于你的需要,也许也就需要转换一种格式到另一种. Perl中转换本地时间到UNIX时间戳 为了从日期字符串中获得UNIX时间,可以使用Date::Parse模块中str2t

  • 用VBS获取Unix时间戳的函数代码

    VBS中没有类似C标准库中的time函数,怎么获取Unix时间戳呢?乍一看很简单: 复制代码 代码如下: Function UnixTime() UnixTime = DateDiff("s", "01/01/1970 00:00:00", Now()) End Function 一个很想当然的方法,仅仅注意到了"1970年1月1日0时0分0秒",而忽略了"协调世界时". 协调世界时,又称世界标准时间或世界协调时间,简称UTC

随机推荐