Unix时间戳和北京时间的相互转换(C语言实现 )

2023-11-05

一、问题背景

最近物联网项目中需要上传包含时间戳的设备数据到服务器平台。原本想把“年”,“月”,“日”,“时”,“分”, “秒”分别用一个 uint8_t 的数据类型去存储,即占用6个字节。但是在平台配置协议时,只有一种叫“Unix时间戳”的数据类型。Unix时间戳只占用4个字节,而且Unix时间戳在服务器端更加通用,但是在单片机上没有像 Linux 环境下现成的 time(),localtime(),mktime() 等库函数调用。所以考虑自己实现 Unix时间戳 和北京时间的相互转换。

二、Unix时间戳简介

Unix时间戳:
是从1970年1月1日00:00:00开始到当前时刻经过的秒数。
例如:一个小时表示为Unix时间戳格式为:3600秒;一天表示为Unix时间戳为86400秒。 
当然由于时区的关系,北京时间在算出来的秒数后面需要加上8个小时(8*3600秒)。

比如在64位linux中,我们获取Unix时间戳可以用:

typedef long time_t; /* time value */
time_t time(time_t * timer)

调用后会返回一个 time_t 类型的值(即 long)。但在以前大部分的32位操作系统上,此 “time_t” 定义为有正负号的32位整数(signed int32)。即最大秒数为:2^31,大约在 2038年 就会存在秒数溢出的问题(时间重新变为1970年)。所以后面的设备都改用64位去存储,当然这不是本文探讨的地方。

为什么使用Unix时间戳?
在服务器端使用Unix时间戳更加通用。

三、算法转换思路

北京时间转Unix时间戳:

这个转换比较简单,用当前的时间的年月日时分秒,依次减去1970/1/1 00:00:00即可。只要注意闰年的情况就行,最后注意需要加上北京时区的8个小时。

Unix时间戳转北京时间:

不严谨的说每隔4年就有一个闰年(此处暂不考虑2100年这样的非闰年,因为time_t限制,可取的范围只有1970~2038),所以可以将4年看做一个周期(即365+365+365+366=1461天)。通过总天数除以1461得到周期的个数,然后1970加上周期的个数乘以4就是年份。总天数对1461取余就是这个周期内的天数,然后根据平闰年去判断年月日时分秒。

特殊说明:我们这是使用无符号的 uint32_t 去保存时间戳,相比使用有符号的 int32_t,因此溢出错误会被延后到 2106 年。但本程序暂未处理 2100 这类非闰年,所以只保证 1970~2100 这时间段内的计算。

四、具体代码

