C++ 时间处理-从字符串中解析日期时间

1. 关键词

C++ 时间处理 从字符串中解析日期时间 跨平台

2. 问题

C++如何将字符串的日期时间解析成对应的时间戳?

3. 解决思路

  • 可以用正则表达式将字符串解析成 struct tm 类型的对象。
  • mktime()函数可以将 struct tm 类型的时间转换成时间戳。

4. 代码实现


#include <cstdint>
#include <string>
#include <vector>
#include <regex>
#include <time.h>

using time_regex_type = std::pair<std::string, std::regex>;
using time_regex_vec_type = std::vector<time_regex_type>;

std::string supported_time_formats(const time_regex_vec_type &fmtlist)
{
    std::string time_fmts;
    for (size_t i = 0; i < fmtlist.size(); i++)
    {
        time_fmts += fmtlist[i].first + "\n";
    }
    return time_fmts;
}

bool datetime::verify_time(const struct tm &time)
{
    // 校验年
    if (time.tm_year < 1900)
    {
        CUTL_ERROR("the year should be >= 1900");
        return false;
    }
    // 校验月
    if (time.tm_mon < 1 || time.tm_mon > 12)
    {
        CUTL_ERROR("the month should be between 1 and 12");
        return false;
    }
    // 校验日
    std::vector<int> large_month = {1, 3, 5, 7, 8, 10, 12};
    if (std::find(large_month.begin(), large_month.end(), time.tm_mon) != large_month.end() && (time.tm_mday < 1 || time.tm_mday > 31))
    {
        CUTL_ERROR("the day should be between 1 and 31 for " + std::to_string(time.tm_mon) + " month");
        return false;
    }
    std::vector<int> small_month = {4, 6, 9, 11};
    if (std::find(small_month.begin(), small_month.end(), time.tm_mon) != small_month.end() && (time.tm_mday < 1 || time.tm_mday > 30))
    {
        CUTL_ERROR("the day should be between 1 and 30 for " + std::to_string(time.tm_mon) + " month");
        return false;
    }
    if (time.tm_mon == 2)
    {
        auto is_leap_year = [](int year)
        { return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); };
        if (is_leap_year(time.tm_year) && (time.tm_mday < 1 || time.tm_mday > 29))
        {
            CUTL_ERROR("the day should be between 1 and 29 for " + std::to_string(time.tm_year) + "-" + fmt_uint(time.tm_mon, 2));
            return false;
        }
        if (!is_leap_year(time.tm_year) && (time.tm_mday < 1 || time.tm_mday > 28))
        {
            CUTL_ERROR("the day should be between 1 and 28 for " + std::to_string(time.tm_year) + "-" + fmt_uint(time.tm_mon, 2));
            return false;
        }
    }

    // 校验时分秒
    if (time.tm_hour < 0 || time.tm_hour > 23)
    {
        CUTL_ERROR("the hour should be between 0 and 23");
        return false;
    }
    if (time.tm_min < 0 || time.tm_min > 59)
    {
        CUTL_ERROR("the minute should be between 0 and 59");
        return false;
    }
    if (time.tm_sec < 0 || time.tm_sec > 59)
    {
        CUTL_ERROR("the second should be between 0 and 59");
        return false;
    }

    return true;
}


