0
点赞
收藏
分享

微信扫一扫

借助金融科技差异化发展,不一样的“破茧”手法

雪域迷影 2023-06-09 阅读 47

linux 下时间转换(c接口)
----------------------------------------------------------------
author:hjjdebug
date:  2023年 06月 08日 星期四 09:44:54 CST
----------------------------------------------------------------

1. 目的:


    计算机擅长的是处理数据,尤其是整数,浮点数. 人类只能看懂ascii字符,当然图形图像这里不说.
    所以时间就有两种表示方式, 整数形式,字符串形式, 这两种时间格式转换接口,根据当前系统提供的接口,
    可以定义为str2sec(), sec2str(). 就是秒值到字符串, 字符串到秒值两个函数
    查接口, 至少系统给我们提供了一个单方向的转换,从整数到字符串. 其中ctime 的含义是canlender_time
       char *ctime(const time_t *timep);
       char *ctime_r(const time_t *timep, char *buf);


2. 实现:


    实现上有一个关键数据结构,叫struct tm, 定义如下:
          struct tm {
               int tm_sec;    /* Seconds (0-60) */
               int tm_min;    /* Minutes (0-59) */
               int tm_hour;   /* Hours (0-23) */
               int tm_mday;   /* Day of the month (1-31) */
               int tm_mon;    /* Month (0-11) */
               int tm_year;   /* Year - 1900 */
               int tm_wday;   /* Day of the week (0-6, Sunday = 0) */
               int tm_yday;   /* Day in the year (0-365, 1 Jan = 0) */
               int tm_isdst;  /* Daylight saving time */
           };
     就是说它可以储藏年,月,日,时,分,秒数据,利用它可以方便的转换成字符串(sprintf),
     不过连sprintf 你也不用写了,系统提供了以下函数
       char *asctime(const struct tm *tm);
       char *asctime_r(const struct tm *tm, char *buf);

     也可以方便的转换成秒(计算).
     这个计算你也不用写了,系统提供了以下函数
        time_t mktime(struct tm *tm);

     那字符串怎样转成struct tm 呢? 可以用scan.  这里有没有系统接口呢? 未知?

     那秒值又怎样转换成struct tm呢? 系统提供给我们的是
      struct tm *localtime(const time_t *timep);
      struct tm *localtime_r(const time_t *timep, struct tm *result); // _r 的含义是带输出参数result

      与其对应的有gmtime()函数, gm是GMT/UTC的意思,
      GMT: Greenwich Mean Time, 格林威治平均时.也就是格林威治皇家观测台全球标准时,等于UTC
      UTC: Universal Time Coordinated , 世界时.
      UTC 就是0时区时间.
      而我们的localtime 是东8区时间, 比世界时大8个小时.

      这期间的核心架构关系我很想画一个图, 但是我懒,我dot用不熟, 我忙... 总之我没有画,但它在我心中了.
      大致是字符串+双箭头+struct tm+双箭头+time_t


3. 改进:


    以上架构的时间精度是基于秒的, 但是如果想提高精度, 例如提高到ms 怎么办?
    我这里做音视频开发,每帧播放时间一般是40ms, 秒值太大了, 需要提高到ms级别.
    方法1: 利用现有架构, 在其上追加毫秒. 我相信这是比较容易走通的.
    方法2: 修改当前架构, 使其支持毫秒精度,这个方法难度更大,改动更深刻,理解更彻底!

我这里采用方法2,  字符串+双箭头+struct tm_ms+双箭头+time_t_ms
struct tm_ms 扩充了毫秒结构, time_t_ms 代表的是毫秒值(从1970年1月1日0时起,注意时区)
提供代码及测试代码如下:


4. 代码:


    系统给我们提供了一个获取微秒的函数gettimeofday()

#include<stdio.h>
#include<string.h>
#include <sys/time.h>

typedef long time_t_ms;

