C: Unix UTC 时间戳转为 UTC+8 北京时间

代码功能总结

核心功能

纯 C 实现,不依赖time.h系统时间库 ,把 Unix UTC 时间戳(单位秒,long long)转换成 UTC+8 北京时间 ,输出 YYYY-MM-DD HH:MM:SS

  1. 时间转换逻辑:Unix 时间戳 (UTC) + 8 小时偏移量 → 北京时间总秒数;
  2. 拆分总秒数:分离出时、分、秒;
  3. 日期推算:从 1970-01-01 开始,逐年、逐月扣减天数,算出年、月、日;
  4. 内置闰年判断、每月天数计算;
  5. 增加简单保护:拒绝 1970 年前负时间戳,对时分秒做负数取模修正。

重要局限(必须重点标注)

  • 不处理闰秒:和操作系统标准时间最多存在 1 秒偏差;
  • ❌ 原生逻辑仅支持1970-01-01 之后的时间;
  • ❌ 日期推算使用 while 循环,大批量解析时间戳性能较差;
  • ❌ 没有时区自动切换能力,固定 UTC+8。

适用应用场合

推荐场景

  1. 嵌入式裸机 / 无操作系统 MCU (如 TI C2000、STM32 等):没有操作系统time库,无法调用gmtime/localtime,需要软件手动解析时间戳;
  2. 小型固件、日志模块:设备通过网络获取 Unix 时间戳,本地转换成北京时间打印日志;
  3. 轻量协议解析:Modbus、自定义通信协议上报 Unix 时间戳,本地做时间格式化展示;
  4. 跨平台可移植代码:不受主机系统时区、系统时间库差异影响,固定输出北京时间;
  5. 学习用途:理解 Unix 时间戳、公历闰年、日期换算原理。

不推荐场景

  1. 金融、计量、高精度时序系统:存在闰秒带来的 1s 误差;
  2. 百万级大批量时间戳解析:循环逐年逐月计算,CPU 开销高;
  3. 需要支持 1970 年之前历史时间;
  4. 需要自动夏令时、多时区切换;
  5. 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;
}
相关推荐
彧azz4 小时前
数据结构:关于图的学习
c语言·数据结构·笔记·学习·算法
彧azz5 小时前
二叉搜索树学习记录
c语言·数据结构·笔记·算法
深念Y8 小时前
iOS模拟器在无Metal的NVIDIA显卡环境下的可行性研究报告
服务器·ios·unix·pve·freebsd
番茄灭世神9 小时前
汇顶模拟前端GH3220使用备忘录
c语言·单片机·传感器·模拟芯片·电生理信号处理
Brilliantwxx12 小时前
【STM32 】把 printf 搬到串口上 —— C 标准 IO 函数重定向与多文件工程搭建(实战串口电灯)
c语言·开发语言·stm32·单片机·嵌入式硬件·架构·ecmascript
wuminyu12 小时前
Kafka利用sendfile与Page Cache实现高性能传输剖析
java·linux·c语言·jvm·c++
亮_一个嵌入式新手12 小时前
C语言Day21
c语言·开发语言
djarmy21 小时前
MAIN.c(1): warning C318: can’t open file ‘STC8G.H’ 报错 解决 分析
c语言·开发语言·mongodb
李永奉1 天前
中科蓝讯SDK开发-BT893x 面条耳机连接vivo手机后连接APP端,最后手机端设置页面手动断开蓝牙连接,此时耳机端假断开
c语言·开发语言·嵌入式硬件·物联网·智能手机·电脑