首页 > 其他分享 >C语言 实现 unix时间戳转换到自定义 tm结构体

C语言 实现 unix时间戳转换到自定义 tm结构体

时间:2024-10-09 09:32:38浏览次数:1  
标签:case 自定义 ++ TimeStr pos C语言 unix tm User

之前使用ESP32写了一个闹钟,免得我老是把手机闹钟给滑了就不知该起床了

原本想用标准库解决的,但是这个时间一直不准,逼得用 SNTP 获取了步进单位为(second)的时间戳,然后使用 GPtimer 来维持时间戳才算是把精准计时给解决了

废话不多说,直接上代码

typedef struct
{
    int year;
    int month;
    int day;
    int hour;
    int minute;
    int second;
} My_tm;

/// @brief 判断是否是闰年
/// @param year 年份
/// @return
static int isLeapYear(int year)
{
    if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
    {
        return 1; // 是闰年
    }
    return 0; // 不是闰年
}

/// @brief 时间戳转换成My_tm结构体
/// @param timestamp 时间戳
/// @param my_tm 结构体指针
/// @param timezone_offset_hours 时区偏移量
void convertTimestamp(time_t timestamp, My_tm *my_tm, const int timezone_offset_hours)
{
    // 计算每个时间单位的秒数
    const long secondsPerMinute = 60;
    const long secondsPerHour = 3600;
    const long secondsPerDay = 86400;

    // 调整为指定时区
    timestamp += timezone_offset_hours * secondsPerHour;

    // 初始化结构体
    my_tm->year = 1970;
    my_tm->month = 1;
    my_tm->day = 1;
    my_tm->hour = 0;
    my_tm->minute = 0;
    my_tm->second = 0;

    // 计算累积天数
    int totalDays = timestamp / secondsPerDay;

    // 跳过1970年前的天数
    int currentYear = 1970;
    while (totalDays >= 365)
    {
        if (isLeapYear(currentYear))
        {
            if (totalDays >= 366)
            {
                totalDays -= 366;
                currentYear++;
            }
            else
            {
                break;
            }
        }
        else
        {
            totalDays -= 365;
            currentYear++;
        }
    }

    // 设置年份
    my_tm->year = currentYear;

    // 跳过月份的天数
    static const int daysPerMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    int monthIndex = 0;
    while (totalDays >= daysPerMonth[monthIndex])
    {
        if (monthIndex == 1 && isLeapYear(currentYear))
        {
            if (totalDays >= 29)
            {
                totalDays -= 29;
                monthIndex++;
            }
            else
            {
                break;
            }
        }
        else
        {
            totalDays -= daysPerMonth[monthIndex];
            monthIndex++;
        }
    }

    // 设置月份和日期
    my_tm->month = monthIndex + 1;
    my_tm->day = totalDays + 1;

    // 计算剩余时间
    int remainingSeconds = timestamp % secondsPerDay;
    my_tm->hour = remainingSeconds / secondsPerHour;
    remainingSeconds %= secondsPerHour;
    my_tm->minute = remainingSeconds / secondsPerMinute;
    my_tm->second = remainingSeconds % secondsPerMinute;
}

/**
 * 将整数转换为字符串,格式为“(整数值)”
 *
 * @param number 待转换的整数
 * @param str 用于存储转换结果的字符数组
 * @param size 字符数组 str 的大小
 *
 * 注意:调用者必须确保 str 的大小足够存储转换后的字符串,包括终止空字符
 */
void intToString(int number, char *str, size_t size)
{
    // 使用 sprintf 将整数转换为字符串
    // 注意:str 必须有足够的空间来存储转换后的字符串
    // %d 是整数的格式化标识符
    // 返回值是写入的字符数,不包括终止空字符
    snprintf(str, size, "%d", number);
}