/******************************************************************
//版本记录:
// 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;
 
    // 测试用例:平/闰年,闰月,8点前等 
    // 使用时,修改这里就可以
    testTime.ui8Year = 2016;
    testTime.ui8Month = 02;
    testTime.ui8DayOfMonth = 29;
    testTime.ui8Hour = 05;
    testTime.ui8Minute = 20;
    testTime.ui8Second = 00;
  
    UnixTimsStamp = covBeijing2UnixTimeStp(&testTime);
    printf("%d/%02d/%02d-%02d:%02d:%02d convert is: %u\n\n", \
                    testTime.ui8Year, testTime.ui8Month, testTime.ui8DayOfMonth, \
                    testTime.ui8Hour, testTime.ui8Minute, testTime.ui8Second, UnixTimsStamp);
 
    covUnixTimeStp2Beijing(UnixTimsStamp, &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);
}
 

运行结果如下:

五、结果验证

通过站长工具验证,测试ok:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Unix时间戳和北京时间的相互转换(C语言实现 ) 的相关文章

  • 在 Javascript 中将 yyyy-mm-dd 转换为 UTC

    我需要仅使用 javascript 将 yyyy mm dd 中的日期 如 2011 12 30 转换为 UTC 如何 var utc new Date 2011 12 30 toUTCString jsFiddle
  • lua 中的来回 UTC 日期

    我在将 lua 日期转换为时间戳然后从中获取原始日期时遇到问题 它确实适用于非 UTC 日期 但不适用于 UTC 目前我的示例代码是 local dt1 os date t print dt1 hour local dt2 os date
  • 将 UTC 转换为当前区域设置时间

    我正在从 Web 服务下载一些 JSON 数据 在此 JSON 中 我有一些日期 时间值 一切都以 UTC 时间为准 如何解析此日期字符串 以便结果 Date 对象位于当前区域设置中 例如 服务器返回 2011 05 18 16 35 01
  • 应用程序中的 UTC 时间相关问题(C#、ASP.NET 和 SQL)

    我正在开发一款与销售和采购报告相关的应用程序 我正在使用该采购和销售报告的数据生成图表 采购订单和销售订单的创建日期是相对于 UTC 5 30 时间的 即该时间适用于印度孟买 场景 1 假设这是一个场景 目前这个应用程序在印度用于这个场景
  • 从长 UTC 时间戳到 JAVA UTC 到 EST

    我正在尝试将 UTC 长时间戳转换为东部标准时间 但我完全迷失了 任何提示都会很棒 谢谢 右 尝试这个 Date estTime new Date utcTime getTime TimeZone getTimeZone EST getRa
  • 为什么我无法在 Javascript Date 对象上使用 getDay() 方法和 getUTCDay() 方法获得相同的结果

    今天是星期一 上午 00 51 我来自魁北克市 因此我的时间为 GMT 0500 UTC 05 00 我不知道为什么下面的代码给了我两个不同的结果 const weekday Array sunday monday tuesday wedn
  • 将 PST 格式的日期和时间转换为 UTC 格式

    我有一个变量 str 字符串类型 其值为 28 Nov 2013 09 15 AM 如何将其转换为 UTC 格式 str 变量中的上述时间采用 PST 因此 UTC 应该是 8超过这个时间 我正在使用 flex 2 下面发现以下代码不起作用
  • 自 UTC 时区当天开始以来的秒数

    如何在Python中找到 自UTC时区开始以来的秒数 我查看了文档 但不明白如何使用它datetime timedelta 这是一种方法 from datetime import datetime time utcnow datetime
  • MySQL 是否应该将时区设置为 UTC?

    跟进问题https serverfault com questions 191331 should servers have their timezone set to gmt utc https serverfault com quest
  • java显示Windows UTC时间

    Windows 在内部将 FileTime 存储为自 1 1 1601 UTC 以来的 100 纳秒数作为 64 位字段 是否可以让 java 打印出当前数字 只是寻找一个例子 因为我找不到办法做到这一点 我想把号码打印出来 任何帮助将不胜
  • JavaScript - Date()、new Date() 和 ISODate 之间的区别

    我看过这个问题的各种版本 但没有一个能满足我的需求 我想为 MongoDB 创建 ISODate 并且我正在使用 Node js 在节点中 当我这样做时 console log Date I get 2016 年 9 月 26 日星期一 1
  • 当数据依赖于日期时间时,在数据库中保存日期时间和时区信息的最佳实践

    关于在数据库中保存日期时间和时区信息有很多问题 但更多的是总体水平 这里我想讲一个具体的案例 系统规格 我们有一个订单系统数据库 它是一个多租户系统 租户可以使用任意时区 它是任意的 但每个租户只有一个时区 保存在租户表中一次并且永不更改
  • 如何将 long (currentTimeInMillis) 转换为 UTC 时间戳?

    我的客户正在向我发送Long这可以被认为是 scala gt System currentTimeMillis res3 Long 1441056836609 scala gt 我如何将其转换为UTC时间戳 在服务器上 我们使用Java 8
  • pytz - 将 UTC 和时区转换为本地时间

    我有一个datetime以 utc 时区为例 utc time datetime datetime utcnow 和 pytz 时区对象 tz timezone America St Johns 正确的转换方法是什么utc time到给定的
  • 获取以 UTC 表示的当前时间的最简单 PowerShell 方法

    我已查看该帖子在 PowerShell 中创建具有特定 UTC 日期时间的 DateTime 对象 https stackoverflow com questions 10487011 creating a datetime object
  • DateTime.Parse("2012-09-30T23:00:00.0000000Z") 始终转换为 DateTimeKind.Local

    我想解析一个表示 UTC 格式的日期时间的字符串 我的字符串表示形式包括 Zulu 时间规范 该规范应指示该字符串表示 UTC 时间 var myDate DateTime Parse 2012 09 30T23 00 00 0000000
  • 测试正确的时区处理

    我们正在处理大量数据 所有数据均以 UTC Java 语言 标记 在读取这些数据 将其存储在数据库中以及再次将其取出之间 发生了一些数据在夏令时期间关闭一小时的情况 由于 UTC 没有夏令时的概念 这显然是软件中的一个错误 一旦知道 就很容
  • System TZ 和 DB TZ 之间的时区感知差异?

    我目前正在将数据库从本地 MySQL 主机迁移到 RDS 中的 Aurora 使用以下命令检查两个系统上的时区设置 SELECT system time zone global time zone session time zone NOW
  • 是否有充分的理由不使用 UTC 存储时间?

    我想知道是否有任何充分的理由以 UTC GMT 以外的任何时间存储时间信息 我相信这是所有软件工程的坚实规则 转换为本地时间只是出于显示目的而在 UI 层发生的转换 我还见过需要翻译才能正确实现算法的情况 用于处理午夜日期更改等 一般来说
  • 如何将 MySQL 数据库更改为 UTC?

    我使用的是 Windows 7 对数据库方面的东西有点陌生 我尝试在 Google 上搜索如何将系统时区更改为 UTC 但文档有些高级 我不太确定如何更改此字段 在 my ini 文件的 mysqld 部分下 添加以下行 default t

随机推荐

  • stm32通过I2C接口实现温湿度(AHT20)的采集

    stm32通过I2C接口实现温湿度 AHT20 的采集 一 I2C总线协议详解 一 I2C总线物理拓扑结构 二 I2C总线协议 三 硬件I2C和模拟I2C 1 硬件I2C 2 模拟I2C 3 区别 二 实现AHT20采集程序 一 了解AHT
  • 教程四:使用物联网平台控制硬件端

    物美智能 系列文章目录 一款强大的物联网管理平台介绍 物美智能 教程一 阿里云使用docker快速部署服务端 教程二 本地配置服务端开发环境 教程三 硬件端 Arduino和ESP8266开发板入门 教程四 使用物联网平台控制硬件端 教程五
  • 【转】this 的使用方法 —— javascript中的this讲解!

    从自己刚刚开始学习javascript到现在已经很久了 今天得益于新酱的细心讲解 总算是把this这个 雾中花 看清晰了 在此首先感谢新酱的讲解 下面将this的一些基本使用和大家分享一下 查看this指向的一句话法则 永远指向其所在函数的
  • word中公式后面标号右对齐

    版权声明 本文为博主原创文章 转载请标明链接 https blog csdn net zaishuiyifangxym article details 81709319
  • CentOS7服务器安装GPU显卡驱动和CUDA简单方法

    一键安装 通过下载官网runfile程序 自带驱动 参考 Centos7 4安装CUDA9 1 GPU驱动安装 先安装驱动 再安装cuda 参考 CentOS安装nvidia显卡驱动的正确方法 添加ELRpo源 1 导入公钥 rpm imp
  • 实战使用pano2vr生成html5全景页面

    随着现代视觉技术的进步以及对空间展示的迫切需求 很多的无人机可以拍出360度甚至720度全景照片 怎样将全景地图以html5的形式展示出来 文章将详细讲解如何使用pano2vr exe制作全景页面 1 准备pano2vr exe 软件 以w
  • adb 连接某个wifi_一加7 Pro全局强制开启90Hz刷新率的办法(附ADB文件下载)

    要说当前市场上值得买的安卓旗舰有哪几台 上个月刚上市的一加7Pro绝对算一个 一加7Pro最大的卖点就是那块从三星特别定制的6 7英寸QHD分辨率90Hz刷新率 自诩为除三星S10外市场上第二好的AMOLED屏幕 当每一位用过这台能完美呈现
  • JAVA程序入门--数据类型掌握练习《输入个人信息、计算圆的面积、变量交换》

    目录 前言 一 引用 1 Scanner类 1 1 Scanner基本语法 1 2 简单使用 1 3 执行 查看效果 二 练习1 输入个人信息 1 练习内容 2 逻辑梳理 3 整理代码 4 执行结果 三 练习2 计算圆形的面积 1 练习内容
  • git push 提交失败

    提交错误如下 git push origin Enumerating objects 1107 done Counting objects 100 1107 1107 done Delta compression using up to 1
  • 图片转二进制——各种方法汇总

    使用Base64转换图片 利用Base64实现二进制和图片之间的转换 具体代码如下 import java awt image BufferedImage import java io ByteArrayInputStream import
  • win10开始菜单打不开,找不到(没有)本地安全策略

    方法一 看你C盘windows目录下的system32目录下 这两个文件gpedit msc和secpol msc还在不在 不在了就从别的电脑上拷过来 然后点 开始 运行 输入gpedit msc 点确定 如果没有执行 那么就直接去wind
  • linux查看已删除空间却没有释放的进程

    背景 rm删除了文件或者文件夹 df查看时发现没有释放磁盘空间 执行lsof n grep deleted这个命令 root localhost lsof n grep deleted 发现有几个删除了但是没有释放空间 root local
  • Sequence Modeling: Recurrent and Recursive Nets(1)

    CONTENTS Recurrent neural networks or RNNs Rumelhart et al 1986a are a family of neural networks for processing sequenti
  • keil debug如何在watch直接修改变量值_ST福利:如何使用STM32F103C8T6的后64KB flash

    在STM32F103系列芯片中 C8T6 和CBT6均为LQFP48封装 而且差异仅为flash大小区别 C8T6为64KB CBT6为128KB 然而 虽然C8T6的datasheet中标称为64KB 实际上C8T6和CBT6由同一片di
  • Linux Watchdog 机制

    前言 Watchdog 是 Linux 系统一个很重要的机制 其目的是监测系统运行的情况 一旦出现锁死 死机的情况 能及时重启机器 取决于设置策略 并收集crash dump watchdog 顾名思义 看门狗 这就说明 有一个被watch
  • Ubuntu16.04安装jdk1.8

    Ubuntu16 04安装jdk记录 在官网上下载jdk版本 这个步骤就不详细说明 图形化的 很简单 移到 opt 目录下 个人习惯 sudo mv sudo mv jdk 8u151 linux x64 tar gz opt 解压缩到 o
  • 基于antd Tree实现可编辑菜单树,支持节点新增、删除

    基于antd3 Tree实现可编辑菜单树 支持节点新增 编辑 删除 基于antd Tree 实现了可编辑菜单树 支持以下功能 树节点 新增 编辑 删除 提示 以下代码 可参考 一 效果 二 完整代码 1 引入库 代码如下 示例 import
  • camera isp应用

    ISP应用及市场调研报告 1 调研目标及方法 1 1 调研目标 手机摄像头模组用ISP功能模块的市场走向及研发方向 为能够正确认识手机摄像模组行业提供技术及市场依据 2 ISP在模组上的应用原理 图一 手机摄像模组后端处理IC功能划分图 2
  • 锁相环工作原理

    锁相环 Phase Locked Loop 相位 锁 环路 简称 PLL 基本构成 f1 是输入频率 f2是输出频率 并且反馈给鉴相器 压控振荡器 压控振荡器的全称是 Voltage Controlled Oscillator 电压 控制
  • Unix时间戳和北京时间的相互转换(C语言实现 )

    一 问题背景 最近物联网项目中需要上传包含时间戳的设备数据到服务器平台 原本想把 年 月 日 时 分 秒 分别用一个 uint8 t 的数据类型去存储 即占用6个字节 但是在平台配置协议时 只有一种叫 Unix时间戳 的数据类型 Unix时