uint64_t parse_datetime(const std::string &time_text, int isdst)
{
    std::smatch matchRes;
    bool result = false;
    static time_regex_vec_type fmt_list = {
        // 0/1, 2/3, 4/5, 6/7的顺序不能反,因为不含毫秒数的时间会被优先匹配到
        std::make_pair("YYYY-MM-DD HH:MM:SS.sss", std::regex(R"((\d{4})-(\d{2})-(\d{2})[ ](\d{2}):(\d{2}):(\d{2}).(\d{3}))")),
        std::make_pair("YYYY-MM-DD HH:MM:SS", std::regex(R"((\d{4})-(\d{2})-(\d{2})[ ](\d{2}):(\d{2}):(\d{2}))")),
        std::make_pair("YYYY.MM.DD HH:MM:SS.sss", std::regex(R"((\d{4}).(\d{2}).(\d{2})[ ](\d{2}):(\d{2}):(\d{2}).(\d{3}))")),
        std::make_pair("YYYY.MM.DD HH:MM:SS", std::regex(R"((\d{4}).(\d{2}).(\d{2})[ ](\d{2}):(\d{2}):(\d{2}))")),
        std::make_pair("YYYY/MM/DD HH:MM:SS.sss", std::regex(R"((\d{4})/(\d{2})/(\d{2})[ ](\d{2}):(\d{2}):(\d{2}).(\d{3}))")),
        std::make_pair("YYYY/MM/DD HH:MM:SS", std::regex(R"((\d{4})/(\d{2})/(\d{2})[ ](\d{2}):(\d{2}):(\d{2}))")),
        std::make_pair("YYYYMMDD HH:MM:SS.sss", std::regex(R"((\d{4})(\d{2})(\d{2})[ ](\d{2}):(\d{2}):(\d{2}).(\d{3}))")),
        std::make_pair("YYYYMMDD HH:MM:SS", std::regex(R"((\d{4})(\d{2})(\d{2})[ ](\d{2}):(\d{2}):(\d{2}))")),
    };
    for (size_t i = 0; i < fmt_list.size(); i++)
    {
        auto &fmt_text = fmt_list[i].first;
        auto &fmt_pattern = fmt_list[i].second;
        result = std::regex_search(time_text, matchRes, fmt_pattern);
        if (result)
        {
            CUTL_DEBUG("matched regex: " + fmt_text);
            break;
        }
    }

    if (!result || matchRes.size() < 7)
    {
        auto time_fmts = supported_time_formats(fmt_list);
        CUTL_ERROR("Only the following time formats are supported:\n" + time_fmts);
        return 0;
    }

    CUTL_DEBUG("matchRes size:" + std::to_string(matchRes.size()) + ", res:" + matchRes[0].str());
    // 解析毫秒值
    int ms = 0;
    if (matchRes.size() == 8)
    {
        ms = std::stoi(matchRes[7].str());
    }
    // 解析tm结构的时间
    struct tm time = {};
    if (matchRes.size() >= 7)
    {
        for (size_t i = 1; i < 7; i++)
        {
            time.tm_year = std::stoi(matchRes[1]);
            time.tm_mon = std::stoi(matchRes[2]);
            time.tm_mday = std::stoi(matchRes[3]);
            time.tm_hour = std::stoi(matchRes[4]);
            time.tm_min = std::stoi(matchRes[5]);
            time.tm_sec = std::stoi(matchRes[6]);
            time.tm_isdst = isdst;
        }
    }
    if (!verify_time(time))
    {
        return 0;
    }

    // 转换为时间戳
    time.tm_year -= 1900;
    time.tm_mon -= 1;
    auto ret = mktime(&time);
    if (ret == -1)
    {
        CUTL_ERROR("mktime() failed");
        return 0;
    }
    auto s = static_cast<uint64_t>(ret);
    return s;
}

5. 测试代码

#pragma once

#include <iostream>
#include "timecount.h"
#include "common.hpp"

void TestDatetimeParseString()
{
    // 字符串解析成时间
    PrintSubTitle("TestDatetimeParseString");

    auto dt0 = parse_datetime(" 2024-03-02 14:18:44 ");
    std::cout << "dt0: " << dt0 << std::endl;
    auto dt2 = parse_datetime(" 2024.03.12 14:18:44");
    std::cout << "dt2: " << dt2 << std::endl;
}

6. 运行结果

--------------------------------------TestDatetimeParseString---------------------------------------
dt0: 2024-03-02 14:18:44.000
dt2: 2024-03-12 14:18:44.000

7. 源码地址

更多详细代码,请查看本人写的C++ 通用工具库: common_util, 本项目已开源,代码简洁,且有详细的文档和Demo。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陌尘(MoChen)

爱打赏的人技术成长更快哦~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值