/// @brief 获取本地时间戳将其转换成字符串
/// @param timezone_offset_hours 时区偏移量
/// @param TimeStr 时间字符串(字符串要接入的位置)
/// @param TimeStrLen 时间字符串长度(传入的字符串长度要>=26)
/// @return 时间字符串"yyyy-mm-dd hh:mm:ss.ms"
char *User_tmToStrings(const int timezone_offset_hours, char *TimeStr, size_t TimeStrLen)
{
    if (TimeStr == NULL || TimeStrLen < 26)
        return NULL;
    static My_tm User_tm; // My_tm结构体
    time_t timestamp;
    size_t str_len = TimeStrLen, pos = 0;
    GetTimestamp();// 获取本地时间戳
    memset((void *)TimeStr, 0, str_len);
    convertTimestamp(timestamp, &User_tm, timezone_offset_hours);

    TimeStr[pos++] = '\"';

    // 填充年份
    intToString(User_tm.year, TimeStr + pos, str_len);
    pos = strlen(TimeStr);
    TimeStr[pos++] = '-'; // 目前写入了6个字符, |"yyyy-|

    // 填充月份
    switch (User_tm.month)
    {
    case 0:
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
    case 6:
    case 7:
    case 8:
    case 9: // 月份小于10前面补0
        TimeStr[pos++] = '0';
        intToString(User_tm.month, TimeStr + pos, str_len - pos);
        pos += strlen(TimeStr + pos); // 计算已经写入的字符数
        break;
    default:
        intToString(User_tm.month, TimeStr + pos, str_len - pos);
        pos += strlen(TimeStr + pos);
        break;
    }
    TimeStr[pos++] = '-'; // 目前写入了9个字符, |"yyyy-mm-|

    // 填充日期
    switch (User_tm.day)
    {
    case 0:
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
    case 6:
    case 7:
    case 8:
    case 9: // 日期小于10前面补0
        TimeStr[pos++] = '0';
        intToString(User_tm.day, TimeStr + pos, str_len - pos);
        pos += strlen(TimeStr + pos);
        break;
    default:
        intToString(User_tm.day, TimeStr + pos, str_len - pos);
        pos += strlen(TimeStr + pos);
        break;
    }
    TimeStr[pos++] = ' '; // 目前写入了12个字符, |"yyyy-mm-dd |

    // 填充小时
    switch (User_tm.hour)
    {
    case 0:
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
    case 6:
    case 7:
    case 8:
    case 9: // 小时小于10前面补0
        TimeStr[pos++] = '0';
        intToString(User_tm.hour, TimeStr + pos, str_len - pos);
        pos += strlen(TimeStr + pos);
        break;
    default:
        intToString(User_tm.hour, TimeStr + pos, str_len - pos);
        pos += strlen(TimeStr + pos);
        break;
    }
    TimeStr[pos++] = ':'; // 目前写入了15个字符, |"yyyy-mm-dd hh:|

    // 填充分钟
    switch (User_tm.minute)
    {
    case 0:
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
    case 6:
    case 7:
    case 8:
    case 9: // 分钟小于10前面补0
        TimeStr[pos++] = '0';
        intToString(User_tm.minute, TimeStr + pos, str_len - pos);
        pos += strlen(TimeStr + pos);
        break;
    default:
        intToString(User_tm.minute, TimeStr + pos, str_len - pos);
        pos += strlen(TimeStr + pos);
        break;
    }
    TimeStr[pos++] = ':'; // 目前写入了18个字符, |"yyyy-mm-dd- hh:mm:|

    // 填充秒数
    switch (User_tm.second)
    {
    case 0:
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
    case 6:
    case 7:
    case 8:
    case 9: // 秒数小于10前面补0
        TimeStr[pos++] = '0';
        intToString(User_tm.second, TimeStr + pos, str_len - pos);
        pos += strlen(TimeStr + pos);
        break;
    default:
        intToString(User_tm.second, TimeStr + pos, str_len - pos);
        pos += strlen(TimeStr + pos);
        break;
    }
    TimeStr[pos++] = '.'; // 目前写入了21个字符, |"yyyy-mm-dd- hh:mm:ss.|

    // 填充毫秒数
    TimeStr[pos++] = '0';
    TimeStr[pos++] = '0';
    TimeStr[pos++] = '0';
    // UserStrcat(TimeStr, "000", strlen("000"), pos);
    // strcat(TimeStr + pos, "000"); // 毫秒数, 由于获取的时间时间戳是unit: s, 所以直接加3个0
    // pos += 3;                     // 目前写入了25个字符, "yyyy-mm-dd- hh:mm:ss.000"

    switch (timezone_offset_hours)
    {
    case 0:
        TimeStr[pos++] = 'Z';
        TimeStr[pos++] = '\"';
        TimeStr[pos++] = '\0';
        while (TimeStr[pos] != ' ')
        {
            pos--;
        }
        TimeStr[pos] = 'T';
        return TimeStr;
    default:
        TimeStr[pos++] = '\"';
        break;
    }

    return TimeStr;
}