struct tm_ms {
	int tm_msec; /* 毫秒 -取值区间 [0,999] */
	int tm_sec; /* 秒 – 取值区间为[0,59] */
	int tm_min; /* 分 - 取值区间为[0,59] */
	int tm_hour; /* 时 - 取值区间为[0,23] */
	int tm_mday; /* 一个月中的日期 - 取值区间为[1,31] */
	int tm_mon; /* 月份(从一月开始,0代表一月) - 取值区间为[0,11] */
	int tm_year; /* 年份,其值等于实际年份减去1900 */
};
const char Days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
void localtime_ms(time_t_ms time,struct tm_ms *t)
{
	unsigned int Pass4year;
	int hours_per_year;

	if(time < 0)
	{
		time = 0;
	}
	//time 先加上时区偏差,我们是东8区,先加上8小时时间
	//就是说整数时间都是0时区时间,而datetime是包含分区信息的
	//所以datetime 变回time 要减偏差, time变成datetime要加偏差
	//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	time += 8 * 3600 *1000;
	//取毫秒时间
	t->tm_msec=(int)(time % 1000);
	time /= 1000;
	//取秒时间
	t->tm_sec=(int)(time % 60);
	time /= 60;
	//取分钟时间
	t->tm_min=(int)(time % 60);
	time /= 60;
	//取过去多少个四年,每四年有 1461*24 小时
	Pass4year=((unsigned int)time / (1461L * 24L));
	//计算年份
	t->tm_year=(Pass4year << 2) + 1970;
	//四年中剩下的小时数
	time %= 1461L * 24L;
	//校正闰年影响的年份,计算一年中剩下的小时数
	for (;;)
	{
		//一年的小时数
		hours_per_year = 365 * 24;
		//判断闰年
		if ((t->tm_year & 3) == 0)
		{
			//是闰年,一年则多24小时,即一天
			hours_per_year += 24;
		}
		if (time < hours_per_year)
		{
			break;
		}
		t->tm_year++;
		time -= hours_per_year;
	}
	//小时数
	t->tm_hour=(int)(time % 24);
	//一年中剩下的天数
	time /= 24;
	//假定为闰年
	time++;
	//校正闰年的误差,计算月份,日期
	if((t->tm_year & 3) == 0)
	{
		if (time > 60)
		{
			time--;
		}
		else
		{
			if (time == 60)
			{
				t->tm_mon = 1;
				t->tm_mday = 29;
				return ;
			}
		}
	}
	//计算月日
	for (t->tm_mon = 0; Days[t->tm_mon] < time;t->tm_mon++)
	{
		time -= Days[t->tm_mon];
	}

	t->tm_mday = (int)(time);

	return;
}

static time_t_ms mon_yday[2][12] =
{
	{0,31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
	{0,31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335},
};

int isleap(int year)
{
	return (year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0);
}

time_t_ms mktime_ms(struct tm_ms dt)
{
	time_t_ms result;
	int i =0;
	// 以平年时间计算的秒数
	result = (dt.tm_year - 1970) * 365 * 24 * 3600 +
		(mon_yday[isleap(dt.tm_year)][dt.tm_mon] + dt.tm_mday - 1) * 24 * 3600 +
		dt.tm_hour * 3600 + dt.tm_min * 60 + dt.tm_sec;
	// 加上闰年的秒数
	for(i=1970; i < dt.tm_year; i++)
	{
		if(isleap(i))
		{
			result += 24 * 3600;
		}
	}

	result = result*1000+dt.tm_msec;

	//加上时区偏差,我们是东8区,减去8小时时间,
	//就是说整数时间都是0时区时间,而datetime是包含分区信息的
	//所以datetime 变回time 要减偏差, time变成datetime要加偏差
	//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	result -= 8 * 3600 *1000;

	return(result);
}

long gettime_us(void)
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec * 1000000 + tv.tv_usec;
}

const char *str="2023-06-07 20:29:00.948";

int main()
{ 
#if 1
	//测试固定字符串, 看清月份是从0开始的. 请man localtime
	printf("A> %s\n",str);
	struct tm_ms t;
	int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0, msec=0;
	sscanf(str, "%d-%d-%d %d:%d:%d.%d", &year, &month, &day, &hour, &minute, &second,&msec);
	t.tm_year=year;
	t.tm_mon= month-1;
	t.tm_mday=day;
	t.tm_hour=hour;
	t.tm_min=minute;
	t.tm_sec=second;
	t.tm_msec=msec;
	time_t_ms time1 = mktime_ms(t);  //将localtime得到年月日时分秒再次转换成时间戳,验证算法是否正确
	printf("time_ms:%ld\r\n",time1);
	struct tm_ms t2;
	localtime_ms(time1,&t2);
	printf("B> %04d-%02d-%02d %02d:%02d:%02d.%3d\r\n",t2.tm_year,t2.tm_mon+1,t2.tm_mday,t2.tm_hour,t2.tm_min,t2.tm_sec,t2.tm_msec);
#endif

//1.通过获取当前时间的毫秒数,判断localtime_ms 是否可以工作
	time_t_ms time=gettime_us()/1000;
	printf("A> time_ms:%ld\n",time);
	struct tm_ms tm_;
	localtime_ms(time,&tm_);
	//月份的0代表的是1月, tm_mon范围 [0-11], 请man localtime
	printf("datetime:%04d-%02d-%02d %02d:%02d:%02d.%3d\r\n",tm_.tm_year,tm_.tm_mon+1,tm_.tm_mday,tm_.tm_hour,tm_.tm_min,tm_.tm_sec,tm_.tm_msec);

//2. 把struct tm_ms变回 time_t_ms, 验证mktime_ms
	time_t_ms time2=mktime_ms(tm_);
	printf("B> time_ms:%ld\n",time2);
	return 0;
}

5. 测试结果:

$ ./test_localtime
A> 2023-06-07 20:29:00.948
time_ms:1686140940948
B> 2023-06-07 20:29:00.948
A> time_ms:1686188589860
datetime:2023-06-08 09:43:09.860
B> time_ms:1686188589860

举报

相关推荐

0 条评论