代码功能总结
核心功能
纯 C 实现,不依赖time.h系统时间库 ,把 Unix UTC 时间戳(单位秒,long long)转换成 UTC+8 北京时间 ,输出 YYYY-MM-DD HH:MM:SS。
- 时间转换逻辑:Unix 时间戳 (UTC) + 8 小时偏移量 → 北京时间总秒数;
- 拆分总秒数:分离出时、分、秒;
- 日期推算:从 1970-01-01 开始,逐年、逐月扣减天数,算出年、月、日;
- 内置闰年判断、每月天数计算;
- 增加简单保护:拒绝 1970 年前负时间戳,对时分秒做负数取模修正。
重要局限(必须重点标注)
- ❌ 不处理闰秒:和操作系统标准时间最多存在 1 秒偏差;
- ❌ 原生逻辑仅支持1970-01-01 之后的时间;
- ❌ 日期推算使用 while 循环,大批量解析时间戳性能较差;
- ❌ 没有时区自动切换能力,固定 UTC+8。
适用应用场合
✅ 推荐场景
- 嵌入式裸机 / 无操作系统 MCU (如 TI C2000、STM32 等):没有操作系统
time库,无法调用gmtime/localtime,需要软件手动解析时间戳; - 小型固件、日志模块:设备通过网络获取 Unix 时间戳,本地转换成北京时间打印日志;
- 轻量协议解析:Modbus、自定义通信协议上报 Unix 时间戳,本地做时间格式化展示;
- 跨平台可移植代码:不受主机系统时区、系统时间库差异影响,固定输出北京时间;
- 学习用途:理解 Unix 时间戳、公历闰年、日期换算原理。
❌ 不推荐场景
- 金融、计量、高精度时序系统:存在闰秒带来的 1s 误差;
- 百万级大批量时间戳解析:循环逐年逐月计算,CPU 开销高;
- 需要支持 1970 年之前历史时间;
- 需要自动夏令时、多时区切换;
- Linux/Windows PC 软件:优先使用系统
time.h,更稳定且自带闰秒处理。
cpp
/**
* @file unix_ts_beijing.c
* @brief 无time.h依赖,Unix UTC时间戳转换为北京时间(UTC+8)
* @details
* 核心功能:输入long long类型Unix时间戳(UTC,单位秒),手动换算得到北京时间,
* 输出 YYYY-MM-DD HH:MM:SS。内置闰年判断、月份天数计算。
* @note 限制说明:
* 1. 不处理闰秒,与系统标准时间最多存在1秒偏差;
* 2. 仅支持1970-01-01之后的非负时间戳;
* 3. 日期采用逐年逐月循环推算,大批量时间戳解析性能一般;
* 4. 固定UTC+8时区,不支持夏令时、多时区切换。
* @application 适用场合:裸机嵌入式MCU、无操作系统固件、日志打印、协议时间解析;
* 不适用:金融高精度时序、百万级批量解析、1970年前历史时间。
*/
#include <stdio.h>
// 北京时间UTC+8,偏移8小时,用long long常量防止整型溢出
#define BEIJING_OFFSET (8 * 3600LL)
/**
* @brief 判断指定年份是否为闰年
* @param year 待判断年份
* @return 1=闰年,0=平年
* @note 闰年规则:能被4整除且不能被100整除,或者能被400整除
*/
int is_leap_year(int year)
{
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
/**
* @brief 获取某年某月对应的总天数
* @param year 年份
* @param month 月份 [1~12]
* @return 当月天数
*/
int get_days_in_month(int year, int month)
{
// 平年各月天数数组,下标0对应1月
int days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
// 闰年2月特殊处理
if (month == 2 && is_leap_year(year))
{
return 29;
}
return days_in_month[month - 1];
}
/**
* @brief 将Unix时间戳(UTC)转换为北京时间 UTC+8
* @param unix_timestamp Unix时间戳,单位:秒,long long类型
* @note 本实现不依赖time.h库,纯手动时间换算
* @note 限制:仅支持1970-01-01之后的非负时间戳;不处理闰秒
*/
void unix_ts_to_beijing(long long unix_timestamp)
{
// Unix时间戳是UTC时间,加上8小时偏移得到北京时间对应的时间戳
long long beijing_ts = unix_timestamp + BEIJING_OFFSET;
long long total_sec = beijing_ts;
// 计算秒,并做负数兼容处理
int seconds = total_sec % 60;
if (seconds < 0)
{
total_sec -= 60;
seconds += 60;
}
// 计算分钟,并做负数兼容处理
int minutes = (total_sec / 60) % 60;
if (minutes < 0)
{
total_sec -= 3600;
minutes += 60;
}
// 计算小时,负数兼容处理
int hours = (total_sec / 3600) % 24;
if (hours < 0)
{
hours += 24;
}
// 换算总天数:总秒数 / 86400(一天=24*3600秒)
long long days = beijing_ts / 86400LL;
// 校验:不支持1970年之前的时间戳
if (days < 0)
{
printf("时间戳小于1970-01-01,暂不支持!\n");
return;
}
// 从1970年开始,逐年减去当年天数,定位目标年份
int year = 1970;
while (1)
{
int days_in_year = is_leap_year(year) ? 366 : 365;
// 剩余天数不足一整年,退出年份循环
if (days < days_in_year)
{
break;
}
days -= days_in_year;
year++;
}
// 在目标年份内,逐月减去当月天数,定位目标月份
int month = 1;
while (1)
{
int dim = get_days_in_month(year, month);
// 剩余天数不足一个月,退出月份循环
if (days < dim)
{
break;
}
days -= dim;
month++;
}
// days是当月偏移(从0开始),日期需要+1
int day = days + 1;
// 格式化输出结果
printf("Unix 时间戳: %lld\n", unix_timestamp);
printf("北京时间: %04d-%02d-%02d %02d:%02d:%02d (UTC+8)\n",
year, month, day, hours, minutes, seconds);
}
int main(void)
{
// 测试用例
unix_ts_to_beijing(1789005443);
return 0;
}