标签:case,自定义,++,TimeStr,pos,C语言,unix,tm,User
From: https://www.cnblogs.com/JPW-2024/p/18453576

相关文章

  • 【C语言小型项目实践】:初学者到中级水平的计算器项目
    目录一、计算器项目1.1.功能描述1.2.技术要点 二、代码实现2.1.代码说明2.2.注意事项 三、测试用例3.1.基本运算3.2.括号与优先级3.3.多个运算符3.4.更复杂的表达式3.5.错误情况2.6.带负数的运算2.7.混合情况一、计算器项目1.1.功能描述此......
  • Linux系统编程—I/O缓冲区(C语言实现)
    I/O缓冲区进程的I/O缓冲区机制是计算机操作系统中一个重要的概念,它涉及到数据在内存和外设之间的传输。以下是关于进程的I/O缓冲区机制的详细解释:1.定义与作用定义:I/O缓冲区是指在内存里开辟的一块区域,用来存放接收用户输入和用于计算机输出的数据,以减小系统开销和提高......
  • 【C语言】输出数据的二进制存储形式
        说在前面:是一个C语言新手,很新的新手,在这个专栏记录一些探索过程    今天学习中学到类型转换,将int和short类型赋值给char类型变量时,因为想要清楚看到隐式转换的结果,产生了写一个东西来输出数据在计算机中的二进制存储形式的想法,以下为尝试过程一、首先想......
  • C语言——static 关键字与 const 关键字
    static静态的        一、static修饰局部变量——称为静态局部变量                static改变了局部变量的生命周期(本质上是改变了变量的存储类型),当被static修饰时,局部变量由栈区存放到了静态区。voidtest(){intnum=1;printf("%d......
  • 实验2 C语言分支与循环基础应用编程-1
    任务一#include<stdio.h>#include<stdlib.h>#include<time.h>#defineN5#defineN1397#defineN2476#defineN321intmain(){intcnt;intrandom_major,random_no;srand(time(NULL));//以当前系统时间作为随机种子cnt=0;......
  • 自定义DFS,DFT,DTFT函数并比较关系
    一、DFS(离散傅里叶级数)functiony=DFS(x,L)N=length(x);xi=[x;zeros(L-N,1)]; y=zeros(1,L);fork=1:Lsum=0; forn=1:Lsum=sum+xi(n)*exp(-2j*pi*k*n/L); end y(k)=sum; end end 二、DFT(离散傅里叶变换) functiony=DFT(x,L)N......
  • 自定义卷积函数并计算窗函数的卷积
    一、自定义卷积functiony=Convu(x,W) Nx=length(x); Nw=length(W); y=zeros(1,Nx+Nw-1); forn=1:Nx+Nw-1 sum=0; startIdx=max(1,n-Nw+1); endIdx=min(n,Nx); fork=1:Nw ifstartIdx+k-1<=endIdx sum=sum+......
  • zotero自定义界面
    简介本人zotero使用到的四个插件EtherealStyle(这是一个多彩的插件,用于增强Zotero的界面功能)GreenFrog(显示影响因子)JasminumTranslateforZotero插件下载来源:Zotero插件商店|Zotero中文社区(zotero-chinese.com)最终页面效果显示影响因子、期刊分区、CCF分区......
  • Qt实现自定义控件-按钮
    背景:想着自己实现一个好看一点的按钮,切换时加一点动画。也算巩固一下Qt的基本知识。基本环境: 主要用到的类有:QBrush:画刷,主要是画一些颜色啥的。QPainter:画笔,可以画形状。比如带弧形的矩形,圆形,写字等。QPropertyAnimation:动画相关。关键逻辑:重载paintEvent:实现渲染逻辑,......
  • 初识C语言
    一、C语言的简介什么是C语言C语言之父C语言的由来C语言的发展为什么要学C语言/能做什么为什么要学习这套C语言课程内容介绍1、C语言标准1.1标准简史1972年C语⾔在⻉尔实验室诞⽣.丹尼斯·⾥奇参考B语⾔开发.1970-80年代,C语⾔被⼴泛应⽤,产⽣很多不同的C语⾔版本.程......