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

之前使用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;
}
posted @ 2024-10-09 09:39  我是只平平  阅读(39)  评论(2编辑  收藏  举报